From 246f530cdb5a93b5da6db2c2fb00549b4081d943 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Tue, 18 May 2021 15:51:24 -0500 Subject: [PATCH 001/272] adc: Move the zynq-xadc file to the adc directories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's an ADC, put it where it belongs. Cc: Edgar E. Iglesias Cc: Alistair Francis Cc: Peter Maydell Cc: qemu-arm@nongnu.org Signed-off-by: Corey Minyard Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias --- MAINTAINERS | 6 ++++-- hw/adc/meson.build | 1 + hw/{misc => adc}/zynq-xadc.c | 2 +- hw/arm/xilinx_zynq.c | 2 +- hw/misc/meson.build | 2 +- include/hw/{misc => adc}/zynq-xadc.h | 0 6 files changed, 8 insertions(+), 5 deletions(-) rename hw/{misc => adc}/zynq-xadc.c (99%) rename include/hw/{misc => adc}/zynq-xadc.h (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 636bf2f536..455fdadfbb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -923,8 +923,10 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/*/xilinx_* F: hw/*/cadence_* -F: hw/misc/zynq* -F: include/hw/misc/zynq* +F: hw/misc/zynq_slcr.c +F: hw/adc/zynq-xadc.c +F: include/hw/misc/zynq_slcr.h +F: include/hw/adc/zynq-xadc.h X: hw/ssi/xilinx_* Xilinx ZynqMP and Versal diff --git a/hw/adc/meson.build b/hw/adc/meson.build index 6ddee23813..3d397b4ea1 100644 --- a/hw/adc/meson.build +++ b/hw/adc/meson.build @@ -1,2 +1,3 @@ softmmu_ss.add(when: 'CONFIG_STM32F2XX_ADC', if_true: files('stm32f2xx_adc.c')) softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_adc.c')) +softmmu_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq-xadc.c')) \ No newline at end of file diff --git a/hw/misc/zynq-xadc.c b/hw/adc/zynq-xadc.c similarity index 99% rename from hw/misc/zynq-xadc.c rename to hw/adc/zynq-xadc.c index 7b1972ce06..cfc7bab065 100644 --- a/hw/misc/zynq-xadc.c +++ b/hw/adc/zynq-xadc.c @@ -15,7 +15,7 @@ #include "qemu/osdep.h" #include "hw/irq.h" -#include "hw/misc/zynq-xadc.h" +#include "hw/adc/zynq-xadc.h" #include "migration/vmstate.h" #include "qemu/timer.h" #include "qemu/log.h" diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 81af32dc42..245af81bbb 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -26,7 +26,7 @@ #include "hw/boards.h" #include "hw/block/flash.h" #include "hw/loader.h" -#include "hw/misc/zynq-xadc.h" +#include "hw/adc/zynq-xadc.h" #include "hw/ssi/ssi.h" #include "hw/usb/chipidea.h" #include "qemu/error-report.h" diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 66e1648533..508fce50c7 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -84,7 +84,7 @@ softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files( 'bcm2835_cprman.c', )) softmmu_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_misc.c')) -softmmu_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq_slcr.c', 'zynq-xadc.c')) +softmmu_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq_slcr.c')) softmmu_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-xramc.c')) softmmu_ss.add(when: 'CONFIG_STM32F2XX_SYSCFG', if_true: files('stm32f2xx_syscfg.c')) softmmu_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_syscfg.c')) diff --git a/include/hw/misc/zynq-xadc.h b/include/hw/adc/zynq-xadc.h similarity index 100% rename from include/hw/misc/zynq-xadc.h rename to include/hw/adc/zynq-xadc.h From 58f3e3fe69a66a5c27675faf3e7afa52e027e621 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Tue, 18 May 2021 15:54:52 -0500 Subject: [PATCH 002/272] adc: Move the max111x driver to the adc directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's an adc, put it where it belongs. Cc: Andrzej Zaborowski Cc: Peter Maydell Cc: qemu-arm@nongnu.org Signed-off-by: Corey Minyard Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- MAINTAINERS | 4 ++-- hw/adc/Kconfig | 3 +++ hw/{misc => adc}/max111x.c | 2 +- hw/adc/meson.build | 3 ++- hw/arm/spitz.c | 2 +- hw/misc/Kconfig | 3 --- hw/misc/meson.build | 1 - include/hw/{misc => adc}/max111x.h | 0 8 files changed, 9 insertions(+), 9 deletions(-) rename hw/{misc => adc}/max111x.c (99%) rename include/hw/{misc => adc}/max111x.h (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 455fdadfbb..2fde240be9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -844,8 +844,8 @@ F: hw/display/tc6393xb.c F: hw/gpio/max7310.c F: hw/gpio/zaurus.c F: hw/misc/mst_fpga.c -F: hw/misc/max111x.c -F: include/hw/misc/max111x.h +F: hw/adc/max111x.c +F: include/hw/adc/max111x.h F: include/hw/arm/pxa.h F: include/hw/arm/sharpsl.h F: include/hw/display/tc6393xb.h diff --git a/hw/adc/Kconfig b/hw/adc/Kconfig index 25d2229fb8..a825bd3d34 100644 --- a/hw/adc/Kconfig +++ b/hw/adc/Kconfig @@ -1,2 +1,5 @@ config STM32F2XX_ADC bool + +config MAX111X + bool diff --git a/hw/misc/max111x.c b/hw/adc/max111x.c similarity index 99% rename from hw/misc/max111x.c rename to hw/adc/max111x.c index 1b3234a519..e8bf4cccd4 100644 --- a/hw/misc/max111x.c +++ b/hw/adc/max111x.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "hw/misc/max111x.h" +#include "hw/adc/max111x.h" #include "hw/irq.h" #include "migration/vmstate.h" #include "qemu/module.h" diff --git a/hw/adc/meson.build b/hw/adc/meson.build index 3d397b4ea1..ac4f093fea 100644 --- a/hw/adc/meson.build +++ b/hw/adc/meson.build @@ -1,3 +1,4 @@ softmmu_ss.add(when: 'CONFIG_STM32F2XX_ADC', if_true: files('stm32f2xx_adc.c')) softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_adc.c')) -softmmu_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq-xadc.c')) \ No newline at end of file +softmmu_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq-xadc.c')) +softmmu_ss.add(when: 'CONFIG_MAX111X', if_true: files('max111x.c')) diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c index b45a929cbd..0e2626125e 100644 --- a/hw/arm/spitz.c +++ b/hw/arm/spitz.c @@ -30,7 +30,7 @@ #include "audio/audio.h" #include "hw/boards.h" #include "hw/sysbus.h" -#include "hw/misc/max111x.h" +#include "hw/adc/max111x.h" #include "migration/vmstate.h" #include "exec/address-spaces.h" #include "cpu.h" diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index c71ed25820..996d45aff5 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -11,9 +11,6 @@ config ARMSSE_MHU config ARMSSE_CPU_PWRCTRL bool -config MAX111X - bool - config TMP105 bool depends on I2C diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 508fce50c7..b0a8ee8994 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -3,7 +3,6 @@ softmmu_ss.add(when: 'CONFIG_EDU', if_true: files('edu.c')) softmmu_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('vmcoreinfo.c')) softmmu_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c')) softmmu_ss.add(when: 'CONFIG_ISA_TESTDEV', if_true: files('pc-testdev.c')) -softmmu_ss.add(when: 'CONFIG_MAX111X', if_true: files('max111x.c')) softmmu_ss.add(when: 'CONFIG_PCA9552', if_true: files('pca9552.c')) softmmu_ss.add(when: 'CONFIG_PCI_TESTDEV', if_true: files('pci-testdev.c')) softmmu_ss.add(when: 'CONFIG_SGA', if_true: files('sga.c')) diff --git a/include/hw/misc/max111x.h b/include/hw/adc/max111x.h similarity index 100% rename from include/hw/misc/max111x.h rename to include/hw/adc/max111x.h From 5e9ae4b1a31a17a72487372067a78b6afa68b68d Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Tue, 18 May 2021 16:08:03 -0500 Subject: [PATCH 003/272] sensor: Move hardware sensors from misc to a sensor directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lots of this are expected to be coming in, create a directory for them. Also move the tmp105.h file into the include directory where it should be. Cc: Cédric Le Goater Cc: Peter Maydell Cc: Andrew Jeffery Cc: Joel Stanley Cc: Andrzej Zaborowski Cc: qemu-arm@nongnu.org Signed-off-by: Corey Minyard Acked-by: Cédric Le Goater --- hw/Kconfig | 1 + hw/arm/aspeed.c | 2 +- hw/arm/nseries.c | 2 +- hw/meson.build | 1 + hw/misc/Kconfig | 12 ------------ hw/misc/meson.build | 3 --- hw/sensor/Kconfig | 11 +++++++++++ hw/{misc => sensor}/emc141x.c | 2 +- hw/sensor/meson.build | 3 +++ hw/{misc => sensor}/tmp105.c | 2 +- hw/{misc => sensor}/tmp421.c | 0 include/hw/{misc => sensor}/emc141x_regs.h | 0 {hw/misc => include/hw/sensor}/tmp105.h | 2 +- include/hw/{misc => sensor}/tmp105_regs.h | 0 tests/qtest/emc141x-test.c | 2 +- tests/qtest/npcm7xx_smbus-test.c | 2 +- tests/qtest/tmp105-test.c | 2 +- 17 files changed, 24 insertions(+), 23 deletions(-) create mode 100644 hw/sensor/Kconfig rename hw/{misc => sensor}/emc141x.c (99%) create mode 100644 hw/sensor/meson.build rename hw/{misc => sensor}/tmp105.c (99%) rename hw/{misc => sensor}/tmp421.c (100%) rename include/hw/{misc => sensor}/emc141x_regs.h (100%) rename {hw/misc => include/hw/sensor}/tmp105.h (97%) rename include/hw/{misc => sensor}/tmp105_regs.h (100%) diff --git a/hw/Kconfig b/hw/Kconfig index 805860f564..8cb7664d70 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -32,6 +32,7 @@ source remote/Kconfig source rtc/Kconfig source scsi/Kconfig source sd/Kconfig +source sensor/Kconfig source smbios/Kconfig source ssi/Kconfig source timer/Kconfig diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 1301e8fdff..9d43e26c51 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -17,7 +17,7 @@ #include "hw/i2c/i2c_mux_pca954x.h" #include "hw/i2c/smbus_eeprom.h" #include "hw/misc/pca9552.h" -#include "hw/misc/tmp105.h" +#include "hw/sensor/tmp105.h" #include "hw/misc/led.h" #include "hw/qdev-properties.h" #include "sysemu/block-backend.h" diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c index 0aefa5d0f3..a10d8f53b5 100644 --- a/hw/arm/nseries.c +++ b/hw/arm/nseries.c @@ -36,7 +36,7 @@ #include "hw/display/blizzard.h" #include "hw/input/tsc2xxx.h" #include "hw/misc/cbus.h" -#include "hw/misc/tmp105.h" +#include "hw/sensor/tmp105.h" #include "hw/qdev-properties.h" #include "hw/block/flash.h" #include "hw/hw.h" diff --git a/hw/meson.build b/hw/meson.build index ba0601e36e..b3366c888e 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -31,6 +31,7 @@ subdir('rdma') subdir('rtc') subdir('scsi') subdir('sd') +subdir('sensor') subdir('smbios') subdir('ssi') subdir('timer') diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index 996d45aff5..507058d8bf 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -11,18 +11,6 @@ config ARMSSE_MHU config ARMSSE_CPU_PWRCTRL bool -config TMP105 - bool - depends on I2C - -config TMP421 - bool - depends on I2C - -config EMC141X - bool - depends on I2C - config ISA_DEBUG bool depends on ISA_BUS diff --git a/hw/misc/meson.build b/hw/misc/meson.build index b0a8ee8994..046c7e0c72 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -6,9 +6,6 @@ softmmu_ss.add(when: 'CONFIG_ISA_TESTDEV', if_true: files('pc-testdev.c')) softmmu_ss.add(when: 'CONFIG_PCA9552', if_true: files('pca9552.c')) softmmu_ss.add(when: 'CONFIG_PCI_TESTDEV', if_true: files('pci-testdev.c')) softmmu_ss.add(when: 'CONFIG_SGA', if_true: files('sga.c')) -softmmu_ss.add(when: 'CONFIG_TMP105', if_true: files('tmp105.c')) -softmmu_ss.add(when: 'CONFIG_TMP421', if_true: files('tmp421.c')) -softmmu_ss.add(when: 'CONFIG_EMC141X', if_true: files('emc141x.c')) softmmu_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c')) softmmu_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c')) softmmu_ss.add(when: 'CONFIG_LED', if_true: files('led.c')) diff --git a/hw/sensor/Kconfig b/hw/sensor/Kconfig new file mode 100644 index 0000000000..097cb8f11e --- /dev/null +++ b/hw/sensor/Kconfig @@ -0,0 +1,11 @@ +config TMP105 + bool + depends on I2C + +config TMP421 + bool + depends on I2C + +config EMC141X + bool + depends on I2C diff --git a/hw/misc/emc141x.c b/hw/sensor/emc141x.c similarity index 99% rename from hw/misc/emc141x.c rename to hw/sensor/emc141x.c index f7c53d48a4..7ce8f4e979 100644 --- a/hw/misc/emc141x.c +++ b/hw/sensor/emc141x.c @@ -25,7 +25,7 @@ #include "qapi/visitor.h" #include "qemu/module.h" #include "qom/object.h" -#include "hw/misc/emc141x_regs.h" +#include "hw/sensor/emc141x_regs.h" #define SENSORS_COUNT_MAX 4 diff --git a/hw/sensor/meson.build b/hw/sensor/meson.build new file mode 100644 index 0000000000..9e0f3ab1fd --- /dev/null +++ b/hw/sensor/meson.build @@ -0,0 +1,3 @@ +softmmu_ss.add(when: 'CONFIG_TMP105', if_true: files('tmp105.c')) +softmmu_ss.add(when: 'CONFIG_TMP421', if_true: files('tmp421.c')) +softmmu_ss.add(when: 'CONFIG_EMC141X', if_true: files('emc141x.c')) diff --git a/hw/misc/tmp105.c b/hw/sensor/tmp105.c similarity index 99% rename from hw/misc/tmp105.c rename to hw/sensor/tmp105.c index d299d9b21b..2056449489 100644 --- a/hw/misc/tmp105.c +++ b/hw/sensor/tmp105.c @@ -22,7 +22,7 @@ #include "hw/i2c/i2c.h" #include "hw/irq.h" #include "migration/vmstate.h" -#include "tmp105.h" +#include "hw/sensor/tmp105.h" #include "qapi/error.h" #include "qapi/visitor.h" #include "qemu/module.h" diff --git a/hw/misc/tmp421.c b/hw/sensor/tmp421.c similarity index 100% rename from hw/misc/tmp421.c rename to hw/sensor/tmp421.c diff --git a/include/hw/misc/emc141x_regs.h b/include/hw/sensor/emc141x_regs.h similarity index 100% rename from include/hw/misc/emc141x_regs.h rename to include/hw/sensor/emc141x_regs.h diff --git a/hw/misc/tmp105.h b/include/hw/sensor/tmp105.h similarity index 97% rename from hw/misc/tmp105.h rename to include/hw/sensor/tmp105.h index 7c97071ad7..244e2989fe 100644 --- a/hw/misc/tmp105.h +++ b/include/hw/sensor/tmp105.h @@ -15,7 +15,7 @@ #define QEMU_TMP105_H #include "hw/i2c/i2c.h" -#include "hw/misc/tmp105_regs.h" +#include "hw/sensor/tmp105_regs.h" #include "qom/object.h" #define TYPE_TMP105 "tmp105" diff --git a/include/hw/misc/tmp105_regs.h b/include/hw/sensor/tmp105_regs.h similarity index 100% rename from include/hw/misc/tmp105_regs.h rename to include/hw/sensor/tmp105_regs.h diff --git a/tests/qtest/emc141x-test.c b/tests/qtest/emc141x-test.c index 714058806a..8c86694091 100644 --- a/tests/qtest/emc141x-test.c +++ b/tests/qtest/emc141x-test.c @@ -11,7 +11,7 @@ #include "libqos/qgraph.h" #include "libqos/i2c.h" #include "qapi/qmp/qdict.h" -#include "hw/misc/emc141x_regs.h" +#include "hw/sensor/emc141x_regs.h" #define EMC1414_TEST_ID "emc1414-test" diff --git a/tests/qtest/npcm7xx_smbus-test.c b/tests/qtest/npcm7xx_smbus-test.c index 4f9f493872..6b3038ac59 100644 --- a/tests/qtest/npcm7xx_smbus-test.c +++ b/tests/qtest/npcm7xx_smbus-test.c @@ -18,7 +18,7 @@ #include "qemu/bitops.h" #include "libqos/i2c.h" #include "libqos/libqtest.h" -#include "hw/misc/tmp105_regs.h" +#include "hw/sensor/tmp105_regs.h" #define NR_SMBUS_DEVICES 16 #define SMBUS_ADDR(x) (0xf0080000 + 0x1000 * (x)) diff --git a/tests/qtest/tmp105-test.c b/tests/qtest/tmp105-test.c index f930a96b83..3678646df5 100644 --- a/tests/qtest/tmp105-test.c +++ b/tests/qtest/tmp105-test.c @@ -13,7 +13,7 @@ #include "libqos/qgraph.h" #include "libqos/i2c.h" #include "qapi/qmp/qdict.h" -#include "hw/misc/tmp105_regs.h" +#include "hw/sensor/tmp105_regs.h" #define TMP105_TEST_ID "tmp105-test" #define TMP105_TEST_ADDR 0x49 From 3909c079454a49b113c60a5cd91d749f78ca1c7f Mon Sep 17 00:00:00 2001 From: Pavel Dovgalyuk Date: Mon, 17 May 2021 16:06:28 +0300 Subject: [PATCH 004/272] virtio: disable ioeventfd for record/replay virtio devices support separate iothreads waiting for events from file descriptors. These are asynchronous events that can't be recorded and replayed, therefore this patch disables ioeventfd for all devices when record or replay is enabled. Signed-off-by: Pavel Dovgalyuk Message-Id: <162125678869.1252810.4317416444097392406.stgit@pasha-ThinkPad-X280> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/s390x/virtio-ccw.c | 6 ++++++ hw/virtio/virtio-mmio.c | 6 ++++++ hw/virtio/virtio-pci.c | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index d68888fccd..6a2df1c1e9 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -31,6 +31,7 @@ #include "trace.h" #include "hw/s390x/css-bridge.h" #include "hw/s390x/s390-virtio-ccw.h" +#include "sysemu/replay.h" #define NR_CLASSIC_INDICATOR_BITS 64 @@ -770,6 +771,11 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) dev->flags &= ~VIRTIO_CCW_FLAG_USE_IOEVENTFD; } + /* fd-based ioevents can't be synchronized in record/replay */ + if (replay_mode != REPLAY_MODE_NONE) { + dev->flags &= ~VIRTIO_CCW_FLAG_USE_IOEVENTFD; + } + if (k->realize) { k->realize(dev, &err); if (err) { diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index 5952471b38..1af48a1b04 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -29,6 +29,7 @@ #include "qemu/host-utils.h" #include "qemu/module.h" #include "sysemu/kvm.h" +#include "sysemu/replay.h" #include "hw/virtio/virtio-mmio.h" #include "qemu/error-report.h" #include "qemu/log.h" @@ -740,6 +741,11 @@ static void virtio_mmio_realizefn(DeviceState *d, Error **errp) proxy->flags &= ~VIRTIO_IOMMIO_FLAG_USE_IOEVENTFD; } + /* fd-based ioevents can't be synchronized in record/replay */ + if (replay_mode != REPLAY_MODE_NONE) { + proxy->flags &= ~VIRTIO_IOMMIO_FLAG_USE_IOEVENTFD; + } + if (proxy->legacy) { memory_region_init_io(&proxy->iomem, OBJECT(d), &virtio_legacy_mem_ops, proxy, diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index b321604d9b..f1e105fa52 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -37,6 +37,7 @@ #include "qemu/range.h" #include "hw/virtio/virtio-bus.h" #include "qapi/visitor.h" +#include "sysemu/replay.h" #define VIRTIO_PCI_REGION_SIZE(dev) VIRTIO_PCI_CONFIG_OFF(msix_present(dev)) @@ -1760,6 +1761,11 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD; } + /* fd-based ioevents can't be synchronized in record/replay */ + if (replay_mode != REPLAY_MODE_NONE) { + proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD; + } + /* * virtio pci bar layout used by default. * subclasses can re-arrange things if needed. From 9cf4fd872d14ae109ce1ee430bf67499c2682fa5 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Mon, 17 May 2021 15:26:37 +0200 Subject: [PATCH 005/272] virtio: Clarify MR transaction optimization The device model batching its ioeventfds in a single MR transaction is an optimization. Clarify this in virtio-scsi, virtio-blk and generic virtio code. Also clarify that the transaction must commit before closing ioeventfds so that no one is tempted to merge the loops in the start functions error path and in the stop functions. Signed-off-by: Greg Kurz Message-Id: <162125799728.1394228.339855768563326832.stgit@bahia.lan> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Stefan Hajnoczi Reviewed-by: Stefan Hajnoczi Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/block/dataplane/virtio-blk.c | 16 ++++++++++++++++ hw/scsi/virtio-scsi-dataplane.c | 16 ++++++++++++++++ hw/virtio/virtio.c | 16 ++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c index cd81893d1d..252c3a7a23 100644 --- a/hw/block/dataplane/virtio-blk.c +++ b/hw/block/dataplane/virtio-blk.c @@ -198,6 +198,10 @@ int virtio_blk_data_plane_start(VirtIODevice *vdev) goto fail_guest_notifiers; } + /* + * Batch all the host notifiers in a single transaction to avoid + * quadratic time complexity in address_space_update_ioeventfds(). + */ memory_region_transaction_begin(); /* Set up virtqueue notify */ @@ -211,6 +215,10 @@ int virtio_blk_data_plane_start(VirtIODevice *vdev) virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); } + /* + * The transaction expects the ioeventfds to be open when it + * commits. Do it now, before the cleanup loop. + */ memory_region_transaction_commit(); while (j--) { @@ -330,12 +338,20 @@ void virtio_blk_data_plane_stop(VirtIODevice *vdev) aio_context_release(s->ctx); + /* + * Batch all the host notifiers in a single transaction to avoid + * quadratic time complexity in address_space_update_ioeventfds(). + */ memory_region_transaction_begin(); for (i = 0; i < nvqs; i++) { virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); } + /* + * The transaction expects the ioeventfds to be open when it + * commits. Do it now, before the cleanup loop. + */ memory_region_transaction_commit(); for (i = 0; i < nvqs; i++) { diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c index 28e003250a..18eb824c97 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c @@ -152,6 +152,10 @@ int virtio_scsi_dataplane_start(VirtIODevice *vdev) goto fail_guest_notifiers; } + /* + * Batch all the host notifiers in a single transaction to avoid + * quadratic time complexity in address_space_update_ioeventfds(). + */ memory_region_transaction_begin(); rc = virtio_scsi_set_host_notifier(s, vs->ctrl_vq, 0); @@ -198,6 +202,10 @@ fail_host_notifiers: virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); } + /* + * The transaction expects the ioeventfds to be open when it + * commits. Do it now, before the cleanup loop. + */ memory_region_transaction_commit(); for (i = 0; i < vq_init_count; i++) { @@ -238,12 +246,20 @@ void virtio_scsi_dataplane_stop(VirtIODevice *vdev) blk_drain_all(); /* ensure there are no in-flight requests */ + /* + * Batch all the host notifiers in a single transaction to avoid + * quadratic time complexity in address_space_update_ioeventfds(). + */ memory_region_transaction_begin(); for (i = 0; i < vs->conf.num_queues + 2; i++) { virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); } + /* + * The transaction expects the ioeventfds to be open when it + * commits. Do it now, before the cleanup loop. + */ memory_region_transaction_commit(); for (i = 0; i < vs->conf.num_queues + 2; i++) { diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index ab516ac614..6dcf3baf56 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -3728,6 +3728,10 @@ static int virtio_device_start_ioeventfd_impl(VirtIODevice *vdev) VirtioBusState *qbus = VIRTIO_BUS(qdev_get_parent_bus(DEVICE(vdev))); int i, n, r, err; + /* + * Batch all the host notifiers in a single transaction to avoid + * quadratic time complexity in address_space_update_ioeventfds(). + */ memory_region_transaction_begin(); for (n = 0; n < VIRTIO_QUEUE_MAX; n++) { VirtQueue *vq = &vdev->vq[n]; @@ -3766,6 +3770,10 @@ assign_error: r = virtio_bus_set_host_notifier(qbus, n, false); assert(r >= 0); } + /* + * The transaction expects the ioeventfds to be open when it + * commits. Do it now, before the cleanup loop. + */ memory_region_transaction_commit(); while (--i >= 0) { @@ -3790,6 +3798,10 @@ static void virtio_device_stop_ioeventfd_impl(VirtIODevice *vdev) VirtioBusState *qbus = VIRTIO_BUS(qdev_get_parent_bus(DEVICE(vdev))); int n, r; + /* + * Batch all the host notifiers in a single transaction to avoid + * quadratic time complexity in address_space_update_ioeventfds(). + */ memory_region_transaction_begin(); for (n = 0; n < VIRTIO_QUEUE_MAX; n++) { VirtQueue *vq = &vdev->vq[n]; @@ -3801,6 +3813,10 @@ static void virtio_device_stop_ioeventfd_impl(VirtIODevice *vdev) r = virtio_bus_set_host_notifier(qbus, n, false); assert(r >= 0); } + /* + * The transaction expects the ioeventfds to be open when it + * commits. Do it now, before the cleanup loop. + */ memory_region_transaction_commit(); for (n = 0; n < VIRTIO_QUEUE_MAX; n++) { From 9b0ca75e0196a72523232063db1e07ae36a5077a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 26 May 2021 16:24:38 +0200 Subject: [PATCH 006/272] hw/pci-host/q35: Ignore write of reserved PCIEXBAR LENGTH field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit libFuzzer triggered the following assertion: cat << EOF | qemu-system-i386 -M pc-q35-5.0 \ -nographic -monitor none -serial none \ -qtest stdio -d guest_errors -trace pci\* outl 0xcf8 0xf2000060 outl 0xcfc 0x8400056e EOF pci_cfg_write mch 00:0 @0x60 <- 0x8400056e Aborted (core dumped) This is because guest wrote MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_RVD (reserved value) to the PCIE XBAR register. There is no indication on the datasheet about what occurs when this value is written. Simply ignore it on QEMU (and report an guest error): pci_cfg_write mch 00:0 @0x60 <- 0x8400056e Q35: Reserved PCIEXBAR LENGTH pci_cfg_read mch 00:0 @0x0 -> 0x8086 pci_cfg_read mch 00:0 @0x0 -> 0x29c08086 ... Cc: qemu-stable@nongnu.org Reported-by: Alexander Bulekov BugLink: https://bugs.launchpad.net/qemu/+bug/1878641 Fixes: df2d8b3ed4 ("q35: Introduce q35 pc based chipset emulator") Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20210526142438.281477-1-f4bug@amsat.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Alexander Bulekov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci-host/q35.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index 2eb729dff5..0f37cf056a 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -29,6 +29,7 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "hw/i386/pc.h" #include "hw/pci-host/q35.h" #include "hw/qdev-properties.h" @@ -318,6 +319,8 @@ static void mch_update_pciexbar(MCHPCIState *mch) addr_mask |= MCH_HOST_BRIDGE_PCIEXBAR_64ADMSK; break; case MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_RVD: + qemu_log_mask(LOG_GUEST_ERROR, "Q35: Reserved PCIEXBAR LENGTH\n"); + return; default: abort(); } From 80ebfd69b906186a12f0dc892a49188b4d672fdc Mon Sep 17 00:00:00 2001 From: Andrew Melnychenko Date: Wed, 9 Jun 2021 12:58:41 +0300 Subject: [PATCH 007/272] virtio-pci: Added check for virtio device presence in mm callbacks. During unplug the virtio device is unplugged from virtio-bus on pci. In some cases, requests to virtio-pci mm may acquire during/after unplug. Added check that virtio device is on the bus, for "common" memory region. Signed-off-by: Andrew Melnychenko Message-Id: <20210609095843.141378-2-andrew@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-pci.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index f1e105fa52..1bef7a2be8 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -1147,6 +1147,10 @@ static uint64_t virtio_pci_common_read(void *opaque, hwaddr addr, uint32_t val = 0; int i; + if (vdev == NULL) { + return UINT64_MAX; + } + switch (addr) { case VIRTIO_PCI_COMMON_DFSELECT: val = proxy->dfselect; @@ -1230,6 +1234,10 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr, VirtIOPCIProxy *proxy = opaque; VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + if (vdev == NULL) { + return; + } + switch (addr) { case VIRTIO_PCI_COMMON_DFSELECT: proxy->dfselect = val; From bf697371db87cc1a2d04f5e8dda1b4b3e2be0f0d Mon Sep 17 00:00:00 2001 From: Andrew Melnychenko Date: Wed, 9 Jun 2021 12:58:42 +0300 Subject: [PATCH 008/272] virtio-pci: Added check for virtio device in PCI config cbs. Now, if virtio device is not present on virtio-bus - pci config callbacks will not lead to possible crush. The read will return "-1" which should be interpreted by a driver that pci device may be unplugged. Signed-off-by: Andrew Melnychenko Message-Id: <20210609095843.141378-3-andrew@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-pci.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 1bef7a2be8..c0d9c47df7 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -424,6 +424,11 @@ static uint64_t virtio_pci_config_read(void *opaque, hwaddr addr, VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); uint32_t config = VIRTIO_PCI_CONFIG_SIZE(&proxy->pci_dev); uint64_t val = 0; + + if (vdev == NULL) { + return UINT64_MAX; + } + if (addr < config) { return virtio_ioport_read(proxy, addr); } @@ -455,6 +460,11 @@ static void virtio_pci_config_write(void *opaque, hwaddr addr, VirtIOPCIProxy *proxy = opaque; uint32_t config = VIRTIO_PCI_CONFIG_SIZE(&proxy->pci_dev); VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + + if (vdev == NULL) { + return; + } + if (addr < config) { virtio_ioport_write(proxy, addr, val); return; From df07a8f8cb743e0ff86346bcb49fe09240e4be6c Mon Sep 17 00:00:00 2001 From: Andrew Melnychenko Date: Wed, 9 Jun 2021 12:58:43 +0300 Subject: [PATCH 009/272] virtio-pci: Changed return values for "notify", "device" and "isr" read. At some point, after unplugging virtio-pci the virtio device may be unrealised, but the memory regions may be present in flatview. So, it's a possible situation when memory region's callbacks are called for "unplugged" device. Previous two patches made sure this case does not cause QEMU to crash. This patch adds check for "notify" memory region. Now reads will return "-1" if a virtio device is not present on a virtio bus. Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1938042 Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1743098 Signed-off-by: Andrew Melnychenko Message-Id: <20210609095843.141378-4-andrew@daynix.com> Reviewed-by: Stefano Garzarella Reviewed-by: Raphael Norwitz Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-pci.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index c0d9c47df7..433060ac02 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -1349,6 +1349,11 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr, static uint64_t virtio_pci_notify_read(void *opaque, hwaddr addr, unsigned size) { + VirtIOPCIProxy *proxy = opaque; + if (virtio_bus_get_device(&proxy->bus) == NULL) { + return UINT64_MAX; + } + return 0; } @@ -1386,7 +1391,7 @@ static uint64_t virtio_pci_isr_read(void *opaque, hwaddr addr, uint64_t val; if (vdev == NULL) { - return 0; + return UINT64_MAX; } val = qatomic_xchg(&vdev->isr, 0); @@ -1407,7 +1412,7 @@ static uint64_t virtio_pci_device_read(void *opaque, hwaddr addr, uint64_t val; if (vdev == NULL) { - return 0; + return UINT64_MAX; } switch (size) { From 109c20ea28cc0d82fa353e692345b172cb5721cc Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 29 Jun 2021 17:29:37 +0200 Subject: [PATCH 010/272] migration: failover: reset partially_hotplugged When the card is plugged back, reset the partially_hotplugged flag to false Bug: https://bugzilla.redhat.com/show_bug.cgi?id=1787194 Signed-off-by: Laurent Vivier Message-Id: <20210629152937.619193-1-lvivier@redhat.com> Reviewed-by: Juan Quintela Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/net/virtio-net.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index bd7958b9f0..16d20cdee5 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -3234,6 +3234,7 @@ static bool failover_replug_primary(VirtIONet *n, DeviceState *dev, } hotplug_handler_plug(hotplug_ctrl, dev, &err); } + pdev->partially_hotplugged = false; out: error_propagate(errp, err); From a4344574fd47336b6d8fc85ce1f66d4262e7dafd Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 24 Jun 2021 16:42:27 -0400 Subject: [PATCH 011/272] tests: acpi: prepare for changing DSDT tables Signed-off-by: Igor Mammedov Message-Id: <20210624204229.998824-2-imammedo@redhat.com> Reviewed-by: Stefan Hajnoczi Tested-by: John Sucaet Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test-allowed-diff.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..6c83a3ef76 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,11 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/pc/DSDT", +"tests/data/acpi/pc/DSDT.bridge", +"tests/data/acpi/pc/DSDT.ipmikcs", +"tests/data/acpi/pc/DSDT.cphp", +"tests/data/acpi/pc/DSDT.memhp", +"tests/data/acpi/pc/DSDT.numamem", +"tests/data/acpi/pc/DSDT.nohpet", +"tests/data/acpi/pc/DSDT.dimmpxm", +"tests/data/acpi/pc/DSDT.acpihmat", +"tests/data/acpi/pc/DSDT.hpbridge", From 7193d7cdd93e50f0e5f09803b98d27d3f9b147ac Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 24 Jun 2021 16:42:28 -0400 Subject: [PATCH 012/272] acpi: pc: revert back to v5.2 PCI slot enumeration Commit [1] moved _SUN variable from only hot-pluggable to all devices. This made linux kernel enumerate extra slots that weren't present before. If extra slot happens to be be enumerated first and there is a device in th same slot but on other bridge, linux kernel will add -N suffix to slot name of the later, thus changing NIC name compared to QEMU 5.2. This in some case confuses systemd, if it is using SLOT NIC naming scheme and interface name becomes not the same as it was under QEMU-5.2. Reproducer QEMU CLI: -M pc-i440fx-5.2 -nodefaults \ -device pci-bridge,chassis_nr=1,id=pci.1,bus=pci.0,addr=0x3 \ -device virtio-net-pci,id=nic1,bus=pci.1,addr=0x1 \ -device virtio-net-pci,id=nic2,bus=pci.1,addr=0x2 \ -device virtio-net-pci,id=nic3,bus=pci.1,addr=0x3 with RHEL8 guest produces following results: v5.2: kernel: virtio_net virtio0 ens1: renamed from eth0 kernel: virtio_net virtio2 ens3: renamed from eth2 kernel: virtio_net virtio1 enp1s2: renamed from eth1 (slot 2 is assigned to empty bus 0 slot and virtio1 is assigned to 2-2 slot, and renaming falls back, for some reason, to path based naming scheme) v6.0: kernel: virtio_net virtio0 ens1: renamed from eth0 kernel: virtio_net virtio2 ens3: renamed from eth2 systemd-udevd[299]: Error changing net interface name 'eth1' to 'ens3': File exists systemd-udevd[299]: could not rename interface '3' from 'eth1' to 'ens3': File exists (with commit [1] kernel assigns virtio2 to 3-2 slot since bridge advertises _SUN=0x3 and kernel assigns slot 3 to bridge. Still it manages to rename virtio2 correctly to ens3, however systemd gets confused with virtio1 where slot allocation exactly the same (2-2) as in 5.2 case and tries to rename it to ens3 which is rightfully taken by virtio2) I'm not sure what breaks in systemd interface renaming (it probably should be investigated), but on QEMU side we can safely revert _SUN to 5.2 behavior (i.e. avoid cold-plugged bridges and non hot-pluggable device classes), without breaking acpi-index, which uses slot numbers but it doesn't have to use _SUN, it could use an arbitrary variable name that has the same slot value). It will help existing VMs to keep networking with non trivial configs in working order since systemd will do its interface renaming magic as it used to do. 1) Fixes: b7f23f62e40 (pci: acpi: add _DSM method to PCI devices) Signed-off-by: Igor Mammedov Message-Id: <20210624204229.998824-3-imammedo@redhat.com> Reviewed-by: Stefan Hajnoczi Tested-by: John Sucaet Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-build.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 796ffc6f5c..357437ff1d 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -435,11 +435,15 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus, aml_append(dev, aml_name_decl("_ADR", aml_int(slot << 16))); if (bsel) { - aml_append(dev, aml_name_decl("_SUN", aml_int(slot))); + /* + * 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))); method = aml_method("_DSM", 4, AML_SERIALIZED); aml_append(method, aml_return( aml_call6("PDSM", aml_arg(0), aml_arg(1), aml_arg(2), - aml_arg(3), aml_name("BSEL"), aml_name("_SUN")) + aml_arg(3), aml_name("BSEL"), aml_name("ASUN")) )); aml_append(dev, method); } @@ -466,6 +470,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus, aml_append(method, aml_return(aml_int(s3d))); aml_append(dev, method); } else if (hotplug_enabled_dev) { + 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, From 40f23e4e52f6188036062abea432560e5cdd239a Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 24 Jun 2021 16:42:29 -0400 Subject: [PATCH 013/272] tests: acpi: pc: update expected DSDT blobs @@ -930,20 +930,20 @@ DefinitionBlock ("", "DSDT", 1, "BOCHS ", "BXPC ", 0x00000001) Device (S00) { Name (_ADR, Zero) // _ADR: Address - Name (_SUN, Zero) // _SUN: Slot User Number + Name (ASUN, Zero) Method (_DSM, 4, Serialized) // _DSM: Device-Specific Method { - Return (PDSM (Arg0, Arg1, Arg2, Arg3, BSEL, _SUN)) + Return (PDSM (Arg0, Arg1, Arg2, Arg3, BSEL, ASUN)) } } Device (S10) { Name (_ADR, 0x00020000) // _ADR: Address - Name (_SUN, 0x02) // _SUN: Slot User Number + Name (ASUN, 0x02) Method (_DSM, 4, Serialized) // _DSM: Device-Specific Method { - Return (PDSM (Arg0, Arg1, Arg2, Arg3, BSEL, _SUN)) + Return (PDSM (Arg0, Arg1, Arg2, Arg3, BSEL, ASUN)) } Method (_S1D, 0, NotSerialized) // _S1D: S1 Device State with a hank per bridge: @@ -965,10 +965,10 @@ DefinitionBlock ("", "DSDT", 1, "BOCHS ", "BXPC ", 0x00000001) Device (S18) { Name (_ADR, 0x00030000) // _ADR: Address - Name (_SUN, 0x03) // _SUN: Slot User Number + Name (ASUN, 0x03) Method (_DSM, 4, Serialized) // _DSM: Device-Specific Method { - Return (PDSM (Arg0, Arg1, Arg2, Arg3, BSEL, _SUN)) + Return (PDSM (Arg0, Arg1, Arg2, Arg3, BSEL, ASUN)) } } Signed-off-by: Igor Mammedov Message-Id: <20210624204229.998824-4-imammedo@redhat.com> Reviewed-by: Stefan Hajnoczi Tested-by: John Sucaet Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/pc/DSDT | Bin 6002 -> 6002 bytes tests/data/acpi/pc/DSDT.acpihmat | Bin 7327 -> 7327 bytes tests/data/acpi/pc/DSDT.bridge | Bin 8668 -> 8668 bytes tests/data/acpi/pc/DSDT.cphp | Bin 6466 -> 6466 bytes tests/data/acpi/pc/DSDT.dimmpxm | Bin 7656 -> 7656 bytes tests/data/acpi/pc/DSDT.hpbridge | Bin 5969 -> 5969 bytes tests/data/acpi/pc/DSDT.ipmikcs | Bin 6074 -> 6074 bytes tests/data/acpi/pc/DSDT.memhp | Bin 7361 -> 7361 bytes tests/data/acpi/pc/DSDT.nohpet | Bin 5860 -> 5860 bytes tests/data/acpi/pc/DSDT.numamem | Bin 6008 -> 6008 bytes tests/qtest/bios-tables-test-allowed-diff.h | 10 ---------- 11 files changed, 10 deletions(-) diff --git a/tests/data/acpi/pc/DSDT b/tests/data/acpi/pc/DSDT index b9dd9b38e4ef720636ba19ccbdf262de8a6439d5..cc1223773e9c459a8d2f20666c051a74338d40b7 100644 GIT binary patch delta 61 zcmeyQ_eqb-CD&USu&WP9BD%?o fCD_m)o+IATC5VTCfr)_wB+SKxRm0{lLaR9eXLTFp delta 62 zcmbPlIp31YCDgG8yduO#5=kK@h~tjF>rvS QxR@rdlak#0LuffC015RFTL1t6 diff --git a/tests/data/acpi/pc/DSDT.bridge b/tests/data/acpi/pc/DSDT.bridge index a9b4d5659457f6de30b993962bce673c9413d81d..77778c3a69946efd501e7eff0a73af309b553f13 100644 GIT binary patch delta 73 zcmccPe8-u~CD3CG}2zvw0}mS97Jc#e2SmmnSn1||j$ R$H}$)l8la3>@*3 PYxyM^<2O&|U(E>s%3KcC diff --git a/tests/data/acpi/pc/DSDT.dimmpxm b/tests/data/acpi/pc/DSDT.dimmpxm index e00a447f92b27f9a91be802eb11fe89dc0457e20..b56b2e089017f933f8a3089c4fd2389fb8ef1e40 100644 GIT binary patch delta 100 zcmaE1{lc2dCDwlr;t!7i diff --git a/tests/data/acpi/pc/DSDT.hpbridge b/tests/data/acpi/pc/DSDT.hpbridge index 5d8ba195055f2eda74223323baeb88390ea36739..bb0593eeb8730d51a6f0fe51a00a00df9c83c419 100644 GIT binary patch delta 78 zcmcbpcTtbaCD@v8 delta 74 zcmcbpcTtbaCD|UB3{<`;7~t?$&P$dLS8Jvh6eE*@s2J*JPZs>3>@*3 b*YZj-#!o)ZYrqCl#>EU&C%0Le&x{iQ&x;Zm diff --git a/tests/data/acpi/pc/DSDT.ipmikcs b/tests/data/acpi/pc/DSDT.ipmikcs index 01e53bd436698db6f6adfff584ec56cb99074a5f..2e618e49d357ae1d0ac20d822f71d676ea90f2fc 100644 GIT binary patch delta 53 zcmdm`ze}IXCDYt>y#( Dp7{>n delta 49 zcmdm`ze}IXCD T3>=P={|HGkI&KydUd;&rQ4bK$ delta 65 zcmX?TdC-!}CDwF!J&Q(lNp6YCOe3*33;&u8yduO#5=kK@h~tj UF>u6B{v#v_lw#Q|DZGLc04$Uc4*&oF diff --git a/tests/data/acpi/pc/DSDT.nohpet b/tests/data/acpi/pc/DSDT.nohpet index d4f0050533f970128774f825274177096a46c3b8..623f06a900d12500d2197d101f76f6875e92ed64 100644 GIT binary patch delta 61 zcmaE&`$U(^CD>0$#B;Y{|Qt696|m5IX<> delta 59 zcmaE&`$U(^CDnU(Dn?CJxOh;H&? f2{tr{=ZJT73F2X3U}E3^33D-F)v)<2?`lo}e)Jpa delta 62 zcmeyN_d}1%CD Date: Thu, 24 Jun 2021 13:00:56 +0200 Subject: [PATCH 014/272] acpi/ged: fix reset cause MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reset requests should use SHUTDOWN_CAUSE_GUEST_RESET not SHUTDOWN_CAUSE_GUEST_SHUTDOWN. Reported-by: Peter Maydell Signed-off-by: Gerd Hoffmann Message-Id: <20210624110057.2398779-1-kraxel@redhat.com> Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/generic_event_device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index 39c825763a..e28457a7d1 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -207,7 +207,7 @@ static void ged_regs_write(void *opaque, hwaddr addr, uint64_t data, return; case ACPI_GED_REG_RESET: if (data == ACPI_GED_RESET_VALUE) { - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } return; } From 9e2423ef58f37e1d9def4ef33b054cb7e86da1f7 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Mon, 14 Jun 2021 13:43:57 +0200 Subject: [PATCH 015/272] docs: add slot when adding new PCIe root port Without providing a specific slot, QEMU won't be able to create the second additional PCIe root port with the following error: $ qemu-system-x86_64 [...] -machine q35 \ > -device pcie-root-port,bus=pcie.0,id=rp1 \ > -device pcie-root-port,bus=pcie.0,id=rp2 qemu-system-x86_64: -device pcie-root-port,bus=pcie.0,id=rp2: Can't add chassis slot, error -16 This is due to the fact they both try to use slot 0. Update the documentation to specify a slot for each new PCIe root port. Signed-off-by: Vincent Bernat Message-Id: <20210614114357.1146725-1-vincent@bernat.ch> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- docs/pcie_pci_bridge.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/pcie_pci_bridge.txt b/docs/pcie_pci_bridge.txt index ab35ebf3ca..1aa08fc5f0 100644 --- a/docs/pcie_pci_bridge.txt +++ b/docs/pcie_pci_bridge.txt @@ -70,9 +70,9 @@ A detailed command line would be: [qemu-bin + storage options] \ -m 2G \ --device pcie-root-port,bus=pcie.0,id=rp1 \ --device pcie-root-port,bus=pcie.0,id=rp2 \ --device pcie-root-port,bus=pcie.0,id=rp3,bus-reserve=1 \ +-device pcie-root-port,bus=pcie.0,id=rp1,slot=1 \ +-device pcie-root-port,bus=pcie.0,id=rp2,slot=2 \ +-device pcie-root-port,bus=pcie.0,id=rp3,slot=3,bus-reserve=1 \ -device pcie-pci-bridge,id=br1,bus=rp1 \ -device pcie-pci-bridge,id=br2,bus=rp2 \ -device e1000,bus=br1,addr=8 From 0f08586c7171757d77c27ee6c606e8a1c44ac6e3 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Wed, 14 Apr 2021 21:02:46 +0100 Subject: [PATCH 016/272] util/async: add a human-readable name to BHs for debugging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It can be difficult to debug issues with BHs in production environments. Although BHs can usually be identified by looking up their ->cb() function pointer, this requires debug information for the program. It is also not possible to print human-readable diagnostics about BHs because they have no identifier. This patch adds a name to each BH. The name is not unique per instance but differentiates between cb() functions, which is usually enough. It's done by changing aio_bh_new() and friends to macros that stringify cb. The next patch will use the name field when reporting leaked BHs. Signed-off-by: Stefan Hajnoczi Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20210414200247.917496-2-stefanha@redhat.com> --- include/block/aio.h | 35 +++++++++++++++++++++++++++++----- include/qemu/main-loop.h | 4 +++- tests/unit/ptimer-test-stubs.c | 2 +- util/async.c | 9 +++++++-- util/main-loop.c | 4 ++-- 5 files changed, 43 insertions(+), 11 deletions(-) diff --git a/include/block/aio.h b/include/block/aio.h index 10fcae1515..807edce9b5 100644 --- a/include/block/aio.h +++ b/include/block/aio.h @@ -292,19 +292,44 @@ void aio_context_acquire(AioContext *ctx); void aio_context_release(AioContext *ctx); /** - * aio_bh_schedule_oneshot: Allocate a new bottom half structure that will run - * only once and as soon as possible. + * aio_bh_schedule_oneshot_full: Allocate a new bottom half structure that will + * run only once and as soon as possible. + * + * @name: A human-readable identifier for debugging purposes. */ -void aio_bh_schedule_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque); +void aio_bh_schedule_oneshot_full(AioContext *ctx, QEMUBHFunc *cb, void *opaque, + const char *name); /** - * aio_bh_new: Allocate a new bottom half structure. + * aio_bh_schedule_oneshot: Allocate a new bottom half structure that will run + * only once and as soon as possible. + * + * A convenience wrapper for aio_bh_schedule_oneshot_full() that uses cb as the + * name string. + */ +#define aio_bh_schedule_oneshot(ctx, cb, opaque) \ + aio_bh_schedule_oneshot_full((ctx), (cb), (opaque), (stringify(cb))) + +/** + * aio_bh_new_full: Allocate a new bottom half structure. * * Bottom halves are lightweight callbacks whose invocation is guaranteed * to be wait-free, thread-safe and signal-safe. The #QEMUBH structure * is opaque and must be allocated prior to its use. + * + * @name: A human-readable identifier for debugging purposes. */ -QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque); +QEMUBH *aio_bh_new_full(AioContext *ctx, QEMUBHFunc *cb, void *opaque, + const char *name); + +/** + * aio_bh_new: Allocate a new bottom half structure + * + * A convenience wrapper for aio_bh_new_full() that uses the cb as the name + * string. + */ +#define aio_bh_new(ctx, cb, opaque) \ + aio_bh_new_full((ctx), (cb), (opaque), (stringify(cb))) /** * aio_notify: Force processing of pending events. diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h index 98aef5647c..8dbc6fcb89 100644 --- a/include/qemu/main-loop.h +++ b/include/qemu/main-loop.h @@ -294,7 +294,9 @@ void qemu_cond_timedwait_iothread(QemuCond *cond, int ms); void qemu_fd_register(int fd); -QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque); +#define qemu_bh_new(cb, opaque) \ + qemu_bh_new_full((cb), (opaque), (stringify(cb))) +QEMUBH *qemu_bh_new_full(QEMUBHFunc *cb, void *opaque, const char *name); void qemu_bh_schedule_idle(QEMUBH *bh); enum { diff --git a/tests/unit/ptimer-test-stubs.c b/tests/unit/ptimer-test-stubs.c index 7f801a4d09..2a3ef58799 100644 --- a/tests/unit/ptimer-test-stubs.c +++ b/tests/unit/ptimer-test-stubs.c @@ -108,7 +108,7 @@ int64_t qemu_clock_deadline_ns_all(QEMUClockType type, int attr_mask) return deadline; } -QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque) +QEMUBH *qemu_bh_new_full(QEMUBHFunc *cb, void *opaque, const char *name) { QEMUBH *bh = g_new(QEMUBH, 1); diff --git a/util/async.c b/util/async.c index 5d9b7cc1eb..9a668996b8 100644 --- a/util/async.c +++ b/util/async.c @@ -57,6 +57,7 @@ enum { struct QEMUBH { AioContext *ctx; + const char *name; QEMUBHFunc *cb; void *opaque; QSLIST_ENTRY(QEMUBH) next; @@ -107,7 +108,8 @@ static QEMUBH *aio_bh_dequeue(BHList *head, unsigned *flags) return bh; } -void aio_bh_schedule_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque) +void aio_bh_schedule_oneshot_full(AioContext *ctx, QEMUBHFunc *cb, + void *opaque, const char *name) { QEMUBH *bh; bh = g_new(QEMUBH, 1); @@ -115,11 +117,13 @@ void aio_bh_schedule_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque) .ctx = ctx, .cb = cb, .opaque = opaque, + .name = name, }; aio_bh_enqueue(bh, BH_SCHEDULED | BH_ONESHOT); } -QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque) +QEMUBH *aio_bh_new_full(AioContext *ctx, QEMUBHFunc *cb, void *opaque, + const char *name) { QEMUBH *bh; bh = g_new(QEMUBH, 1); @@ -127,6 +131,7 @@ QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque) .ctx = ctx, .cb = cb, .opaque = opaque, + .name = name, }; return bh; } diff --git a/util/main-loop.c b/util/main-loop.c index 4ae5b23e99..06b18b195c 100644 --- a/util/main-loop.c +++ b/util/main-loop.c @@ -544,9 +544,9 @@ void main_loop_wait(int nonblocking) /* Functions to operate on the main QEMU AioContext. */ -QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque) +QEMUBH *qemu_bh_new_full(QEMUBHFunc *cb, void *opaque, const char *name) { - return aio_bh_new(qemu_aio_context, cb, opaque); + return aio_bh_new_full(qemu_aio_context, cb, opaque, name); } /* From 023ca420ee3d4de76518d690afa98dcac33998ce Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Wed, 14 Apr 2021 21:02:47 +0100 Subject: [PATCH 017/272] util/async: print leaked BH name when AioContext finalizes BHs must be deleted before the AioContext is finalized. If not, it's a bug and probably indicates that some part of the program still expects the BH to run in the future. That can lead to memory leaks, inconsistent state, or just hangs. Unfortunately the assert(flags & BH_DELETED) call in aio_ctx_finalize() is difficult to debug because the assertion failure contains no information about the BH! Use the QEMUBH name field added in the previous patch to show a useful error when a leaked BH is detected. Suggested-by: Eric Ernst Signed-off-by: Stefan Hajnoczi Message-Id: <20210414200247.917496-3-stefanha@redhat.com> --- util/async.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/util/async.c b/util/async.c index 9a668996b8..9a41591319 100644 --- a/util/async.c +++ b/util/async.c @@ -344,8 +344,20 @@ aio_ctx_finalize(GSource *source) assert(QSIMPLEQ_EMPTY(&ctx->bh_slice_list)); while ((bh = aio_bh_dequeue(&ctx->bh_list, &flags))) { - /* qemu_bh_delete() must have been called on BHs in this AioContext */ - assert(flags & BH_DELETED); + /* + * qemu_bh_delete() must have been called on BHs in this AioContext. In + * many cases memory leaks, hangs, or inconsistent state occur when a + * BH is leaked because something still expects it to run. + * + * If you hit this, fix the lifecycle of the BH so that + * qemu_bh_delete() and any associated cleanup is called before the + * AioContext is finalized. + */ + if (unlikely(!(flags & BH_DELETED))) { + fprintf(stderr, "%s: BH '%s' leaked, aborting...\n", + __func__, bh->name); + abort(); + } g_free(bh); } From 0dfc7af2b287323dde0725c04f5765e0e508d9fd Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Mon, 5 Jul 2021 22:04:56 +0900 Subject: [PATCH 018/272] block/file-posix: Optimize for macOS This commit introduces "punch hole" operation and optimizes transfer block size for macOS. Thanks to Konstantin Nazarov for detailed analysis of a flaw in an old version of this change: https://gist.github.com/akihikodaki/87df4149e7ca87f18dc56807ec5a1bc5#gistcomment-3654667 Signed-off-by: Akihiko Odaki Message-id: 20210705130458.97642-1-akihiko.odaki@gmail.com Signed-off-by: Stefan Hajnoczi --- block/file-posix.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index a26eab0ac3..cb9bffe047 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -46,6 +46,7 @@ #if defined(HAVE_HOST_BLOCK_DEVICE) #include #include +#include #include #include #include @@ -1254,6 +1255,15 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) return; } +#if defined(__APPLE__) && (__MACH__) + struct statfs buf; + + if (!fstatfs(s->fd, &buf)) { + bs->bl.opt_transfer = buf.f_iosize; + bs->bl.pdiscard_alignment = buf.f_bsize; + } +#endif + if (bs->sg || S_ISBLK(st.st_mode)) { int ret = hdev_get_max_hw_transfer(s->fd, &st); @@ -1591,6 +1601,7 @@ out: } } +#if defined(CONFIG_FALLOCATE) || defined(BLKZEROOUT) || defined(BLKDISCARD) static int translate_err(int err) { if (err == -ENODEV || err == -ENOSYS || err == -EOPNOTSUPP || @@ -1599,6 +1610,7 @@ static int translate_err(int err) } return err; } +#endif #ifdef CONFIG_FALLOCATE static int do_fallocate(int fd, int mode, off_t offset, off_t len) @@ -1811,16 +1823,27 @@ static int handle_aiocb_discard(void *opaque) } } while (errno == EINTR); - ret = -errno; + ret = translate_err(-errno); #endif } else { #ifdef CONFIG_FALLOCATE_PUNCH_HOLE ret = do_fallocate(s->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, aiocb->aio_offset, aiocb->aio_nbytes); + ret = translate_err(-errno); +#elif defined(__APPLE__) && (__MACH__) + fpunchhole_t fpunchhole; + fpunchhole.fp_flags = 0; + fpunchhole.reserved = 0; + fpunchhole.fp_offset = aiocb->aio_offset; + fpunchhole.fp_length = aiocb->aio_nbytes; + if (fcntl(s->fd, F_PUNCHHOLE, &fpunchhole) == -1) { + ret = errno == ENODEV ? -ENOTSUP : -errno; + } else { + ret = 0; + } #endif } - ret = translate_err(ret); if (ret == -ENOTSUP) { s->has_discard = false; } From 12a521b56d0538ca0363dd79db8f359cef40da69 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Mon, 5 Jul 2021 22:04:57 +0900 Subject: [PATCH 019/272] block: Add backend_defaults property backend_defaults property allow users to control if default block properties should be decided with backend information. If it is off, any backend information will be discarded, which is suitable if you plan to perform live migration to a different disk backend. If it is on, a block device may utilize backend information more aggressively. By default, it is auto, which uses backend information for block sizes and ignores the others, which is consistent with the older versions. Signed-off-by: Akihiko Odaki Message-id: 20210705130458.97642-2-akihiko.odaki@gmail.com Signed-off-by: Stefan Hajnoczi --- hw/block/block.c | 42 ++++++++++++++++++++++++++++++++++---- include/hw/block/block.h | 3 +++ tests/qemu-iotests/172.out | 38 ++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/hw/block/block.c b/hw/block/block.c index 1e34573da7..d47ebf005a 100644 --- a/hw/block/block.c +++ b/hw/block/block.c @@ -65,24 +65,58 @@ bool blkconf_blocksizes(BlockConf *conf, Error **errp) { BlockBackend *blk = conf->blk; BlockSizes blocksizes; - int backend_ret; + BlockDriverState *bs; + bool use_blocksizes; + bool use_bs; + + switch (conf->backend_defaults) { + case ON_OFF_AUTO_AUTO: + use_blocksizes = !blk_probe_blocksizes(blk, &blocksizes); + use_bs = false; + break; + + case ON_OFF_AUTO_ON: + use_blocksizes = !blk_probe_blocksizes(blk, &blocksizes); + bs = blk_bs(blk); + use_bs = bs; + break; + + case ON_OFF_AUTO_OFF: + use_blocksizes = false; + use_bs = false; + break; + + default: + abort(); + } - backend_ret = blk_probe_blocksizes(blk, &blocksizes); /* fill in detected values if they are not defined via qemu command line */ if (!conf->physical_block_size) { - if (!backend_ret) { + if (use_blocksizes) { conf->physical_block_size = blocksizes.phys; } else { conf->physical_block_size = BDRV_SECTOR_SIZE; } } if (!conf->logical_block_size) { - if (!backend_ret) { + if (use_blocksizes) { conf->logical_block_size = blocksizes.log; } else { conf->logical_block_size = BDRV_SECTOR_SIZE; } } + if (use_bs) { + if (!conf->opt_io_size) { + conf->opt_io_size = bs->bl.opt_transfer; + } + if (conf->discard_granularity == -1) { + if (bs->bl.pdiscard_alignment) { + conf->discard_granularity = bs->bl.pdiscard_alignment; + } else if (bs->bl.request_alignment != 1) { + conf->discard_granularity = bs->bl.request_alignment; + } + } + } if (conf->logical_block_size > conf->physical_block_size) { error_setg(errp, diff --git a/include/hw/block/block.h b/include/hw/block/block.h index c172cbe65f..5902c0440a 100644 --- a/include/hw/block/block.h +++ b/include/hw/block/block.h @@ -19,6 +19,7 @@ typedef struct BlockConf { BlockBackend *blk; + OnOffAuto backend_defaults; uint32_t physical_block_size; uint32_t logical_block_size; uint32_t min_io_size; @@ -48,6 +49,8 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf) } #define DEFINE_BLOCK_PROPERTIES_BASE(_state, _conf) \ + DEFINE_PROP_ON_OFF_AUTO("backend_defaults", _state, \ + _conf.backend_defaults, ON_OFF_AUTO_AUTO), \ DEFINE_PROP_BLOCKSIZE("logical_block_size", _state, \ _conf.logical_block_size), \ DEFINE_PROP_BLOCKSIZE("physical_block_size", _state, \ diff --git a/tests/qemu-iotests/172.out b/tests/qemu-iotests/172.out index d53f61d0de..4cf4d536b4 100644 --- a/tests/qemu-iotests/172.out +++ b/tests/qemu-iotests/172.out @@ -21,6 +21,7 @@ Testing: dev: floppy, id "" unit = 0 (0x0) drive = "floppy0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -48,6 +49,7 @@ Testing: -fda TEST_DIR/t.qcow2 dev: floppy, id "" unit = 0 (0x0) drive = "floppy0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -85,6 +87,7 @@ Testing: -fdb TEST_DIR/t.qcow2 dev: floppy, id "" unit = 1 (0x1) drive = "floppy1" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -96,6 +99,7 @@ Testing: -fdb TEST_DIR/t.qcow2 dev: floppy, id "" unit = 0 (0x0) drive = "floppy0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -137,6 +141,7 @@ Testing: -fda TEST_DIR/t.qcow2 -fdb TEST_DIR/t.qcow2.2 dev: floppy, id "" unit = 1 (0x1) drive = "floppy1" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -148,6 +153,7 @@ Testing: -fda TEST_DIR/t.qcow2 -fdb TEST_DIR/t.qcow2.2 dev: floppy, id "" unit = 0 (0x0) drive = "floppy0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -190,6 +196,7 @@ Testing: -fdb dev: floppy, id "" unit = 1 (0x1) drive = "floppy1" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -201,6 +208,7 @@ Testing: -fdb dev: floppy, id "" unit = 0 (0x0) drive = "floppy0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -228,6 +236,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 dev: floppy, id "" unit = 0 (0x0) drive = "floppy0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -265,6 +274,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2,index=1 dev: floppy, id "" unit = 1 (0x1) drive = "floppy1" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -276,6 +286,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2,index=1 dev: floppy, id "" unit = 0 (0x0) drive = "floppy0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -317,6 +328,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=floppy,file=TEST_DIR/t dev: floppy, id "" unit = 1 (0x1) drive = "floppy1" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -328,6 +340,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=floppy,file=TEST_DIR/t dev: floppy, id "" unit = 0 (0x0) drive = "floppy0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -373,6 +386,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0 dev: floppy, id "" unit = 0 (0x0) drive = "none0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -410,6 +424,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1 dev: floppy, id "" unit = 1 (0x1) drive = "none0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -447,6 +462,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco dev: floppy, id "" unit = 1 (0x1) drive = "none1" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -458,6 +474,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco dev: floppy, id "" unit = 0 (0x0) drive = "none0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -509,6 +526,7 @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl dev: floppy, id "" unit = 1 (0x1) drive = "none0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -520,6 +538,7 @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl dev: floppy, id "" unit = 0 (0x0) drive = "floppy0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -562,6 +581,7 @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl dev: floppy, id "" unit = 1 (0x1) drive = "none0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -573,6 +593,7 @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl dev: floppy, id "" unit = 0 (0x0) drive = "floppy0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -615,6 +636,7 @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl dev: floppy, id "" unit = 0 (0x0) drive = "none0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -626,6 +648,7 @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl dev: floppy, id "" unit = 1 (0x1) drive = "floppy1" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -668,6 +691,7 @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl dev: floppy, id "" unit = 0 (0x0) drive = "none0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -679,6 +703,7 @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl dev: floppy, id "" unit = 1 (0x1) drive = "floppy1" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -730,6 +755,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q dev: floppy, id "" unit = 1 (0x1) drive = "none0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -741,6 +767,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q dev: floppy, id "" unit = 0 (0x0) drive = "floppy0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -783,6 +810,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q dev: floppy, id "" unit = 1 (0x1) drive = "none0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -794,6 +822,7 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q dev: floppy, id "" unit = 0 (0x0) drive = "floppy0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -842,6 +871,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -global floppy.drive=none0 -device dev: floppy, id "" unit = 0 (0x0) drive = "none0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -909,6 +939,7 @@ Testing: -device floppy dev: floppy, id "" unit = 0 (0x0) drive = "" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -933,6 +964,7 @@ Testing: -device floppy,drive-type=120 dev: floppy, id "" unit = 0 (0x0) drive = "" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -957,6 +989,7 @@ Testing: -device floppy,drive-type=144 dev: floppy, id "" unit = 0 (0x0) drive = "" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -981,6 +1014,7 @@ Testing: -device floppy,drive-type=288 dev: floppy, id "" unit = 0 (0x0) drive = "" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -1008,6 +1042,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,drive-t dev: floppy, id "" unit = 0 (0x0) drive = "none0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -1045,6 +1080,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,drive-t dev: floppy, id "" unit = 0 (0x0) drive = "none0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -1085,6 +1121,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,logical dev: floppy, id "" unit = 0 (0x0) drive = "none0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) @@ -1122,6 +1159,7 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,physica dev: floppy, id "" unit = 0 (0x0) drive = "none0" + backend_defaults = "auto" logical_block_size = 512 (512 B) physical_block_size = 512 (512 B) min_io_size = 0 (0 B) From 9f460c64e13897117f35ffb61f6f5e0102cabc70 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Mon, 5 Jul 2021 22:04:58 +0900 Subject: [PATCH 020/272] block/io: Merge discard request alignments Signed-off-by: Akihiko Odaki Message-id: 20210705130458.97642-3-akihiko.odaki@gmail.com Signed-off-by: Stefan Hajnoczi --- block/io.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/block/io.c b/block/io.c index cf177a9d2d..e0a689c584 100644 --- a/block/io.c +++ b/block/io.c @@ -125,6 +125,8 @@ void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll) static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src) { + dst->pdiscard_alignment = MAX(dst->pdiscard_alignment, + src->pdiscard_alignment); dst->opt_transfer = MAX(dst->opt_transfer, src->opt_transfer); dst->max_transfer = MIN_NON_ZERO(dst->max_transfer, src->max_transfer); dst->max_hw_transfer = MIN_NON_ZERO(dst->max_hw_transfer, From 80cc1a0dd19cc414ddaa3f1b9b6ef91e3ebc12b2 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 9 Oct 2020 16:07:01 -0400 Subject: [PATCH 021/272] vmbus: Don't make QOM property registration conditional Having properties registered conditionally makes QOM type introspection difficult. Instead of skipping registration of the "instanceid" property, always register the property but validate its value against the instance id required by the class. Signed-off-by: Eduardo Habkost Acked-by: Maciej S. Szmigiero Message-Id: <20201009200701.1830060-1-ehabkost@redhat.com> Signed-off-by: Eduardo Habkost --- hw/hyperv/vmbus.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/hw/hyperv/vmbus.c b/hw/hyperv/vmbus.c index 984caf898d..c9887d5a7b 100644 --- a/hw/hyperv/vmbus.c +++ b/hw/hyperv/vmbus.c @@ -2372,6 +2372,14 @@ static void vmbus_dev_realize(DeviceState *dev, Error **errp) assert(!qemu_uuid_is_null(&vdev->instanceid)); + if (!qemu_uuid_is_null(&vdc->instanceid)) { + /* Class wants to only have a single instance with a fixed UUID */ + if (!qemu_uuid_is_equal(&vdev->instanceid, &vdc->instanceid)) { + error_setg(&err, "instance id can't be changed"); + goto error_out; + } + } + /* Check for instance id collision for this class id */ QTAILQ_FOREACH(child, &BUS(vmbus)->children, sibling) { VMBusDevice *child_dev = VMBUS_DEVICE(child->child); @@ -2438,18 +2446,22 @@ static void vmbus_dev_unrealize(DeviceState *dev) free_channels(vdev); } +static Property vmbus_dev_props[] = { + DEFINE_PROP_UUID("instanceid", VMBusDevice, instanceid), + DEFINE_PROP_END_OF_LIST() +}; + + static void vmbus_dev_class_init(ObjectClass *klass, void *data) { DeviceClass *kdev = DEVICE_CLASS(klass); + device_class_set_props(kdev, vmbus_dev_props); kdev->bus_type = TYPE_VMBUS; kdev->realize = vmbus_dev_realize; kdev->unrealize = vmbus_dev_unrealize; kdev->reset = vmbus_dev_reset; } -static Property vmbus_dev_instanceid = - DEFINE_PROP_UUID("instanceid", VMBusDevice, instanceid); - static void vmbus_dev_instance_init(Object *obj) { VMBusDevice *vdev = VMBUS_DEVICE(obj); @@ -2458,8 +2470,6 @@ static void vmbus_dev_instance_init(Object *obj) if (!qemu_uuid_is_null(&vdc->instanceid)) { /* Class wants to only have a single instance with a fixed UUID */ vdev->instanceid = vdc->instanceid; - } else { - qdev_property_add_static(DEVICE(vdev), &vmbus_dev_instanceid); } } From cdcf766d0b0364165ba9e5ceacfdf37c8b1fe4ae Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Mon, 11 Jan 2021 15:33:32 -0500 Subject: [PATCH 022/272] Deprecate pmem=on with non-DAX capable backend file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is not safe to pretend that emulated NVDIMM supports persistence while backend actually failed to enable it and used non-persistent mapping as fall back. Instead of falling-back, QEMU should be more strict and error out with clear message that it's not supported. So if user asks for persistence (pmem=on), they should store backing file on NVDIMM. Signed-off-by: Igor Mammedov Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20210111203332.740815-1-imammedo@redhat.com> Signed-off-by: Eduardo Habkost --- docs/system/deprecated.rst | 18 ++++++++++++++++++ util/mmap-alloc.c | 2 ++ 2 files changed, 20 insertions(+) diff --git a/docs/system/deprecated.rst b/docs/system/deprecated.rst index 70e08baff6..94fb7dbf4e 100644 --- a/docs/system/deprecated.rst +++ b/docs/system/deprecated.rst @@ -221,6 +221,24 @@ This machine is deprecated because we have enough AST2500 based OpenPOWER machines. It can be easily replaced by the ``witherspoon-bmc`` or the ``romulus-bmc`` machines. +Backend options +--------------- + +Using non-persistent backing file with pmem=on (since 6.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +This option is used when ``memory-backend-file`` is consumed by emulated NVDIMM +device. However enabling ``memory-backend-file.pmem`` option, when backing file +is (a) not DAX capable or (b) not on a filesystem that support direct mapping +of persistent memory, is not safe and may lead to data loss or corruption in case +of host crash. +Options are: + + - modify VM configuration to set ``pmem=off`` to continue using fake NVDIMM + (without persistence guaranties) with backing file on non DAX storage + - move backing file to NVDIMM storage and keep ``pmem=on`` + (to have NVDIMM with persistence guaranties). + Device options -------------- diff --git a/util/mmap-alloc.c b/util/mmap-alloc.c index 838e286ce5..893d864354 100644 --- a/util/mmap-alloc.c +++ b/util/mmap-alloc.c @@ -225,6 +225,8 @@ static void *mmap_activate(void *ptr, size_t size, int fd, "crash.\n", file_name); g_free(proc_link); g_free(file_name); + warn_report("Using non DAX backing file with 'pmem=on' option" + " is deprecated"); } /* * If mmap failed with MAP_SHARED_VALIDATE | MAP_SYNC, we will try From e3e01466bff42b5ea977340d8d7d90df482b0c97 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Mon, 14 Jun 2021 14:28:42 -0600 Subject: [PATCH 023/272] MAINTAINERS: Add maintainer for vhost-user RNG implementation This patch adds entry for the vhost-user-rng related files. Signed-off-by: Mathieu Poirier Message-Id: <20210614202842.581640-6-mathieu.poirier@linaro.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index cfbf7ef79b..517f22b2e7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1949,6 +1949,15 @@ F: include/sysemu/rng*.h F: backends/rng*.c F: tests/qtest/virtio-rng-test.c +vhost-user-rng +M: Mathieu Poirier +S: Supported +F: docs/tools/vhost-user-rng.rst +F: hw/virtio/vhost-user-rng.c +F: hw/virtio/vhost-user-rng-pci.c +F: include/hw/virtio/vhost-user-rng.h +F: tools/vhost-user-rng/* + virtio-crypto M: Gonglei S: Supported From fb4a08121695a88acefcbcd86f1376df079eefee Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Tue, 22 Jun 2021 22:19:23 +0200 Subject: [PATCH 024/272] s390x/cpumodel: add 3931 and 3932 This defines 5 new facilities and the new 3931 and 3932 machines. As before the name is not yet known and we do use gen16a and gen16b. The new features are part of the full model. The default model is still empty (same as z15) and will be added in a separate patch at a later point in time. Also add the dependencies of new facilities and as a fix for z15 add a dependency from S390_FEAT_VECTOR_PACKED_DECIMAL_ENH to S390_VECTOR_PACKED_DECIMAL. [merged <20210701084348.26556-1-borntraeger@de.ibm.com>] Signed-off-by: Christian Borntraeger Message-Id: <20210622201923.150205-2-borntraeger@de.ibm.com> Reviewed-by: David Hildenbrand Signed-off-by: Cornelia Huck --- target/s390x/cpu_features_def.h.inc | 5 +++++ target/s390x/cpu_models.c | 6 ++++++ target/s390x/gen-features.c | 14 ++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/target/s390x/cpu_features_def.h.inc b/target/s390x/cpu_features_def.h.inc index 7db3449e04..e86662bb3b 100644 --- a/target/s390x/cpu_features_def.h.inc +++ b/target/s390x/cpu_features_def.h.inc @@ -109,6 +109,11 @@ DEF_FEAT(VECTOR_PACKED_DECIMAL_ENH, "vxpdeh", STFL, 152, "Vector-Packed-Decimal- DEF_FEAT(MSA_EXT_9, "msa9-base", STFL, 155, "Message-security-assist-extension-9 facility (excluding subfunctions)") DEF_FEAT(ETOKEN, "etoken", STFL, 156, "Etoken facility") DEF_FEAT(UNPACK, "unpack", STFL, 161, "Unpack facility") +DEF_FEAT(NNPA, "nnpa", STFL, 165, "NNPA facility") +DEF_FEAT(VECTOR_PACKED_DECIMAL_ENH2, "vxpdeh2", STFL, 192, "Vector-Packed-Decimal-Enhancement facility 2") +DEF_FEAT(BEAR_ENH, "beareh", STFL, 193, "BEAR-enhancement facility") +DEF_FEAT(RDP, "rdp", STFL, 194, "Reset-DAT-protection facility") +DEF_FEAT(PAI, "pai", STFL, 196, "Processor-Activity-Instrumentation facility") /* Features exposed via SCLP SCCB Byte 80 - 98 (bit numbers relative to byte-80) */ DEF_FEAT(SIE_GSLS, "gsls", SCLP_CONF_CHAR, 40, "SIE: Guest-storage-limit-suppression facility") diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 94090a6e22..9699823b20 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -88,6 +88,8 @@ static S390CPUDef s390_cpu_defs[] = { CPUDEF_INIT(0x3907, 14, 1, 47, 0x08000000U, "z14ZR1", "IBM z14 Model ZR1 GA1"), CPUDEF_INIT(0x8561, 15, 1, 47, 0x08000000U, "gen15a", "IBM z15 T01 GA1"), CPUDEF_INIT(0x8562, 15, 1, 47, 0x08000000U, "gen15b", "IBM z15 T02 GA1"), + CPUDEF_INIT(0x3931, 16, 1, 47, 0x08000000U, "gen16a", "IBM 3931 GA1"), + CPUDEF_INIT(0x3932, 16, 1, 47, 0x08000000U, "gen16b", "IBM 3932 GA1"), }; #define QEMU_MAX_CPU_TYPE 0x3906 @@ -812,6 +814,8 @@ static void check_consistency(const S390CPUModel *model) { S390_FEAT_MSA_EXT_9, S390_FEAT_MSA_EXT_4 }, { S390_FEAT_MULTIPLE_EPOCH, S390_FEAT_TOD_CLOCK_STEERING }, { S390_FEAT_VECTOR_PACKED_DECIMAL, S390_FEAT_VECTOR }, + { S390_FEAT_VECTOR_PACKED_DECIMAL_ENH, S390_FEAT_VECTOR_PACKED_DECIMAL }, + { S390_FEAT_VECTOR_PACKED_DECIMAL_ENH2, S390_FEAT_VECTOR_PACKED_DECIMAL_ENH }, { S390_FEAT_VECTOR_ENH, S390_FEAT_VECTOR }, { S390_FEAT_INSTRUCTION_EXEC_PROT, S390_FEAT_SIDE_EFFECT_ACCESS_ESOP2 }, { S390_FEAT_SIDE_EFFECT_ACCESS_ESOP2, S390_FEAT_ESOP }, @@ -843,6 +847,8 @@ static void check_consistency(const S390CPUModel *model) { S390_FEAT_PTFF_STOUE, S390_FEAT_MULTIPLE_EPOCH }, { S390_FEAT_AP_QUEUE_INTERRUPT_CONTROL, S390_FEAT_AP }, { S390_FEAT_DIAG_318, S390_FEAT_EXTENDED_LENGTH_SCCB }, + { S390_FEAT_NNPA, S390_FEAT_VECTOR }, + { S390_FEAT_RDP, S390_FEAT_LOCAL_TLB_CLEARING }, }; int i; diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c index 242c95ede4..7d85322d68 100644 --- a/target/s390x/gen-features.c +++ b/target/s390x/gen-features.c @@ -424,6 +424,8 @@ static uint16_t base_GEN15_GA1[] = { S390_FEAT_MISC_INSTRUCTION_EXT3, }; +#define base_GEN16_GA1 EmptyFeat + /* Full features (in order of release) * Automatically includes corresponding base features. * Full features are all features this hardware supports even if kvm/QEMU do not @@ -567,6 +569,15 @@ static uint16_t full_GEN15_GA1[] = { S390_FEAT_UNPACK, }; +static uint16_t full_GEN16_GA1[] = { + S390_FEAT_NNPA, + S390_FEAT_VECTOR_PACKED_DECIMAL_ENH2, + S390_FEAT_BEAR_ENH, + S390_FEAT_RDP, + S390_FEAT_PAI, +}; + + /* Default features (in order of release) * Automatically includes corresponding base features. * Default features are all features this version of QEMU supports for this @@ -652,6 +663,8 @@ static uint16_t default_GEN15_GA1[] = { S390_FEAT_ETOKEN, }; +#define default_GEN16_GA1 EmptyFeat + /* QEMU (CPU model) features */ static uint16_t qemu_V2_11[] = { @@ -785,6 +798,7 @@ static CpuFeatDefSpec CpuFeatDef[] = { CPU_FEAT_INITIALIZER(GEN14_GA1), CPU_FEAT_INITIALIZER(GEN14_GA2), CPU_FEAT_INITIALIZER(GEN15_GA1), + CPU_FEAT_INITIALIZER(GEN16_GA1), }; #define FEAT_GROUP_INITIALIZER(_name) \ From 28761057043aa234b33a3301b39c8707984bb0a0 Mon Sep 17 00:00:00 2001 From: Ulrich Weigand Date: Wed, 30 Jun 2021 12:50:58 +0200 Subject: [PATCH 025/272] target/s390x: Fix CC set by CONVERT TO FIXED/LOGICAL The FP-to-integer conversion instructions need to set CC 3 whenever a "special case" occurs; this is the case whenever the instruction also signals the IEEE invalid exception. (See e.g. figure 19-18 in the Principles of Operation.) However, qemu currently will set CC 3 only in the case where the input was a NaN. This is indeed one of the special cases, but there are others, most notably the case where the input is out of range of the target data type. This patch fixes the problem by switching these instructions to the "static" CC method and computing the correct result directly in the helper. (It cannot be re-computed later as the information about the invalid exception is no longer available.) This fixes a bug observed when running the wasmtime test suite under the s390x-linux-user target. Signed-off-by: Ulrich Weigand Reviewed-by: Richard Henderson Message-Id: <20210630105058.GA29130@oc3748833570.ibm.com> Signed-off-by: Cornelia Huck --- target/s390x/fpu_helper.c | 63 ++++++++++++++++++++++++++++++++++++--- target/s390x/helper.h | 24 +++++++-------- target/s390x/translate.c | 39 ++++++++---------------- 3 files changed, 83 insertions(+), 43 deletions(-) diff --git a/target/s390x/fpu_helper.c b/target/s390x/fpu_helper.c index 13af158748..a42741eec6 100644 --- a/target/s390x/fpu_helper.c +++ b/target/s390x/fpu_helper.c @@ -168,6 +168,34 @@ uint32_t set_cc_nz_f128(float128 v) } } +/* condition codes for FP to integer conversion ops */ +static uint32_t set_cc_conv_f32(float32 v, float_status *stat) +{ + if (stat->float_exception_flags & float_flag_invalid) { + return 3; + } else { + return set_cc_nz_f32(v); + } +} + +static uint32_t set_cc_conv_f64(float64 v, float_status *stat) +{ + if (stat->float_exception_flags & float_flag_invalid) { + return 3; + } else { + return set_cc_nz_f64(v); + } +} + +static uint32_t set_cc_conv_f128(float128 v, float_status *stat) +{ + if (stat->float_exception_flags & float_flag_invalid) { + return 3; + } else { + return set_cc_nz_f128(v); + } +} + static inline uint8_t round_from_m34(uint32_t m34) { return extract32(m34, 0, 4); @@ -506,9 +534,11 @@ uint64_t HELPER(cgeb)(CPUS390XState *env, uint64_t v2, uint32_t m34) { int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); int64_t ret = float32_to_int64(v2, &env->fpu_status); + uint32_t cc = set_cc_conv_f32(v2, &env->fpu_status); s390_restore_bfp_rounding_mode(env, old_mode); handle_exceptions(env, xxc_from_m34(m34), GETPC()); + env->cc_op = cc; if (float32_is_any_nan(v2)) { return INT64_MIN; } @@ -520,9 +550,11 @@ uint64_t HELPER(cgdb)(CPUS390XState *env, uint64_t v2, uint32_t m34) { int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); int64_t ret = float64_to_int64(v2, &env->fpu_status); + uint32_t cc = set_cc_conv_f64(v2, &env->fpu_status); s390_restore_bfp_rounding_mode(env, old_mode); handle_exceptions(env, xxc_from_m34(m34), GETPC()); + env->cc_op = cc; if (float64_is_any_nan(v2)) { return INT64_MIN; } @@ -535,9 +567,11 @@ uint64_t HELPER(cgxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m34) int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); float128 v2 = make_float128(h, l); int64_t ret = float128_to_int64(v2, &env->fpu_status); + uint32_t cc = set_cc_conv_f128(v2, &env->fpu_status); s390_restore_bfp_rounding_mode(env, old_mode); handle_exceptions(env, xxc_from_m34(m34), GETPC()); + env->cc_op = cc; if (float128_is_any_nan(v2)) { return INT64_MIN; } @@ -549,9 +583,11 @@ uint64_t HELPER(cfeb)(CPUS390XState *env, uint64_t v2, uint32_t m34) { int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); int32_t ret = float32_to_int32(v2, &env->fpu_status); + uint32_t cc = set_cc_conv_f32(v2, &env->fpu_status); s390_restore_bfp_rounding_mode(env, old_mode); handle_exceptions(env, xxc_from_m34(m34), GETPC()); + env->cc_op = cc; if (float32_is_any_nan(v2)) { return INT32_MIN; } @@ -563,9 +599,11 @@ uint64_t HELPER(cfdb)(CPUS390XState *env, uint64_t v2, uint32_t m34) { int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); int32_t ret = float64_to_int32(v2, &env->fpu_status); + uint32_t cc = set_cc_conv_f64(v2, &env->fpu_status); s390_restore_bfp_rounding_mode(env, old_mode); handle_exceptions(env, xxc_from_m34(m34), GETPC()); + env->cc_op = cc; if (float64_is_any_nan(v2)) { return INT32_MIN; } @@ -578,9 +616,11 @@ uint64_t HELPER(cfxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m34) int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); float128 v2 = make_float128(h, l); int32_t ret = float128_to_int32(v2, &env->fpu_status); + uint32_t cc = set_cc_conv_f128(v2, &env->fpu_status); s390_restore_bfp_rounding_mode(env, old_mode); handle_exceptions(env, xxc_from_m34(m34), GETPC()); + env->cc_op = cc; if (float128_is_any_nan(v2)) { return INT32_MIN; } @@ -592,8 +632,11 @@ uint64_t HELPER(clgeb)(CPUS390XState *env, uint64_t v2, uint32_t m34) { int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); uint64_t ret = float32_to_uint64(v2, &env->fpu_status); + uint32_t cc = set_cc_conv_f32(v2, &env->fpu_status); + s390_restore_bfp_rounding_mode(env, old_mode); handle_exceptions(env, xxc_from_m34(m34), GETPC()); + env->cc_op = cc; if (float32_is_any_nan(v2)) { return 0; } @@ -605,9 +648,11 @@ uint64_t HELPER(clgdb)(CPUS390XState *env, uint64_t v2, uint32_t m34) { int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); uint64_t ret = float64_to_uint64(v2, &env->fpu_status); + uint32_t cc = set_cc_conv_f64(v2, &env->fpu_status); s390_restore_bfp_rounding_mode(env, old_mode); handle_exceptions(env, xxc_from_m34(m34), GETPC()); + env->cc_op = cc; if (float64_is_any_nan(v2)) { return 0; } @@ -618,11 +663,14 @@ uint64_t HELPER(clgdb)(CPUS390XState *env, uint64_t v2, uint32_t m34) uint64_t HELPER(clgxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m34) { int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); - uint64_t ret = float128_to_uint64(make_float128(h, l), &env->fpu_status); + float128 v2 = make_float128(h, l); + uint64_t ret = float128_to_uint64(v2, &env->fpu_status); + uint32_t cc = set_cc_conv_f128(v2, &env->fpu_status); s390_restore_bfp_rounding_mode(env, old_mode); handle_exceptions(env, xxc_from_m34(m34), GETPC()); - if (float128_is_any_nan(make_float128(h, l))) { + env->cc_op = cc; + if (float128_is_any_nan(v2)) { return 0; } return ret; @@ -633,9 +681,11 @@ uint64_t HELPER(clfeb)(CPUS390XState *env, uint64_t v2, uint32_t m34) { int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); uint32_t ret = float32_to_uint32(v2, &env->fpu_status); + uint32_t cc = set_cc_conv_f32(v2, &env->fpu_status); s390_restore_bfp_rounding_mode(env, old_mode); handle_exceptions(env, xxc_from_m34(m34), GETPC()); + env->cc_op = cc; if (float32_is_any_nan(v2)) { return 0; } @@ -647,9 +697,11 @@ uint64_t HELPER(clfdb)(CPUS390XState *env, uint64_t v2, uint32_t m34) { int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); uint32_t ret = float64_to_uint32(v2, &env->fpu_status); + uint32_t cc = set_cc_conv_f64(v2, &env->fpu_status); s390_restore_bfp_rounding_mode(env, old_mode); handle_exceptions(env, xxc_from_m34(m34), GETPC()); + env->cc_op = cc; if (float64_is_any_nan(v2)) { return 0; } @@ -660,11 +712,14 @@ uint64_t HELPER(clfdb)(CPUS390XState *env, uint64_t v2, uint32_t m34) uint64_t HELPER(clfxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m34) { int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); - uint32_t ret = float128_to_uint32(make_float128(h, l), &env->fpu_status); + float128 v2 = make_float128(h, l); + uint32_t ret = float128_to_uint32(v2, &env->fpu_status); + uint32_t cc = set_cc_conv_f128(v2, &env->fpu_status); s390_restore_bfp_rounding_mode(env, old_mode); handle_exceptions(env, xxc_from_m34(m34), GETPC()); - if (float128_is_any_nan(make_float128(h, l))) { + env->cc_op = cc; + if (float128_is_any_nan(v2)) { return 0; } return ret; diff --git a/target/s390x/helper.h b/target/s390x/helper.h index ba045f559d..6215ca00bc 100644 --- a/target/s390x/helper.h +++ b/target/s390x/helper.h @@ -64,18 +64,18 @@ DEF_HELPER_FLAGS_5(cxb, TCG_CALL_NO_WG_SE, i32, env, i64, i64, i64, i64) DEF_HELPER_FLAGS_3(keb, TCG_CALL_NO_WG, i32, env, i64, i64) DEF_HELPER_FLAGS_3(kdb, TCG_CALL_NO_WG, i32, env, i64, i64) DEF_HELPER_FLAGS_5(kxb, TCG_CALL_NO_WG, i32, env, i64, i64, i64, i64) -DEF_HELPER_FLAGS_3(cgeb, TCG_CALL_NO_WG, i64, env, i64, i32) -DEF_HELPER_FLAGS_3(cgdb, TCG_CALL_NO_WG, i64, env, i64, i32) -DEF_HELPER_FLAGS_4(cgxb, TCG_CALL_NO_WG, i64, env, i64, i64, i32) -DEF_HELPER_FLAGS_3(cfeb, TCG_CALL_NO_WG, i64, env, i64, i32) -DEF_HELPER_FLAGS_3(cfdb, TCG_CALL_NO_WG, i64, env, i64, i32) -DEF_HELPER_FLAGS_4(cfxb, TCG_CALL_NO_WG, i64, env, i64, i64, i32) -DEF_HELPER_FLAGS_3(clgeb, TCG_CALL_NO_WG, i64, env, i64, i32) -DEF_HELPER_FLAGS_3(clgdb, TCG_CALL_NO_WG, i64, env, i64, i32) -DEF_HELPER_FLAGS_4(clgxb, TCG_CALL_NO_WG, i64, env, i64, i64, i32) -DEF_HELPER_FLAGS_3(clfeb, TCG_CALL_NO_WG, i64, env, i64, i32) -DEF_HELPER_FLAGS_3(clfdb, TCG_CALL_NO_WG, i64, env, i64, i32) -DEF_HELPER_FLAGS_4(clfxb, TCG_CALL_NO_WG, i64, env, i64, i64, i32) +DEF_HELPER_3(cgeb, i64, env, i64, i32) +DEF_HELPER_3(cgdb, i64, env, i64, i32) +DEF_HELPER_4(cgxb, i64, env, i64, i64, i32) +DEF_HELPER_3(cfeb, i64, env, i64, i32) +DEF_HELPER_3(cfdb, i64, env, i64, i32) +DEF_HELPER_4(cfxb, i64, env, i64, i64, i32) +DEF_HELPER_3(clgeb, i64, env, i64, i32) +DEF_HELPER_3(clgdb, i64, env, i64, i32) +DEF_HELPER_4(clgxb, i64, env, i64, i64, i32) +DEF_HELPER_3(clfeb, i64, env, i64, i32) +DEF_HELPER_3(clfdb, i64, env, i64, i32) +DEF_HELPER_4(clfxb, i64, env, i64, i64, i32) DEF_HELPER_FLAGS_3(fieb, TCG_CALL_NO_WG, i64, env, i64, i32) DEF_HELPER_FLAGS_3(fidb, TCG_CALL_NO_WG, i64, env, i64, i32) DEF_HELPER_FLAGS_4(fixb, TCG_CALL_NO_WG, i64, env, i64, i64, i32) diff --git a/target/s390x/translate.c b/target/s390x/translate.c index 03dab9f350..71ae3f333e 100644 --- a/target/s390x/translate.c +++ b/target/s390x/translate.c @@ -563,21 +563,6 @@ static void set_cc_nz_u64(DisasContext *s, TCGv_i64 val) gen_op_update1_cc_i64(s, CC_OP_NZ, val); } -static void gen_set_cc_nz_f32(DisasContext *s, TCGv_i64 val) -{ - gen_op_update1_cc_i64(s, CC_OP_NZ_F32, val); -} - -static void gen_set_cc_nz_f64(DisasContext *s, TCGv_i64 val) -{ - gen_op_update1_cc_i64(s, CC_OP_NZ_F64, val); -} - -static void gen_set_cc_nz_f128(DisasContext *s, TCGv_i64 vh, TCGv_i64 vl) -{ - gen_op_update2_cc_i64(s, CC_OP_NZ_F128, vh, vl); -} - /* CC value is in env->cc_op */ static void set_cc_static(DisasContext *s) { @@ -1836,7 +1821,7 @@ static DisasJumpType op_cfeb(DisasContext *s, DisasOps *o) } gen_helper_cfeb(o->out, cpu_env, o->in2, m34); tcg_temp_free_i32(m34); - gen_set_cc_nz_f32(s, o->in2); + set_cc_static(s); return DISAS_NEXT; } @@ -1849,7 +1834,7 @@ static DisasJumpType op_cfdb(DisasContext *s, DisasOps *o) } gen_helper_cfdb(o->out, cpu_env, o->in2, m34); tcg_temp_free_i32(m34); - gen_set_cc_nz_f64(s, o->in2); + set_cc_static(s); return DISAS_NEXT; } @@ -1862,7 +1847,7 @@ static DisasJumpType op_cfxb(DisasContext *s, DisasOps *o) } gen_helper_cfxb(o->out, cpu_env, o->in1, o->in2, m34); tcg_temp_free_i32(m34); - gen_set_cc_nz_f128(s, o->in1, o->in2); + set_cc_static(s); return DISAS_NEXT; } @@ -1875,7 +1860,7 @@ static DisasJumpType op_cgeb(DisasContext *s, DisasOps *o) } gen_helper_cgeb(o->out, cpu_env, o->in2, m34); tcg_temp_free_i32(m34); - gen_set_cc_nz_f32(s, o->in2); + set_cc_static(s); return DISAS_NEXT; } @@ -1888,7 +1873,7 @@ static DisasJumpType op_cgdb(DisasContext *s, DisasOps *o) } gen_helper_cgdb(o->out, cpu_env, o->in2, m34); tcg_temp_free_i32(m34); - gen_set_cc_nz_f64(s, o->in2); + set_cc_static(s); return DISAS_NEXT; } @@ -1901,7 +1886,7 @@ static DisasJumpType op_cgxb(DisasContext *s, DisasOps *o) } gen_helper_cgxb(o->out, cpu_env, o->in1, o->in2, m34); tcg_temp_free_i32(m34); - gen_set_cc_nz_f128(s, o->in1, o->in2); + set_cc_static(s); return DISAS_NEXT; } @@ -1914,7 +1899,7 @@ static DisasJumpType op_clfeb(DisasContext *s, DisasOps *o) } gen_helper_clfeb(o->out, cpu_env, o->in2, m34); tcg_temp_free_i32(m34); - gen_set_cc_nz_f32(s, o->in2); + set_cc_static(s); return DISAS_NEXT; } @@ -1927,7 +1912,7 @@ static DisasJumpType op_clfdb(DisasContext *s, DisasOps *o) } gen_helper_clfdb(o->out, cpu_env, o->in2, m34); tcg_temp_free_i32(m34); - gen_set_cc_nz_f64(s, o->in2); + set_cc_static(s); return DISAS_NEXT; } @@ -1940,7 +1925,7 @@ static DisasJumpType op_clfxb(DisasContext *s, DisasOps *o) } gen_helper_clfxb(o->out, cpu_env, o->in1, o->in2, m34); tcg_temp_free_i32(m34); - gen_set_cc_nz_f128(s, o->in1, o->in2); + set_cc_static(s); return DISAS_NEXT; } @@ -1953,7 +1938,7 @@ static DisasJumpType op_clgeb(DisasContext *s, DisasOps *o) } gen_helper_clgeb(o->out, cpu_env, o->in2, m34); tcg_temp_free_i32(m34); - gen_set_cc_nz_f32(s, o->in2); + set_cc_static(s); return DISAS_NEXT; } @@ -1966,7 +1951,7 @@ static DisasJumpType op_clgdb(DisasContext *s, DisasOps *o) } gen_helper_clgdb(o->out, cpu_env, o->in2, m34); tcg_temp_free_i32(m34); - gen_set_cc_nz_f64(s, o->in2); + set_cc_static(s); return DISAS_NEXT; } @@ -1979,7 +1964,7 @@ static DisasJumpType op_clgxb(DisasContext *s, DisasOps *o) } gen_helper_clgxb(o->out, cpu_env, o->in1, o->in2, m34); tcg_temp_free_i32(m34); - gen_set_cc_nz_f128(s, o->in1, o->in2); + set_cc_static(s); return DISAS_NEXT; } From af4ba0ec8f017c402c239f2888ef62f63770ba8b Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 5 Jul 2021 11:03:41 +0200 Subject: [PATCH 026/272] s390x/tcg: Fix m5 vs. m4 field for VECTOR MULTIPLY SUM LOGICAL The element size is located in m5, not in m4. As there is no m4, qemu currently crashes with an assertion, trying to lookup that field. Reproduced and tested via GO, which ends up using VMSL once the Vector enhancements facility is around for verifying certificates with elliptic curves. Reported-by: Jonathan Albrecht Resolves: https://gitlab.com/qemu-project/qemu/-/issues/449 Signed-off-by: David Hildenbrand Reviewed-by: Thomas Huth Fixes: 8c18fa5b3eba ("s390x/tcg: Implement VECTOR MULTIPLY SUM LOGICAL") Message-Id: <20210705090341.58289-1-david@redhat.com> Signed-off-by: Cornelia Huck --- target/s390x/translate_vx.c.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/s390x/translate_vx.c.inc b/target/s390x/translate_vx.c.inc index a9d51b1f4c..0afa46e463 100644 --- a/target/s390x/translate_vx.c.inc +++ b/target/s390x/translate_vx.c.inc @@ -1783,7 +1783,7 @@ static DisasJumpType op_vmsl(DisasContext *s, DisasOps *o) { TCGv_i64 l1, h1, l2, h2; - if (get_field(s, m4) != ES_64) { + if (get_field(s, m5) != ES_64) { gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } From 33f6a7d66f9e87368ae43fec4adf0d04d9ded0bd Mon Sep 17 00:00:00 2001 From: "Cho, Yu-Chen" Date: Wed, 7 Jul 2021 18:53:11 +0800 Subject: [PATCH 027/272] target/s390x: meson: add target_user_arch the lack of target_user_arch makes it hard to fully leverage the build system in order to separate user code from sysemu code. Provide it, so that we can avoid the proliferation of #ifdef in target code. Signed-off-by: Claudio Fontana Signed-off-by: Cho, Yu-Chen Acked-by: Cornelia Huck Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Message-Id: <20210707105324.23400-2-acho@suse.com> Signed-off-by: Cornelia Huck --- target/s390x/meson.build | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/s390x/meson.build b/target/s390x/meson.build index c42eadb7d2..1219f64112 100644 --- a/target/s390x/meson.build +++ b/target/s390x/meson.build @@ -58,5 +58,8 @@ if host_machine.cpu_family() == 's390x' and cc.has_link_argument('-Wl,--s390-pgs if_true: declare_dependency(link_args: ['-Wl,--s390-pgste'])) endif +s390x_user_ss = ss.source_set() + target_arch += {'s390x': s390x_ss} target_softmmu_arch += {'s390x': s390x_softmmu_ss} +target_user_arch += {'s390x': s390x_user_ss} From 85f1b67d4bcbdc6a12c3d2416df1a6d599414ee5 Mon Sep 17 00:00:00 2001 From: "Cho, Yu-Chen" Date: Wed, 7 Jul 2021 18:53:12 +0800 Subject: [PATCH 028/272] hw/s390x: rename tod-qemu.c to tod-tcg.c we stop short of renaming the actual qom object though, so type remains TYPE_QEMU_S390_TOD, ie "s390-tod-qemu". Signed-off-by: Claudio Fontana Signed-off-by: Cho, Yu-Chen Reviewed-by: David Hildenbrand Reviewed-by: Cornelia Huck Reviewed-by: Thomas Huth Message-Id: <20210707105324.23400-3-acho@suse.com> Signed-off-by: Cornelia Huck --- hw/s390x/meson.build | 2 +- hw/s390x/{tod-qemu.c => tod-tcg.c} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename hw/s390x/{tod-qemu.c => tod-tcg.c} (97%) diff --git a/hw/s390x/meson.build b/hw/s390x/meson.build index 327e9c93af..02e81a9467 100644 --- a/hw/s390x/meson.build +++ b/hw/s390x/meson.build @@ -16,7 +16,7 @@ s390x_ss.add(files( 'sclp.c', 'sclpcpu.c', 'sclpquiesce.c', - 'tod-qemu.c', + 'tod-tcg.c', 'tod.c', )) s390x_ss.add(when: 'CONFIG_KVM', if_true: files( diff --git a/hw/s390x/tod-qemu.c b/hw/s390x/tod-tcg.c similarity index 97% rename from hw/s390x/tod-qemu.c rename to hw/s390x/tod-tcg.c index e91b9590f5..aa44deb809 100644 --- a/hw/s390x/tod-qemu.c +++ b/hw/s390x/tod-tcg.c @@ -1,5 +1,5 @@ /* - * TOD (Time Of Day) clock - QEMU implementation + * TOD (Time Of Day) clock - TCG implementation * * Copyright 2018 Red Hat, Inc. * Author(s): David Hildenbrand From 4f91550a0962519e5c54330833486d7940417be0 Mon Sep 17 00:00:00 2001 From: "Cho, Yu-Chen" Date: Wed, 7 Jul 2021 18:53:13 +0800 Subject: [PATCH 029/272] hw/s390x: tod: make explicit checks for accelerators when initializing replace general "else" with specific checks for each possible accelerator. Handle qtest as a NOP, and error out for an unknown accelerator used in combination with tod. Signed-off-by: Claudio Fontana Signed-off-by: Cho, Yu-Chen Reviewed-by: David Hildenbrand Reviewed-by: Cornelia Huck Reviewed-by: Thomas Huth Message-Id: <20210707105324.23400-4-acho@suse.com> Signed-off-by: Cornelia Huck --- hw/s390x/tod.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/hw/s390x/tod.c b/hw/s390x/tod.c index 3c2979175e..fd5a36bf24 100644 --- a/hw/s390x/tod.c +++ b/hw/s390x/tod.c @@ -14,6 +14,8 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "sysemu/kvm.h" +#include "sysemu/tcg.h" +#include "sysemu/qtest.h" #include "migration/qemu-file-types.h" #include "migration/register.h" @@ -23,8 +25,13 @@ void s390_init_tod(void) if (kvm_enabled()) { obj = object_new(TYPE_KVM_S390_TOD); - } else { + } else if (tcg_enabled()) { obj = object_new(TYPE_QEMU_S390_TOD); + } else if (qtest_enabled()) { + return; + } else { + error_report("current accelerator not handled in s390_init_tod!"); + abort(); } object_property_add_child(qdev_get_machine(), TYPE_S390_TOD, obj); object_unref(obj); From 1be53ca48c6d58355a71f3272bf7fc7c22f1e441 Mon Sep 17 00:00:00 2001 From: "Cho, Yu-Chen" Date: Wed, 7 Jul 2021 18:53:14 +0800 Subject: [PATCH 030/272] hw/s390x: only build tod-tcg from the CONFIG_TCG build this will allow in later patches to remove unneeded stubs in target/s390x. Signed-off-by: Claudio Fontana Signed-off-by: Cho, Yu-Chen Reviewed-by: David Hildenbrand Reviewed-by: Cornelia Huck Reviewed-by: Thomas Huth Message-Id: <20210707105324.23400-5-acho@suse.com> Signed-off-by: Cornelia Huck --- hw/s390x/meson.build | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/s390x/meson.build b/hw/s390x/meson.build index 02e81a9467..28484256ec 100644 --- a/hw/s390x/meson.build +++ b/hw/s390x/meson.build @@ -16,7 +16,6 @@ s390x_ss.add(files( 'sclp.c', 'sclpcpu.c', 'sclpquiesce.c', - 'tod-tcg.c', 'tod.c', )) s390x_ss.add(when: 'CONFIG_KVM', if_true: files( @@ -25,6 +24,9 @@ s390x_ss.add(when: 'CONFIG_KVM', if_true: files( 's390-stattrib-kvm.c', 'pv.c', )) +s390x_ss.add(when: 'CONFIG_TCG', if_true: files( + 'tod-tcg.c', +)) s390x_ss.add(when: 'CONFIG_S390_CCW_VIRTIO', if_true: files('s390-virtio-ccw.c')) 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')) From dda740dec54f947e23b90e1e042281e9a65ec128 Mon Sep 17 00:00:00 2001 From: "Cho, Yu-Chen" Date: Wed, 7 Jul 2021 18:53:15 +0800 Subject: [PATCH 031/272] target/s390x: remove tcg-stub.c now that we protect all calls to the tcg-specific functions with if (tcg_enabled()), we do not need the TCG stub anymore. Signed-off-by: Claudio Fontana Signed-off-by: Cho, Yu-Chen Reviewed-by: David Hildenbrand Reviewed-by: Cornelia Huck Reviewed-by: Thomas Huth Message-Id: <20210707105324.23400-6-acho@suse.com> Signed-off-by: Cornelia Huck --- target/s390x/meson.build | 2 +- target/s390x/tcg-stub.c | 30 ------------------------------ 2 files changed, 1 insertion(+), 31 deletions(-) delete mode 100644 target/s390x/tcg-stub.c diff --git a/target/s390x/meson.build b/target/s390x/meson.build index 1219f64112..a5e1ded93f 100644 --- a/target/s390x/meson.build +++ b/target/s390x/meson.build @@ -21,7 +21,7 @@ s390x_ss.add(when: 'CONFIG_TCG', if_true: files( 'vec_helper.c', 'vec_int_helper.c', 'vec_string_helper.c', -), if_false: files('tcg-stub.c')) +)) s390x_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c'), if_false: files('kvm-stub.c')) diff --git a/target/s390x/tcg-stub.c b/target/s390x/tcg-stub.c deleted file mode 100644 index d22c898802..0000000000 --- a/target/s390x/tcg-stub.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * QEMU TCG support -- s390x specific function stubs. - * - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * David Hildenbrand - * - * 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-common.h" -#include "cpu.h" -#include "tcg_s390x.h" - -void tcg_s390_tod_updated(CPUState *cs, run_on_cpu_data opaque) -{ -} -void QEMU_NORETURN tcg_s390_program_interrupt(CPUS390XState *env, - uint32_t code, uintptr_t ra) -{ - g_assert_not_reached(); -} -void QEMU_NORETURN tcg_s390_data_exception(CPUS390XState *env, uint32_t dxc, - uintptr_t ra) -{ - g_assert_not_reached(); -} From b6b4722307f31491ee553c674ded2a8bba6173e1 Mon Sep 17 00:00:00 2001 From: "Cho, Yu-Chen" Date: Wed, 7 Jul 2021 18:53:16 +0800 Subject: [PATCH 032/272] target/s390x: rename internal.h to s390x-internal.h The internal.h file is renamed to s390x-internal.h, because of the risk of collision with other files with the same name. Signed-off-by: Claudio Fontana Signed-off-by: Cho, Yu-Chen Acked-by: David Hildenbrand Acked-by: Cornelia Huck Reviewed-by: Thomas Huth Message-Id: <20210707105324.23400-7-acho@suse.com> Signed-off-by: Cornelia Huck --- target/s390x/arch_dump.c | 2 +- target/s390x/cc_helper.c | 2 +- target/s390x/cpu.c | 2 +- target/s390x/cpu_models.c | 2 +- target/s390x/crypto_helper.c | 2 +- target/s390x/diag.c | 2 +- target/s390x/excp_helper.c | 2 +- target/s390x/fpu_helper.c | 2 +- target/s390x/gdbstub.c | 2 +- target/s390x/helper.c | 2 +- target/s390x/int_helper.c | 2 +- target/s390x/interrupt.c | 2 +- target/s390x/ioinst.c | 2 +- target/s390x/kvm.c | 2 +- target/s390x/machine.c | 2 +- target/s390x/mem_helper.c | 2 +- target/s390x/misc_helper.c | 2 +- target/s390x/mmu_helper.c | 2 +- target/s390x/{internal.h => s390x-internal.h} | 0 target/s390x/sigp.c | 2 +- target/s390x/translate.c | 2 +- target/s390x/vec_fpu_helper.c | 2 +- target/s390x/vec_helper.c | 2 +- target/s390x/vec_string_helper.c | 2 +- 24 files changed, 23 insertions(+), 23 deletions(-) rename target/s390x/{internal.h => s390x-internal.h} (100%) diff --git a/target/s390x/arch_dump.c b/target/s390x/arch_dump.c index cc1330876b..08daf93ae1 100644 --- a/target/s390x/arch_dump.c +++ b/target/s390x/arch_dump.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "internal.h" +#include "s390x-internal.h" #include "elf.h" #include "sysemu/dump.h" diff --git a/target/s390x/cc_helper.c b/target/s390x/cc_helper.c index e7a74d66dd..c2c96c3a3c 100644 --- a/target/s390x/cc_helper.c +++ b/target/s390x/cc_helper.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "internal.h" +#include "s390x-internal.h" #include "tcg_s390x.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 890f382a36..1795042e97 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -23,7 +23,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "cpu.h" -#include "internal.h" +#include "s390x-internal.h" #include "kvm_s390x.h" #include "sysemu/kvm.h" #include "sysemu/reset.h" diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 9699823b20..442f06e140 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "internal.h" +#include "s390x-internal.h" #include "kvm_s390x.h" #include "sysemu/kvm.h" #include "sysemu/tcg.h" diff --git a/target/s390x/crypto_helper.c b/target/s390x/crypto_helper.c index ff3fbc3950..138d9e7ad9 100644 --- a/target/s390x/crypto_helper.c +++ b/target/s390x/crypto_helper.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" -#include "internal.h" +#include "s390x-internal.h" #include "tcg_s390x.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" diff --git a/target/s390x/diag.c b/target/s390x/diag.c index d620cd4bd4..c17a2498a7 100644 --- a/target/s390x/diag.c +++ b/target/s390x/diag.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "internal.h" +#include "s390x-internal.h" #include "hw/watchdog/wdt_diag288.h" #include "sysemu/cpus.h" #include "hw/s390x/ipl.h" diff --git a/target/s390x/excp_helper.c b/target/s390x/excp_helper.c index 9c361428c8..a61917d04f 100644 --- a/target/s390x/excp_helper.c +++ b/target/s390x/excp_helper.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "internal.h" +#include "s390x-internal.h" #include "exec/helper-proto.h" #include "qemu/timer.h" #include "exec/exec-all.h" diff --git a/target/s390x/fpu_helper.c b/target/s390x/fpu_helper.c index a42741eec6..4067205405 100644 --- a/target/s390x/fpu_helper.c +++ b/target/s390x/fpu_helper.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "internal.h" +#include "s390x-internal.h" #include "tcg_s390x.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" diff --git a/target/s390x/gdbstub.c b/target/s390x/gdbstub.c index 5b4e38a13b..a5d69d0e0b 100644 --- a/target/s390x/gdbstub.c +++ b/target/s390x/gdbstub.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "internal.h" +#include "s390x-internal.h" #include "exec/exec-all.h" #include "exec/gdbstub.h" #include "qemu/bitops.h" diff --git a/target/s390x/helper.c b/target/s390x/helper.c index 1445b74451..8015c4e3d1 100644 --- a/target/s390x/helper.c +++ b/target/s390x/helper.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "internal.h" +#include "s390x-internal.h" #include "exec/gdbstub.h" #include "qemu/timer.h" #include "qemu/qemu-print.h" diff --git a/target/s390x/int_helper.c b/target/s390x/int_helper.c index 658507dd6d..954542388a 100644 --- a/target/s390x/int_helper.c +++ b/target/s390x/int_helper.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "internal.h" +#include "s390x-internal.h" #include "tcg_s390x.h" #include "exec/exec-all.h" #include "qemu/host-utils.h" diff --git a/target/s390x/interrupt.c b/target/s390x/interrupt.c index 9b4d08f2be..3fde18ba46 100644 --- a/target/s390x/interrupt.c +++ b/target/s390x/interrupt.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "kvm_s390x.h" -#include "internal.h" +#include "s390x-internal.h" #include "exec/exec-all.h" #include "sysemu/kvm.h" #include "sysemu/tcg.h" diff --git a/target/s390x/ioinst.c b/target/s390x/ioinst.c index 1ee11522e1..4eb0a7a9f8 100644 --- a/target/s390x/ioinst.c +++ b/target/s390x/ioinst.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "internal.h" +#include "s390x-internal.h" #include "hw/s390x/ioinst.h" #include "trace.h" #include "hw/s390x/s390-pci-bus.h" diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c index 2388924587..5b1fdb55c4 100644 --- a/target/s390x/kvm.c +++ b/target/s390x/kvm.c @@ -26,7 +26,7 @@ #include "qemu-common.h" #include "cpu.h" -#include "internal.h" +#include "s390x-internal.h" #include "kvm_s390x.h" #include "sysemu/kvm_int.h" #include "qemu/cutils.h" diff --git a/target/s390x/machine.c b/target/s390x/machine.c index 5b4e82f1ab..4f11f6ac6e 100644 --- a/target/s390x/machine.c +++ b/target/s390x/machine.c @@ -16,7 +16,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "internal.h" +#include "s390x-internal.h" #include "kvm_s390x.h" #include "migration/vmstate.h" #include "tcg_s390x.h" diff --git a/target/s390x/mem_helper.c b/target/s390x/mem_helper.c index f6a7d29273..9bae13ecf0 100644 --- a/target/s390x/mem_helper.c +++ b/target/s390x/mem_helper.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "internal.h" +#include "s390x-internal.h" #include "tcg_s390x.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" diff --git a/target/s390x/misc_helper.c b/target/s390x/misc_helper.c index 7ea90d414a..33e6999e15 100644 --- a/target/s390x/misc_helper.c +++ b/target/s390x/misc_helper.c @@ -22,7 +22,7 @@ #include "qemu/cutils.h" #include "qemu/main-loop.h" #include "cpu.h" -#include "internal.h" +#include "s390x-internal.h" #include "exec/memory.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c index d492b23a17..52fdd86c63 100644 --- a/target/s390x/mmu_helper.c +++ b/target/s390x/mmu_helper.c @@ -19,7 +19,7 @@ #include "qemu/error-report.h" #include "exec/address-spaces.h" #include "cpu.h" -#include "internal.h" +#include "s390x-internal.h" #include "kvm_s390x.h" #include "sysemu/kvm.h" #include "sysemu/tcg.h" diff --git a/target/s390x/internal.h b/target/s390x/s390x-internal.h similarity index 100% rename from target/s390x/internal.h rename to target/s390x/s390x-internal.h diff --git a/target/s390x/sigp.c b/target/s390x/sigp.c index c2d5cdf061..d57427ced8 100644 --- a/target/s390x/sigp.c +++ b/target/s390x/sigp.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "internal.h" +#include "s390x-internal.h" #include "sysemu/hw_accel.h" #include "sysemu/runstate.h" #include "exec/address-spaces.h" diff --git a/target/s390x/translate.c b/target/s390x/translate.c index 71ae3f333e..e14eab5794 100644 --- a/target/s390x/translate.c +++ b/target/s390x/translate.c @@ -30,7 +30,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "internal.h" +#include "s390x-internal.h" #include "disas/disas.h" #include "exec/exec-all.h" #include "tcg/tcg-op.h" diff --git a/target/s390x/vec_fpu_helper.c b/target/s390x/vec_fpu_helper.c index 8e2b274547..1a77993471 100644 --- a/target/s390x/vec_fpu_helper.c +++ b/target/s390x/vec_fpu_helper.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" #include "qemu-common.h" #include "cpu.h" -#include "internal.h" +#include "s390x-internal.h" #include "vec.h" #include "tcg_s390x.h" #include "tcg/tcg-gvec-desc.h" diff --git a/target/s390x/vec_helper.c b/target/s390x/vec_helper.c index 599bab06bd..ededf13cf0 100644 --- a/target/s390x/vec_helper.c +++ b/target/s390x/vec_helper.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" #include "cpu.h" -#include "internal.h" +#include "s390x-internal.h" #include "vec.h" #include "tcg/tcg.h" #include "tcg/tcg-gvec-desc.h" diff --git a/target/s390x/vec_string_helper.c b/target/s390x/vec_string_helper.c index c516c0ceeb..ac315eb095 100644 --- a/target/s390x/vec_string_helper.c +++ b/target/s390x/vec_string_helper.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" #include "qemu-common.h" #include "cpu.h" -#include "internal.h" +#include "s390x-internal.h" #include "vec.h" #include "tcg/tcg.h" #include "tcg/tcg-gvec-desc.h" From c9274b6bf0571ecbaaed3e9c3b229e17607a0ea2 Mon Sep 17 00:00:00 2001 From: "Cho, Yu-Chen" Date: Wed, 7 Jul 2021 18:53:17 +0800 Subject: [PATCH 033/272] target/s390x: start moving TCG-only code to tcg/ move everything related to translate, as well as HELPER code in tcg/ mmu_helper.c stays put for now, as it contains both TCG and KVM code. After the reshuffling, update MAINTAINERS accordingly. Make use of the new directory: target/s390x/tcg/ Signed-off-by: Claudio Fontana Signed-off-by: Cho, Yu-Chen Acked-by: David Hildenbrand Acked-by: Cornelia Huck Reviewed-by: Thomas Huth Message-Id: <20210707105324.23400-8-acho@suse.com> Signed-off-by: Cornelia Huck --- MAINTAINERS | 1 + hw/s390x/tod-tcg.c | 2 +- include/hw/s390x/tod.h | 2 +- target/s390x/interrupt.c | 2 +- target/s390x/machine.c | 2 +- target/s390x/meson.build | 17 ++--------------- target/s390x/{ => tcg}/cc_helper.c | 0 target/s390x/{ => tcg}/crypto_helper.c | 0 target/s390x/{ => tcg}/excp_helper.c | 0 target/s390x/{ => tcg}/fpu_helper.c | 0 target/s390x/{ => tcg}/insn-data.def | 0 target/s390x/{ => tcg}/insn-format.def | 0 target/s390x/{ => tcg}/int_helper.c | 0 target/s390x/{ => tcg}/mem_helper.c | 0 target/s390x/tcg/meson.build | 14 ++++++++++++++ target/s390x/{ => tcg}/misc_helper.c | 0 target/s390x/{ => tcg}/s390-tod.h | 0 target/s390x/{ => tcg}/tcg_s390x.h | 0 target/s390x/{ => tcg}/translate.c | 0 target/s390x/{ => tcg}/translate_vx.c.inc | 0 target/s390x/{ => tcg}/vec.h | 0 target/s390x/{ => tcg}/vec_fpu_helper.c | 0 target/s390x/{ => tcg}/vec_helper.c | 0 target/s390x/{ => tcg}/vec_int_helper.c | 0 target/s390x/{ => tcg}/vec_string_helper.c | 0 25 files changed, 21 insertions(+), 19 deletions(-) rename target/s390x/{ => tcg}/cc_helper.c (100%) rename target/s390x/{ => tcg}/crypto_helper.c (100%) rename target/s390x/{ => tcg}/excp_helper.c (100%) rename target/s390x/{ => tcg}/fpu_helper.c (100%) rename target/s390x/{ => tcg}/insn-data.def (100%) rename target/s390x/{ => tcg}/insn-format.def (100%) rename target/s390x/{ => tcg}/int_helper.c (100%) rename target/s390x/{ => tcg}/mem_helper.c (100%) create mode 100644 target/s390x/tcg/meson.build rename target/s390x/{ => tcg}/misc_helper.c (100%) rename target/s390x/{ => tcg}/s390-tod.h (100%) rename target/s390x/{ => tcg}/tcg_s390x.h (100%) rename target/s390x/{ => tcg}/translate.c (100%) rename target/s390x/{ => tcg}/translate_vx.c.inc (100%) rename target/s390x/{ => tcg}/vec.h (100%) rename target/s390x/{ => tcg}/vec_fpu_helper.c (100%) rename target/s390x/{ => tcg}/vec_helper.c (100%) rename target/s390x/{ => tcg}/vec_int_helper.c (100%) rename target/s390x/{ => tcg}/vec_string_helper.c (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 684142e12e..8ec845f4e0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -297,6 +297,7 @@ M: Richard Henderson M: David Hildenbrand S: Maintained F: target/s390x/ +F: target/s390x/tcg F: hw/s390x/ F: disas/s390.c F: tests/tcg/s390x/ diff --git a/hw/s390x/tod-tcg.c b/hw/s390x/tod-tcg.c index aa44deb809..9bb94ff72b 100644 --- a/hw/s390x/tod-tcg.c +++ b/hw/s390x/tod-tcg.c @@ -16,7 +16,7 @@ #include "qemu/cutils.h" #include "qemu/module.h" #include "cpu.h" -#include "tcg_s390x.h" +#include "tcg/tcg_s390x.h" static void qemu_s390_tod_get(const S390TODState *td, S390TOD *tod, Error **errp) diff --git a/include/hw/s390x/tod.h b/include/hw/s390x/tod.h index ff3195a4bf..0935e85089 100644 --- a/include/hw/s390x/tod.h +++ b/include/hw/s390x/tod.h @@ -12,7 +12,7 @@ #define HW_S390_TOD_H #include "hw/qdev-core.h" -#include "target/s390x/s390-tod.h" +#include "tcg/s390-tod.h" #include "qom/object.h" typedef struct S390TOD { diff --git a/target/s390x/interrupt.c b/target/s390x/interrupt.c index 3fde18ba46..734f0c62de 100644 --- a/target/s390x/interrupt.c +++ b/target/s390x/interrupt.c @@ -15,7 +15,7 @@ #include "sysemu/kvm.h" #include "sysemu/tcg.h" #include "hw/s390x/ioinst.h" -#include "tcg_s390x.h" +#include "tcg/tcg_s390x.h" #if !defined(CONFIG_USER_ONLY) #include "hw/s390x/s390_flic.h" #endif diff --git a/target/s390x/machine.c b/target/s390x/machine.c index 4f11f6ac6e..81a8a7ff99 100644 --- a/target/s390x/machine.c +++ b/target/s390x/machine.c @@ -19,7 +19,7 @@ #include "s390x-internal.h" #include "kvm_s390x.h" #include "migration/vmstate.h" -#include "tcg_s390x.h" +#include "tcg/tcg_s390x.h" #include "sysemu/kvm.h" #include "sysemu/tcg.h" diff --git a/target/s390x/meson.build b/target/s390x/meson.build index a5e1ded93f..60d7f1b908 100644 --- a/target/s390x/meson.build +++ b/target/s390x/meson.build @@ -8,21 +8,6 @@ s390x_ss.add(files( 'interrupt.c', )) -s390x_ss.add(when: 'CONFIG_TCG', if_true: files( - 'cc_helper.c', - 'crypto_helper.c', - 'excp_helper.c', - 'fpu_helper.c', - 'int_helper.c', - 'mem_helper.c', - 'misc_helper.c', - 'translate.c', - 'vec_fpu_helper.c', - 'vec_helper.c', - 'vec_int_helper.c', - 'vec_string_helper.c', -)) - s390x_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c'), if_false: files('kvm-stub.c')) gen_features = executable('gen-features', 'gen-features.c', native: true, @@ -60,6 +45,8 @@ endif s390x_user_ss = ss.source_set() +subdir('tcg') + target_arch += {'s390x': s390x_ss} target_softmmu_arch += {'s390x': s390x_softmmu_ss} target_user_arch += {'s390x': s390x_user_ss} diff --git a/target/s390x/cc_helper.c b/target/s390x/tcg/cc_helper.c similarity index 100% rename from target/s390x/cc_helper.c rename to target/s390x/tcg/cc_helper.c diff --git a/target/s390x/crypto_helper.c b/target/s390x/tcg/crypto_helper.c similarity index 100% rename from target/s390x/crypto_helper.c rename to target/s390x/tcg/crypto_helper.c diff --git a/target/s390x/excp_helper.c b/target/s390x/tcg/excp_helper.c similarity index 100% rename from target/s390x/excp_helper.c rename to target/s390x/tcg/excp_helper.c diff --git a/target/s390x/fpu_helper.c b/target/s390x/tcg/fpu_helper.c similarity index 100% rename from target/s390x/fpu_helper.c rename to target/s390x/tcg/fpu_helper.c diff --git a/target/s390x/insn-data.def b/target/s390x/tcg/insn-data.def similarity index 100% rename from target/s390x/insn-data.def rename to target/s390x/tcg/insn-data.def diff --git a/target/s390x/insn-format.def b/target/s390x/tcg/insn-format.def similarity index 100% rename from target/s390x/insn-format.def rename to target/s390x/tcg/insn-format.def diff --git a/target/s390x/int_helper.c b/target/s390x/tcg/int_helper.c similarity index 100% rename from target/s390x/int_helper.c rename to target/s390x/tcg/int_helper.c diff --git a/target/s390x/mem_helper.c b/target/s390x/tcg/mem_helper.c similarity index 100% rename from target/s390x/mem_helper.c rename to target/s390x/tcg/mem_helper.c diff --git a/target/s390x/tcg/meson.build b/target/s390x/tcg/meson.build new file mode 100644 index 0000000000..ee4e8fec77 --- /dev/null +++ b/target/s390x/tcg/meson.build @@ -0,0 +1,14 @@ +s390x_ss.add(when: 'CONFIG_TCG', if_true: files( + 'cc_helper.c', + 'crypto_helper.c', + 'excp_helper.c', + 'fpu_helper.c', + 'int_helper.c', + 'mem_helper.c', + 'misc_helper.c', + 'translate.c', + 'vec_fpu_helper.c', + 'vec_helper.c', + 'vec_int_helper.c', + 'vec_string_helper.c', +)) diff --git a/target/s390x/misc_helper.c b/target/s390x/tcg/misc_helper.c similarity index 100% rename from target/s390x/misc_helper.c rename to target/s390x/tcg/misc_helper.c diff --git a/target/s390x/s390-tod.h b/target/s390x/tcg/s390-tod.h similarity index 100% rename from target/s390x/s390-tod.h rename to target/s390x/tcg/s390-tod.h diff --git a/target/s390x/tcg_s390x.h b/target/s390x/tcg/tcg_s390x.h similarity index 100% rename from target/s390x/tcg_s390x.h rename to target/s390x/tcg/tcg_s390x.h diff --git a/target/s390x/translate.c b/target/s390x/tcg/translate.c similarity index 100% rename from target/s390x/translate.c rename to target/s390x/tcg/translate.c diff --git a/target/s390x/translate_vx.c.inc b/target/s390x/tcg/translate_vx.c.inc similarity index 100% rename from target/s390x/translate_vx.c.inc rename to target/s390x/tcg/translate_vx.c.inc diff --git a/target/s390x/vec.h b/target/s390x/tcg/vec.h similarity index 100% rename from target/s390x/vec.h rename to target/s390x/tcg/vec.h diff --git a/target/s390x/vec_fpu_helper.c b/target/s390x/tcg/vec_fpu_helper.c similarity index 100% rename from target/s390x/vec_fpu_helper.c rename to target/s390x/tcg/vec_fpu_helper.c diff --git a/target/s390x/vec_helper.c b/target/s390x/tcg/vec_helper.c similarity index 100% rename from target/s390x/vec_helper.c rename to target/s390x/tcg/vec_helper.c diff --git a/target/s390x/vec_int_helper.c b/target/s390x/tcg/vec_int_helper.c similarity index 100% rename from target/s390x/vec_int_helper.c rename to target/s390x/tcg/vec_int_helper.c diff --git a/target/s390x/vec_string_helper.c b/target/s390x/tcg/vec_string_helper.c similarity index 100% rename from target/s390x/vec_string_helper.c rename to target/s390x/tcg/vec_string_helper.c From 72ffb6310baab625958fa39155d98cce6a3bb235 Mon Sep 17 00:00:00 2001 From: "Cho, Yu-Chen" Date: Wed, 7 Jul 2021 18:53:18 +0800 Subject: [PATCH 034/272] target/s390x: move sysemu-only code out to cpu-sysemu.c move sysemu-only code out to cpu-sysemu.c Signed-off-by: Claudio Fontana Signed-off-by: Cho, Yu-Chen Acked-by: Cornelia Huck Reviewed-by: Thomas Huth Message-Id: <20210707105324.23400-9-acho@suse.com> Signed-off-by: Cornelia Huck --- target/s390x/cpu-sysemu.c | 309 ++++++++++++++++++++++++++++++++++ target/s390x/cpu.c | 287 ++----------------------------- target/s390x/meson.build | 1 + target/s390x/s390x-internal.h | 6 + target/s390x/trace-events | 2 +- 5 files changed, 327 insertions(+), 278 deletions(-) create mode 100644 target/s390x/cpu-sysemu.c diff --git a/target/s390x/cpu-sysemu.c b/target/s390x/cpu-sysemu.c new file mode 100644 index 0000000000..16e5301084 --- /dev/null +++ b/target/s390x/cpu-sysemu.c @@ -0,0 +1,309 @@ +/* + * QEMU S/390 CPU - System Emulation-only code + * + * Copyright (c) 2009 Ulrich Hecht + * Copyright (c) 2011 Alexander Graf + * Copyright (c) 2012 SUSE LINUX Products GmbH + * Copyright (c) 2012 IBM Corp. + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "cpu.h" +#include "s390x-internal.h" +#include "kvm_s390x.h" +#include "sysemu/kvm.h" +#include "sysemu/reset.h" +#include "qemu/timer.h" +#include "trace.h" +#include "qapi/qapi-visit-run-state.h" +#include "sysemu/hw_accel.h" + +#include "hw/s390x/pv.h" +#include "hw/boards.h" +#include "sysemu/arch_init.h" +#include "sysemu/sysemu.h" +#include "sysemu/tcg.h" +#include "hw/core/sysemu-cpu-ops.h" + +/* S390CPUClass::load_normal() */ +static void s390_cpu_load_normal(CPUState *s) +{ + S390CPU *cpu = S390_CPU(s); + uint64_t spsw; + + if (!s390_is_pv()) { + spsw = ldq_phys(s->as, 0); + cpu->env.psw.mask = spsw & PSW_MASK_SHORT_CTRL; + /* + * Invert short psw indication, so SIE will report a specification + * exception if it was not set. + */ + cpu->env.psw.mask ^= PSW_MASK_SHORTPSW; + cpu->env.psw.addr = spsw & PSW_MASK_SHORT_ADDR; + } else { + /* + * Firmware requires us to set the load state before we set + * the cpu to operating on protected guests. + */ + s390_cpu_set_state(S390_CPU_STATE_LOAD, cpu); + } + s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu); +} + +void s390_cpu_machine_reset_cb(void *opaque) +{ + S390CPU *cpu = opaque; + + run_on_cpu(CPU(cpu), s390_do_cpu_full_reset, RUN_ON_CPU_NULL); +} + +static GuestPanicInformation *s390_cpu_get_crash_info(CPUState *cs) +{ + GuestPanicInformation *panic_info; + S390CPU *cpu = S390_CPU(cs); + + cpu_synchronize_state(cs); + panic_info = g_malloc0(sizeof(GuestPanicInformation)); + + panic_info->type = GUEST_PANIC_INFORMATION_TYPE_S390; + panic_info->u.s390.core = cpu->env.core_id; + panic_info->u.s390.psw_mask = cpu->env.psw.mask; + panic_info->u.s390.psw_addr = cpu->env.psw.addr; + panic_info->u.s390.reason = cpu->env.crash_reason; + + return panic_info; +} + +static void s390_cpu_get_crash_info_qom(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + CPUState *cs = CPU(obj); + GuestPanicInformation *panic_info; + + if (!cs->crash_occurred) { + error_setg(errp, "No crash occurred"); + return; + } + + panic_info = s390_cpu_get_crash_info(cs); + + visit_type_GuestPanicInformation(v, "crash-information", &panic_info, + errp); + qapi_free_GuestPanicInformation(panic_info); +} + +void s390_cpu_init_sysemu(Object *obj) +{ + CPUState *cs = CPU(obj); + S390CPU *cpu = S390_CPU(obj); + + cs->start_powered_off = true; + object_property_add(obj, "crash-information", "GuestPanicInformation", + s390_cpu_get_crash_info_qom, NULL, NULL, NULL); + cpu->env.tod_timer = + timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_tod_timer, cpu); + cpu->env.cpu_timer = + timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_cpu_timer, cpu); + s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu); +} + +bool s390_cpu_realize_sysemu(DeviceState *dev, Error **errp) +{ + S390CPU *cpu = S390_CPU(dev); + MachineState *ms = MACHINE(qdev_get_machine()); + unsigned int max_cpus = ms->smp.max_cpus; + + if (cpu->env.core_id >= max_cpus) { + error_setg(errp, "Unable to add CPU with core-id: %" PRIu32 + ", maximum core-id: %d", cpu->env.core_id, + max_cpus - 1); + return false; + } + + if (cpu_exists(cpu->env.core_id)) { + error_setg(errp, "Unable to add CPU with core-id: %" PRIu32 + ", it already exists", cpu->env.core_id); + return false; + } + + /* sync cs->cpu_index and env->core_id. The latter is needed for TCG. */ + CPU(cpu)->cpu_index = cpu->env.core_id; + return true; +} + +void s390_cpu_finalize(Object *obj) +{ + S390CPU *cpu = S390_CPU(obj); + + timer_free(cpu->env.tod_timer); + timer_free(cpu->env.cpu_timer); + + qemu_unregister_reset(s390_cpu_machine_reset_cb, cpu); + g_free(cpu->irqstate); +} + +static const struct SysemuCPUOps s390_sysemu_ops = { + .get_phys_page_debug = s390_cpu_get_phys_page_debug, + .get_crash_info = s390_cpu_get_crash_info, + .write_elf64_note = s390_cpu_write_elf64_note, + .legacy_vmsd = &vmstate_s390_cpu, +}; + +void s390_cpu_class_init_sysemu(CPUClass *cc) +{ + S390CPUClass *scc = S390_CPU_CLASS(cc); + + scc->load_normal = s390_cpu_load_normal; + cc->sysemu_ops = &s390_sysemu_ops; +} + +static bool disabled_wait(CPUState *cpu) +{ + return cpu->halted && !(S390_CPU(cpu)->env.psw.mask & + (PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK)); +} + +static unsigned s390_count_running_cpus(void) +{ + CPUState *cpu; + int nr_running = 0; + + CPU_FOREACH(cpu) { + uint8_t state = S390_CPU(cpu)->env.cpu_state; + if (state == S390_CPU_STATE_OPERATING || + state == S390_CPU_STATE_LOAD) { + if (!disabled_wait(cpu)) { + nr_running++; + } + } + } + + return nr_running; +} + +unsigned int s390_cpu_halt(S390CPU *cpu) +{ + CPUState *cs = CPU(cpu); + trace_cpu_halt(cs->cpu_index); + + if (!cs->halted) { + cs->halted = 1; + cs->exception_index = EXCP_HLT; + } + + return s390_count_running_cpus(); +} + +void s390_cpu_unhalt(S390CPU *cpu) +{ + CPUState *cs = CPU(cpu); + trace_cpu_unhalt(cs->cpu_index); + + if (cs->halted) { + cs->halted = 0; + cs->exception_index = -1; + } +} + +unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu) + { + trace_cpu_set_state(CPU(cpu)->cpu_index, cpu_state); + + switch (cpu_state) { + case S390_CPU_STATE_STOPPED: + case S390_CPU_STATE_CHECK_STOP: + /* halt the cpu for common infrastructure */ + s390_cpu_halt(cpu); + break; + case S390_CPU_STATE_OPERATING: + case S390_CPU_STATE_LOAD: + /* + * Starting a CPU with a PSW WAIT bit set: + * KVM: handles this internally and triggers another WAIT exit. + * TCG: will actually try to continue to run. Don't unhalt, will + * be done when the CPU actually has work (an interrupt). + */ + if (!tcg_enabled() || !(cpu->env.psw.mask & PSW_MASK_WAIT)) { + s390_cpu_unhalt(cpu); + } + break; + default: + error_report("Requested CPU state is not a valid S390 CPU state: %u", + cpu_state); + exit(1); + } + if (kvm_enabled() && cpu->env.cpu_state != cpu_state) { + kvm_s390_set_cpu_state(cpu, cpu_state); + } + cpu->env.cpu_state = cpu_state; + + return s390_count_running_cpus(); +} + +int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit) +{ + if (kvm_enabled()) { + return kvm_s390_set_mem_limit(new_limit, hw_limit); + } + return 0; +} + +void s390_set_max_pagesize(uint64_t pagesize, Error **errp) +{ + if (kvm_enabled()) { + kvm_s390_set_max_pagesize(pagesize, errp); + } +} + +void s390_cmma_reset(void) +{ + if (kvm_enabled()) { + kvm_s390_cmma_reset(); + } +} + +int s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch_id, + int vq, bool assign) +{ + if (kvm_enabled()) { + return kvm_s390_assign_subch_ioeventfd(notifier, sch_id, vq, assign); + } else { + return 0; + } +} + +void s390_crypto_reset(void) +{ + if (kvm_enabled()) { + kvm_s390_crypto_reset(); + } +} + +void s390_enable_css_support(S390CPU *cpu) +{ + if (kvm_enabled()) { + kvm_s390_enable_css_support(cpu); + } +} + +void s390_do_cpu_set_diag318(CPUState *cs, run_on_cpu_data arg) +{ + if (kvm_enabled()) { + kvm_s390_set_diag318(cs, arg.host_ulong); + } +} diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 1795042e97..2b2b70e1c6 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -27,21 +27,11 @@ #include "kvm_s390x.h" #include "sysemu/kvm.h" #include "sysemu/reset.h" -#include "qemu/timer.h" -#include "qemu/error-report.h" #include "qemu/module.h" #include "trace.h" -#include "qapi/visitor.h" #include "qapi/qapi-types-machine.h" -#include "qapi/qapi-visit-run-state.h" #include "sysemu/hw_accel.h" #include "hw/qdev-properties.h" -#ifndef CONFIG_USER_ONLY -#include "hw/s390x/pv.h" -#include "hw/boards.h" -#include "sysemu/arch_init.h" -#include "sysemu/tcg.h" -#endif #include "fpu/softfloat-helpers.h" #include "disas/capstone.h" @@ -72,33 +62,6 @@ static bool s390_cpu_has_work(CPUState *cs) return s390_cpu_has_int(cpu); } -#if !defined(CONFIG_USER_ONLY) -/* S390CPUClass::load_normal() */ -static void s390_cpu_load_normal(CPUState *s) -{ - S390CPU *cpu = S390_CPU(s); - uint64_t spsw; - - if (!s390_is_pv()) { - spsw = ldq_phys(s->as, 0); - cpu->env.psw.mask = spsw & PSW_MASK_SHORT_CTRL; - /* - * Invert short psw indication, so SIE will report a specification - * exception if it was not set. - */ - cpu->env.psw.mask ^= PSW_MASK_SHORTPSW; - cpu->env.psw.addr = spsw & PSW_MASK_SHORT_ADDR; - } else { - /* - * Firmware requires us to set the load state before we set - * the cpu to operating on protected guests. - */ - s390_cpu_set_state(S390_CPU_STATE_LOAD, cpu); - } - s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu); -} -#endif - /* S390CPUClass::reset() */ static void s390_cpu_reset(CPUState *s, cpu_reset_type type) { @@ -169,15 +132,6 @@ static void s390_cpu_reset(CPUState *s, cpu_reset_type type) } } -#if !defined(CONFIG_USER_ONLY) -static void s390_cpu_machine_reset_cb(void *opaque) -{ - S390CPU *cpu = opaque; - - run_on_cpu(CPU(cpu), s390_do_cpu_full_reset, RUN_ON_CPU_NULL); -} -#endif - static void s390_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) { info->mach = bfd_mach_s390_64; @@ -191,9 +145,6 @@ static void s390_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); S390CPUClass *scc = S390_CPU_GET_CLASS(dev); -#if !defined(CONFIG_USER_ONLY) - S390CPU *cpu = S390_CPU(dev); -#endif Error *err = NULL; /* the model has to be realized before qemu_init_vcpu() due to kvm */ @@ -203,23 +154,9 @@ static void s390_cpu_realizefn(DeviceState *dev, Error **errp) } #if !defined(CONFIG_USER_ONLY) - MachineState *ms = MACHINE(qdev_get_machine()); - unsigned int max_cpus = ms->smp.max_cpus; - if (cpu->env.core_id >= max_cpus) { - error_setg(&err, "Unable to add CPU with core-id: %" PRIu32 - ", maximum core-id: %d", cpu->env.core_id, - max_cpus - 1); + if (!s390_cpu_realize_sysemu(dev, &err)) { goto out; } - - if (cpu_exists(cpu->env.core_id)) { - error_setg(&err, "Unable to add CPU with core-id: %" PRIu32 - ", it already exists", cpu->env.core_id); - goto out; - } - - /* sync cs->cpu_index and env->core_id. The latter is needed for TCG. */ - cs->cpu_index = cpu->env.core_id; #endif cpu_exec_realizefn(cs, &err); @@ -228,7 +165,7 @@ static void s390_cpu_realizefn(DeviceState *dev, Error **errp) } #if !defined(CONFIG_USER_ONLY) - qemu_register_reset(s390_cpu_machine_reset_cb, cpu); + qemu_register_reset(s390_cpu_machine_reset_cb, S390_CPU(dev)); #endif s390_cpu_gdb_init(cs); qemu_init_vcpu(cs); @@ -250,44 +187,6 @@ out: error_propagate(errp, err); } -#if !defined(CONFIG_USER_ONLY) -static GuestPanicInformation *s390_cpu_get_crash_info(CPUState *cs) -{ - GuestPanicInformation *panic_info; - S390CPU *cpu = S390_CPU(cs); - - cpu_synchronize_state(cs); - panic_info = g_malloc0(sizeof(GuestPanicInformation)); - - panic_info->type = GUEST_PANIC_INFORMATION_TYPE_S390; - panic_info->u.s390.core = cpu->env.core_id; - panic_info->u.s390.psw_mask = cpu->env.psw.mask; - panic_info->u.s390.psw_addr = cpu->env.psw.addr; - panic_info->u.s390.reason = cpu->env.crash_reason; - - return panic_info; -} - -static void s390_cpu_get_crash_info_qom(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) -{ - CPUState *cs = CPU(obj); - GuestPanicInformation *panic_info; - - if (!cs->crash_occurred) { - error_setg(errp, "No crash occurred"); - return; - } - - panic_info = s390_cpu_get_crash_info(cs); - - visit_type_GuestPanicInformation(v, "crash-information", &panic_info, - errp); - qapi_free_GuestPanicInformation(panic_info); -} -#endif - static void s390_cpu_initfn(Object *obj) { CPUState *cs = CPU(obj); @@ -295,168 +194,11 @@ static void s390_cpu_initfn(Object *obj) cpu_set_cpustate_pointers(cpu); cs->exception_index = EXCP_HLT; -#if !defined(CONFIG_USER_ONLY) - cs->start_powered_off = true; - object_property_add(obj, "crash-information", "GuestPanicInformation", - s390_cpu_get_crash_info_qom, NULL, NULL, NULL); - cpu->env.tod_timer = - timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_tod_timer, cpu); - cpu->env.cpu_timer = - timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_cpu_timer, cpu); - s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu); -#endif -} - -static void s390_cpu_finalize(Object *obj) -{ -#if !defined(CONFIG_USER_ONLY) - S390CPU *cpu = S390_CPU(obj); - - timer_free(cpu->env.tod_timer); - timer_free(cpu->env.cpu_timer); - - qemu_unregister_reset(s390_cpu_machine_reset_cb, cpu); - g_free(cpu->irqstate); -#endif -} #if !defined(CONFIG_USER_ONLY) -static bool disabled_wait(CPUState *cpu) -{ - return cpu->halted && !(S390_CPU(cpu)->env.psw.mask & - (PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK)); -} - -static unsigned s390_count_running_cpus(void) -{ - CPUState *cpu; - int nr_running = 0; - - CPU_FOREACH(cpu) { - uint8_t state = S390_CPU(cpu)->env.cpu_state; - if (state == S390_CPU_STATE_OPERATING || - state == S390_CPU_STATE_LOAD) { - if (!disabled_wait(cpu)) { - nr_running++; - } - } - } - - return nr_running; -} - -unsigned int s390_cpu_halt(S390CPU *cpu) -{ - CPUState *cs = CPU(cpu); - trace_cpu_halt(cs->cpu_index); - - if (!cs->halted) { - cs->halted = 1; - cs->exception_index = EXCP_HLT; - } - - return s390_count_running_cpus(); -} - -void s390_cpu_unhalt(S390CPU *cpu) -{ - CPUState *cs = CPU(cpu); - trace_cpu_unhalt(cs->cpu_index); - - if (cs->halted) { - cs->halted = 0; - cs->exception_index = -1; - } -} - -unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu) - { - trace_cpu_set_state(CPU(cpu)->cpu_index, cpu_state); - - switch (cpu_state) { - case S390_CPU_STATE_STOPPED: - case S390_CPU_STATE_CHECK_STOP: - /* halt the cpu for common infrastructure */ - s390_cpu_halt(cpu); - break; - case S390_CPU_STATE_OPERATING: - case S390_CPU_STATE_LOAD: - /* - * Starting a CPU with a PSW WAIT bit set: - * KVM: handles this internally and triggers another WAIT exit. - * TCG: will actually try to continue to run. Don't unhalt, will - * be done when the CPU actually has work (an interrupt). - */ - if (!tcg_enabled() || !(cpu->env.psw.mask & PSW_MASK_WAIT)) { - s390_cpu_unhalt(cpu); - } - break; - default: - error_report("Requested CPU state is not a valid S390 CPU state: %u", - cpu_state); - exit(1); - } - if (kvm_enabled() && cpu->env.cpu_state != cpu_state) { - kvm_s390_set_cpu_state(cpu, cpu_state); - } - cpu->env.cpu_state = cpu_state; - - return s390_count_running_cpus(); -} - -int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit) -{ - if (kvm_enabled()) { - return kvm_s390_set_mem_limit(new_limit, hw_limit); - } - return 0; -} - -void s390_set_max_pagesize(uint64_t pagesize, Error **errp) -{ - if (kvm_enabled()) { - kvm_s390_set_max_pagesize(pagesize, errp); - } -} - -void s390_cmma_reset(void) -{ - if (kvm_enabled()) { - kvm_s390_cmma_reset(); - } -} - -int s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch_id, - int vq, bool assign) -{ - if (kvm_enabled()) { - return kvm_s390_assign_subch_ioeventfd(notifier, sch_id, vq, assign); - } else { - return 0; - } -} - -void s390_crypto_reset(void) -{ - if (kvm_enabled()) { - kvm_s390_crypto_reset(); - } -} - -void s390_enable_css_support(S390CPU *cpu) -{ - if (kvm_enabled()) { - kvm_s390_enable_css_support(cpu); - } -} - -void s390_do_cpu_set_diag318(CPUState *cs, run_on_cpu_data arg) -{ - if (kvm_enabled()) { - kvm_s390_set_diag318(cs, arg.host_ulong); - } -} + s390_cpu_init_sysemu(obj); #endif +} static gchar *s390_gdb_arch_name(CPUState *cs) { @@ -476,17 +218,6 @@ static void s390_cpu_reset_full(DeviceState *dev) return s390_cpu_reset(s, S390_CPU_RESET_CLEAR); } -#ifndef CONFIG_USER_ONLY -#include "hw/core/sysemu-cpu-ops.h" - -static const struct SysemuCPUOps s390_sysemu_ops = { - .get_phys_page_debug = s390_cpu_get_phys_page_debug, - .get_crash_info = s390_cpu_get_crash_info, - .write_elf64_note = s390_cpu_write_elf64_note, - .legacy_vmsd = &vmstate_s390_cpu, -}; -#endif - #ifdef CONFIG_TCG #include "hw/core/tcg-cpu-ops.h" @@ -515,9 +246,7 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data) dc->user_creatable = true; device_class_set_parent_reset(dc, s390_cpu_reset_full, &scc->parent_reset); -#if !defined(CONFIG_USER_ONLY) - scc->load_normal = s390_cpu_load_normal; -#endif + scc->reset = s390_cpu_reset; cc->class_by_name = s390_cpu_class_by_name, cc->has_work = s390_cpu_has_work; @@ -526,7 +255,7 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data) cc->gdb_read_register = s390_cpu_gdb_read_register; cc->gdb_write_register = s390_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY - cc->sysemu_ops = &s390_sysemu_ops; + s390_cpu_class_init_sysemu(cc); #endif cc->disas_set_info = s390_cpu_disas_set_info; cc->gdb_num_core_regs = S390_NUM_CORE_REGS; @@ -546,7 +275,11 @@ static const TypeInfo s390_cpu_type_info = { .instance_size = sizeof(S390CPU), .instance_align = __alignof__(S390CPU), .instance_init = s390_cpu_initfn, + +#ifndef CONFIG_USER_ONLY .instance_finalize = s390_cpu_finalize, +#endif /* !CONFIG_USER_ONLY */ + .abstract = true, .class_size = sizeof(S390CPUClass), .class_init = s390_cpu_class_init, diff --git a/target/s390x/meson.build b/target/s390x/meson.build index 60d7f1b908..a73bae3dc5 100644 --- a/target/s390x/meson.build +++ b/target/s390x/meson.build @@ -28,6 +28,7 @@ s390x_softmmu_ss.add(files( 'machine.c', 'mmu_helper.c', 'sigp.c', + 'cpu-sysemu.c', )) # Newer kernels on s390 check for an S390_PGSTE program header and diff --git a/target/s390x/s390x-internal.h b/target/s390x/s390x-internal.h index 9256275376..17edd4d13b 100644 --- a/target/s390x/s390x-internal.h +++ b/target/s390x/s390x-internal.h @@ -240,6 +240,12 @@ uint32_t calc_cc(CPUS390XState *env, uint32_t cc_op, uint64_t src, uint64_t dst, #ifndef CONFIG_USER_ONLY unsigned int s390_cpu_halt(S390CPU *cpu); void s390_cpu_unhalt(S390CPU *cpu); +void s390_cpu_init_sysemu(Object *obj); +bool s390_cpu_realize_sysemu(DeviceState *dev, Error **errp); +void s390_cpu_finalize(Object *obj); +void s390_cpu_class_init_sysemu(CPUClass *cc); +void s390_cpu_machine_reset_cb(void *opaque); + #else static inline unsigned int s390_cpu_halt(S390CPU *cpu) { diff --git a/target/s390x/trace-events b/target/s390x/trace-events index e661a81e3a..e83a8cf85e 100644 --- a/target/s390x/trace-events +++ b/target/s390x/trace-events @@ -16,7 +16,7 @@ kvm_clear_cmma(int rc) "CMMA: clearing with result code %d" kvm_failed_cpu_state_set(int cpu_index, uint8_t state, const char *msg) "Warning: Unable to set cpu %d state %" PRIu8 " to KVM: %s" kvm_assign_subch_ioeventfd(int fd, uint32_t addr, bool assign, int datamatch) "fd: %d sch: @0x%x assign: %d vq: %d" -# cpu.c +# cpu-sysemu.c cpu_set_state(int cpu_index, uint8_t state) "setting cpu %d state to %" PRIu8 cpu_halt(int cpu_index) "halting cpu %d" cpu_unhalt(int cpu_index) "unhalting cpu %d" From 98be64d2f4dd5ff66b16e3525ee7d099d82f5ec8 Mon Sep 17 00:00:00 2001 From: "Cho, Yu-Chen" Date: Wed, 7 Jul 2021 18:53:19 +0800 Subject: [PATCH 035/272] target/s390x: split cpu-dump from helper.c Splitting this functionality also allows us to make helper.c sysemu-only. Signed-off-by: Claudio Fontana Signed-off-by: Cho, Yu-Chen Acked-by: Cornelia Huck Reviewed-by: Thomas Huth Message-Id: <20210707105324.23400-10-acho@suse.com> Signed-off-by: Cornelia Huck --- target/s390x/cpu-dump.c | 134 ++++++++++++++++++++++++++++++++++ target/s390x/cpu.c | 43 +++++++++++ target/s390x/helper.c | 151 --------------------------------------- target/s390x/meson.build | 1 + 4 files changed, 178 insertions(+), 151 deletions(-) create mode 100644 target/s390x/cpu-dump.c diff --git a/target/s390x/cpu-dump.c b/target/s390x/cpu-dump.c new file mode 100644 index 0000000000..0f5c062994 --- /dev/null +++ b/target/s390x/cpu-dump.c @@ -0,0 +1,134 @@ +/* + * S/390 CPU dump to FILE + * + * Copyright (c) 2009 Ulrich Hecht + * Copyright (c) 2011 Alexander Graf + * + * 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 "qemu/osdep.h" +#include "cpu.h" +#include "s390x-internal.h" +#include "qemu/qemu-print.h" +#include "sysemu/tcg.h" + +void s390_cpu_dump_state(CPUState *cs, FILE *f, int flags) +{ + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + int i; + + qemu_fprintf(f, "PSW=mask %016" PRIx64 " addr %016" PRIx64, + s390_cpu_get_psw_mask(env), env->psw.addr); + if (!tcg_enabled()) { + qemu_fprintf(f, "\n"); + } else if (env->cc_op > 3) { + qemu_fprintf(f, " cc %15s\n", cc_name(env->cc_op)); + } else { + qemu_fprintf(f, " cc %02x\n", env->cc_op); + } + + for (i = 0; i < 16; i++) { + qemu_fprintf(f, "R%02d=%016" PRIx64, i, env->regs[i]); + if ((i % 4) == 3) { + qemu_fprintf(f, "\n"); + } else { + qemu_fprintf(f, " "); + } + } + + if (flags & CPU_DUMP_FPU) { + if (s390_has_feat(S390_FEAT_VECTOR)) { + for (i = 0; i < 32; i++) { + qemu_fprintf(f, "V%02d=%016" PRIx64 "%016" PRIx64 "%c", + i, env->vregs[i][0], env->vregs[i][1], + i % 2 ? '\n' : ' '); + } + } else { + for (i = 0; i < 16; i++) { + qemu_fprintf(f, "F%02d=%016" PRIx64 "%c", + i, *get_freg(env, i), + (i % 4) == 3 ? '\n' : ' '); + } + } + } + +#ifndef CONFIG_USER_ONLY + for (i = 0; i < 16; i++) { + qemu_fprintf(f, "C%02d=%016" PRIx64, i, env->cregs[i]); + if ((i % 4) == 3) { + qemu_fprintf(f, "\n"); + } else { + qemu_fprintf(f, " "); + } + } +#endif + +#ifdef DEBUG_INLINE_BRANCHES + for (i = 0; i < CC_OP_MAX; i++) { + qemu_fprintf(f, " %15s = %10ld\t%10ld\n", cc_name(i), + inline_branch_miss[i], inline_branch_hit[i]); + } +#endif + + qemu_fprintf(f, "\n"); +} + +const char *cc_name(enum cc_op cc_op) +{ + static const char * const cc_names[] = { + [CC_OP_CONST0] = "CC_OP_CONST0", + [CC_OP_CONST1] = "CC_OP_CONST1", + [CC_OP_CONST2] = "CC_OP_CONST2", + [CC_OP_CONST3] = "CC_OP_CONST3", + [CC_OP_DYNAMIC] = "CC_OP_DYNAMIC", + [CC_OP_STATIC] = "CC_OP_STATIC", + [CC_OP_NZ] = "CC_OP_NZ", + [CC_OP_ADDU] = "CC_OP_ADDU", + [CC_OP_SUBU] = "CC_OP_SUBU", + [CC_OP_LTGT_32] = "CC_OP_LTGT_32", + [CC_OP_LTGT_64] = "CC_OP_LTGT_64", + [CC_OP_LTUGTU_32] = "CC_OP_LTUGTU_32", + [CC_OP_LTUGTU_64] = "CC_OP_LTUGTU_64", + [CC_OP_LTGT0_32] = "CC_OP_LTGT0_32", + [CC_OP_LTGT0_64] = "CC_OP_LTGT0_64", + [CC_OP_ADD_64] = "CC_OP_ADD_64", + [CC_OP_SUB_64] = "CC_OP_SUB_64", + [CC_OP_ABS_64] = "CC_OP_ABS_64", + [CC_OP_NABS_64] = "CC_OP_NABS_64", + [CC_OP_ADD_32] = "CC_OP_ADD_32", + [CC_OP_SUB_32] = "CC_OP_SUB_32", + [CC_OP_ABS_32] = "CC_OP_ABS_32", + [CC_OP_NABS_32] = "CC_OP_NABS_32", + [CC_OP_COMP_32] = "CC_OP_COMP_32", + [CC_OP_COMP_64] = "CC_OP_COMP_64", + [CC_OP_TM_32] = "CC_OP_TM_32", + [CC_OP_TM_64] = "CC_OP_TM_64", + [CC_OP_NZ_F32] = "CC_OP_NZ_F32", + [CC_OP_NZ_F64] = "CC_OP_NZ_F64", + [CC_OP_NZ_F128] = "CC_OP_NZ_F128", + [CC_OP_ICM] = "CC_OP_ICM", + [CC_OP_SLA_32] = "CC_OP_SLA_32", + [CC_OP_SLA_64] = "CC_OP_SLA_64", + [CC_OP_FLOGR] = "CC_OP_FLOGR", + [CC_OP_LCBB] = "CC_OP_LCBB", + [CC_OP_VC] = "CC_OP_VC", + [CC_OP_MULS_32] = "CC_OP_MULS_32", + [CC_OP_MULS_64] = "CC_OP_MULS_64", + }; + + return cc_names[cc_op]; +} diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 2b2b70e1c6..5c456f6014 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -34,10 +34,53 @@ #include "hw/qdev-properties.h" #include "fpu/softfloat-helpers.h" #include "disas/capstone.h" +#include "sysemu/tcg.h" #define CR0_RESET 0xE0UL #define CR14_RESET 0xC2000000UL; +void s390_cpu_set_psw(CPUS390XState *env, uint64_t mask, uint64_t addr) +{ +#ifndef CONFIG_USER_ONLY + uint64_t old_mask = env->psw.mask; +#endif + + env->psw.addr = addr; + env->psw.mask = mask; + + /* KVM will handle all WAITs and trigger a WAIT exit on disabled_wait */ + if (!tcg_enabled()) { + return; + } + env->cc_op = (mask >> 44) & 3; + +#ifndef CONFIG_USER_ONLY + if ((old_mask ^ mask) & PSW_MASK_PER) { + s390_cpu_recompute_watchpoints(env_cpu(env)); + } + + if (mask & PSW_MASK_WAIT) { + s390_handle_wait(env_archcpu(env)); + } +#endif +} + +uint64_t s390_cpu_get_psw_mask(CPUS390XState *env) +{ + uint64_t r = env->psw.mask; + + if (tcg_enabled()) { + uint64_t cc = calc_cc(env, env->cc_op, env->cc_src, + env->cc_dst, env->cc_vr); + + assert(cc <= 3); + r &= ~PSW_MASK_CC; + r |= cc << 44; + } + + return r; +} + static void s390_cpu_set_pc(CPUState *cs, vaddr value) { S390CPU *cpu = S390_CPU(cs); diff --git a/target/s390x/helper.c b/target/s390x/helper.c index 8015c4e3d1..c72e990f4d 100644 --- a/target/s390x/helper.c +++ b/target/s390x/helper.c @@ -23,7 +23,6 @@ #include "s390x-internal.h" #include "exec/gdbstub.h" #include "qemu/timer.h" -#include "qemu/qemu-print.h" #include "hw/s390x/ioinst.h" #include "hw/s390x/pv.h" #include "sysemu/hw_accel.h" @@ -289,153 +288,3 @@ int s390_store_adtl_status(S390CPU *cpu, hwaddr addr, hwaddr len) /* For user-only, tcg is always enabled. */ #define tcg_enabled() true #endif /* CONFIG_USER_ONLY */ - -void s390_cpu_set_psw(CPUS390XState *env, uint64_t mask, uint64_t addr) -{ -#ifndef CONFIG_USER_ONLY - uint64_t old_mask = env->psw.mask; -#endif - - env->psw.addr = addr; - env->psw.mask = mask; - - /* KVM will handle all WAITs and trigger a WAIT exit on disabled_wait */ - if (!tcg_enabled()) { - return; - } - env->cc_op = (mask >> 44) & 3; - -#ifndef CONFIG_USER_ONLY - if ((old_mask ^ mask) & PSW_MASK_PER) { - s390_cpu_recompute_watchpoints(env_cpu(env)); - } - - if (mask & PSW_MASK_WAIT) { - s390_handle_wait(env_archcpu(env)); - } -#endif -} - -uint64_t s390_cpu_get_psw_mask(CPUS390XState *env) -{ - uint64_t r = env->psw.mask; - - if (tcg_enabled()) { - uint64_t cc = calc_cc(env, env->cc_op, env->cc_src, - env->cc_dst, env->cc_vr); - - assert(cc <= 3); - r &= ~PSW_MASK_CC; - r |= cc << 44; - } - - return r; -} - -void s390_cpu_dump_state(CPUState *cs, FILE *f, int flags) -{ - S390CPU *cpu = S390_CPU(cs); - CPUS390XState *env = &cpu->env; - int i; - - qemu_fprintf(f, "PSW=mask %016" PRIx64 " addr %016" PRIx64, - s390_cpu_get_psw_mask(env), env->psw.addr); - if (!tcg_enabled()) { - qemu_fprintf(f, "\n"); - } else if (env->cc_op > 3) { - qemu_fprintf(f, " cc %15s\n", cc_name(env->cc_op)); - } else { - qemu_fprintf(f, " cc %02x\n", env->cc_op); - } - - for (i = 0; i < 16; i++) { - qemu_fprintf(f, "R%02d=%016" PRIx64, i, env->regs[i]); - if ((i % 4) == 3) { - qemu_fprintf(f, "\n"); - } else { - qemu_fprintf(f, " "); - } - } - - if (flags & CPU_DUMP_FPU) { - if (s390_has_feat(S390_FEAT_VECTOR)) { - for (i = 0; i < 32; i++) { - qemu_fprintf(f, "V%02d=%016" PRIx64 "%016" PRIx64 "%c", - i, env->vregs[i][0], env->vregs[i][1], - i % 2 ? '\n' : ' '); - } - } else { - for (i = 0; i < 16; i++) { - qemu_fprintf(f, "F%02d=%016" PRIx64 "%c", - i, *get_freg(env, i), - (i % 4) == 3 ? '\n' : ' '); - } - } - } - -#ifndef CONFIG_USER_ONLY - for (i = 0; i < 16; i++) { - qemu_fprintf(f, "C%02d=%016" PRIx64, i, env->cregs[i]); - if ((i % 4) == 3) { - qemu_fprintf(f, "\n"); - } else { - qemu_fprintf(f, " "); - } - } -#endif - -#ifdef DEBUG_INLINE_BRANCHES - for (i = 0; i < CC_OP_MAX; i++) { - qemu_fprintf(f, " %15s = %10ld\t%10ld\n", cc_name(i), - inline_branch_miss[i], inline_branch_hit[i]); - } -#endif - - qemu_fprintf(f, "\n"); -} - -const char *cc_name(enum cc_op cc_op) -{ - static const char * const cc_names[] = { - [CC_OP_CONST0] = "CC_OP_CONST0", - [CC_OP_CONST1] = "CC_OP_CONST1", - [CC_OP_CONST2] = "CC_OP_CONST2", - [CC_OP_CONST3] = "CC_OP_CONST3", - [CC_OP_DYNAMIC] = "CC_OP_DYNAMIC", - [CC_OP_STATIC] = "CC_OP_STATIC", - [CC_OP_NZ] = "CC_OP_NZ", - [CC_OP_ADDU] = "CC_OP_ADDU", - [CC_OP_SUBU] = "CC_OP_SUBU", - [CC_OP_LTGT_32] = "CC_OP_LTGT_32", - [CC_OP_LTGT_64] = "CC_OP_LTGT_64", - [CC_OP_LTUGTU_32] = "CC_OP_LTUGTU_32", - [CC_OP_LTUGTU_64] = "CC_OP_LTUGTU_64", - [CC_OP_LTGT0_32] = "CC_OP_LTGT0_32", - [CC_OP_LTGT0_64] = "CC_OP_LTGT0_64", - [CC_OP_ADD_64] = "CC_OP_ADD_64", - [CC_OP_SUB_64] = "CC_OP_SUB_64", - [CC_OP_ABS_64] = "CC_OP_ABS_64", - [CC_OP_NABS_64] = "CC_OP_NABS_64", - [CC_OP_ADD_32] = "CC_OP_ADD_32", - [CC_OP_SUB_32] = "CC_OP_SUB_32", - [CC_OP_ABS_32] = "CC_OP_ABS_32", - [CC_OP_NABS_32] = "CC_OP_NABS_32", - [CC_OP_COMP_32] = "CC_OP_COMP_32", - [CC_OP_COMP_64] = "CC_OP_COMP_64", - [CC_OP_TM_32] = "CC_OP_TM_32", - [CC_OP_TM_64] = "CC_OP_TM_64", - [CC_OP_NZ_F32] = "CC_OP_NZ_F32", - [CC_OP_NZ_F64] = "CC_OP_NZ_F64", - [CC_OP_NZ_F128] = "CC_OP_NZ_F128", - [CC_OP_ICM] = "CC_OP_ICM", - [CC_OP_SLA_32] = "CC_OP_SLA_32", - [CC_OP_SLA_64] = "CC_OP_SLA_64", - [CC_OP_FLOGR] = "CC_OP_FLOGR", - [CC_OP_LCBB] = "CC_OP_LCBB", - [CC_OP_VC] = "CC_OP_VC", - [CC_OP_MULS_32] = "CC_OP_MULS_32", - [CC_OP_MULS_64] = "CC_OP_MULS_64", - }; - - return cc_names[cc_op]; -} diff --git a/target/s390x/meson.build b/target/s390x/meson.build index a73bae3dc5..6e1aa3b0cd 100644 --- a/target/s390x/meson.build +++ b/target/s390x/meson.build @@ -6,6 +6,7 @@ s390x_ss.add(files( 'gdbstub.c', 'helper.c', 'interrupt.c', + 'cpu-dump.c', )) s390x_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c'), if_false: files('kvm-stub.c')) From da9448854693bb5958d181b8f67bdb7760e0d0b0 Mon Sep 17 00:00:00 2001 From: "Cho, Yu-Chen" Date: Wed, 7 Jul 2021 18:53:20 +0800 Subject: [PATCH 036/272] target/s390x: make helper.c sysemu-only Now that we have moved cpu-dump functionality out of helper.c, we can make the module sysemu-only. Signed-off-by: Claudio Fontana Signed-off-by: Cho, Yu-Chen Acked-by: Cornelia Huck Reviewed-by: Thomas Huth Message-Id: <20210707105324.23400-11-acho@suse.com> Signed-off-by: Cornelia Huck --- target/s390x/helper.c | 9 +-------- target/s390x/meson.build | 2 +- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/target/s390x/helper.c b/target/s390x/helper.c index c72e990f4d..6e35473c7f 100644 --- a/target/s390x/helper.c +++ b/target/s390x/helper.c @@ -1,5 +1,5 @@ /* - * S/390 helpers + * S/390 helpers - sysemu only * * Copyright (c) 2009 Ulrich Hecht * Copyright (c) 2011 Alexander Graf @@ -27,11 +27,8 @@ #include "hw/s390x/pv.h" #include "sysemu/hw_accel.h" #include "sysemu/runstate.h" -#ifndef CONFIG_USER_ONLY #include "sysemu/tcg.h" -#endif -#ifndef CONFIG_USER_ONLY void s390x_tod_timer(void *opaque) { cpu_inject_clock_comparator((S390CPU *) opaque); @@ -284,7 +281,3 @@ int s390_store_adtl_status(S390CPU *cpu, hwaddr addr, hwaddr len) cpu_physical_memory_unmap(sa, len, 1, len); return 0; } -#else -/* For user-only, tcg is always enabled. */ -#define tcg_enabled() true -#endif /* CONFIG_USER_ONLY */ diff --git a/target/s390x/meson.build b/target/s390x/meson.build index 6e1aa3b0cd..bbcaede384 100644 --- a/target/s390x/meson.build +++ b/target/s390x/meson.build @@ -4,7 +4,6 @@ s390x_ss.add(files( 'cpu_features.c', 'cpu_models.c', 'gdbstub.c', - 'helper.c', 'interrupt.c', 'cpu-dump.c', )) @@ -23,6 +22,7 @@ s390x_ss.add(gen_features_h) s390x_softmmu_ss = ss.source_set() s390x_softmmu_ss.add(files( + 'helper.c', 'arch_dump.c', 'diag.c', 'ioinst.c', From 1fc66ac17a604b3df78518ed9bc9bdf5552ee33a Mon Sep 17 00:00:00 2001 From: "Cho, Yu-Chen" Date: Wed, 7 Jul 2021 18:53:21 +0800 Subject: [PATCH 037/272] target/s390x: use kvm_enabled() to wrap call to kvm_s390_get_hpage_1m this will allow to remove the kvm stubs. Signed-off-by: Claudio Fontana Signed-off-by: Cho, Yu-Chen Reviewed-by: Thomas Huth Message-Id: <20210707105324.23400-12-acho@suse.com> Signed-off-by: Cornelia Huck --- target/s390x/diag.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/s390x/diag.c b/target/s390x/diag.c index c17a2498a7..8405f69df0 100644 --- a/target/s390x/diag.c +++ b/target/s390x/diag.c @@ -20,6 +20,7 @@ #include "hw/s390x/ipl.h" #include "hw/s390x/s390-virtio-ccw.h" #include "hw/s390x/pv.h" +#include "sysemu/kvm.h" #include "kvm_s390x.h" int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3) @@ -168,7 +169,7 @@ out: return; } - if (kvm_s390_get_hpage_1m()) { + if (kvm_enabled() && kvm_s390_get_hpage_1m()) { error_report("Protected VMs can currently not be backed with " "huge pages"); env->regs[r1 + 1] = DIAG_308_RC_INVAL_FOR_PV; From 777ef689b5e5732c4d99f78f9e39f797dfe5fba2 Mon Sep 17 00:00:00 2001 From: "Cho, Yu-Chen" Date: Wed, 7 Jul 2021 18:53:22 +0800 Subject: [PATCH 038/272] target/s390x: remove kvm-stub.c all function calls are protected by kvm_enabled(), so we do not need the stubs. Signed-off-by: Claudio Fontana Signed-off-by: Cho, Yu-Chen Reviewed-by: Cornelia Huck Reviewed-by: Thomas Huth Message-Id: <20210707105324.23400-13-acho@suse.com> Signed-off-by: Cornelia Huck --- MAINTAINERS | 1 - target/s390x/kvm-stub.c | 121 --------------------------------------- target/s390x/meson.build | 2 +- 3 files changed, 1 insertion(+), 123 deletions(-) delete mode 100644 target/s390x/kvm-stub.c diff --git a/MAINTAINERS b/MAINTAINERS index 8ec845f4e0..4e04800576 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -396,7 +396,6 @@ M: Christian Borntraeger S: Supported F: target/s390x/kvm.c F: target/s390x/kvm_s390x.h -F: target/s390x/kvm-stub.c F: target/s390x/ioinst.[ch] F: target/s390x/machine.c F: target/s390x/sigp.c diff --git a/target/s390x/kvm-stub.c b/target/s390x/kvm-stub.c deleted file mode 100644 index 8a308cfebb..0000000000 --- a/target/s390x/kvm-stub.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * QEMU KVM support -- s390x specific function stubs. - * - * Copyright (c) 2009 Ulrich Hecht - * - * 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 "cpu.h" -#include "kvm_s390x.h" - -void kvm_s390_access_exception(S390CPU *cpu, uint16_t code, uint64_t te_code) -{ -} - -int kvm_s390_mem_op(S390CPU *cpu, vaddr addr, uint8_t ar, void *hostbuf, - int len, bool is_write) -{ - return -ENOSYS; -} - -void kvm_s390_program_interrupt(S390CPU *cpu, uint16_t code) -{ -} - -int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state) -{ - return -ENOSYS; -} - -void kvm_s390_vcpu_interrupt_pre_save(S390CPU *cpu) -{ -} - -int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu) -{ - return 0; -} - -int kvm_s390_get_hpage_1m(void) -{ - return 0; -} - -int kvm_s390_get_ri(void) -{ - return 0; -} - -int kvm_s390_get_clock(uint8_t *tod_high, uint64_t *tod_low) -{ - return -ENOSYS; -} - -int kvm_s390_get_clock_ext(uint8_t *tod_high, uint64_t *tod_low) -{ - return -ENOSYS; -} - -int kvm_s390_set_clock(uint8_t tod_high, uint64_t tod_low) -{ - return -ENOSYS; -} - -int kvm_s390_set_clock_ext(uint8_t tod_high, uint64_t tod_low) -{ - return -ENOSYS; -} - -void kvm_s390_enable_css_support(S390CPU *cpu) -{ -} - -int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch, - int vq, bool assign) -{ - return -ENOSYS; -} - -void kvm_s390_cmma_reset(void) -{ -} - -void kvm_s390_reset_vcpu_initial(S390CPU *cpu) -{ -} - -void kvm_s390_reset_vcpu_clear(S390CPU *cpu) -{ -} - -void kvm_s390_reset_vcpu_normal(S390CPU *cpu) -{ -} - -int kvm_s390_set_mem_limit(uint64_t new_limit, uint64_t *hw_limit) -{ - return 0; -} - -void kvm_s390_set_max_pagesize(uint64_t pagesize, Error **errp) -{ -} - -void kvm_s390_crypto_reset(void) -{ -} - -void kvm_s390_stop_interrupt(S390CPU *cpu) -{ -} - -void kvm_s390_restart_interrupt(S390CPU *cpu) -{ -} - -void kvm_s390_set_diag318(CPUState *cs, uint64_t diag318_info) -{ -} diff --git a/target/s390x/meson.build b/target/s390x/meson.build index bbcaede384..6c8e03b8fb 100644 --- a/target/s390x/meson.build +++ b/target/s390x/meson.build @@ -8,7 +8,7 @@ s390x_ss.add(files( 'cpu-dump.c', )) -s390x_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c'), if_false: files('kvm-stub.c')) +s390x_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c')) gen_features = executable('gen-features', 'gen-features.c', native: true, build_by_default: false) From 67043607d17cf62f4ae8110151c44fb73295e66f Mon Sep 17 00:00:00 2001 From: "Cho, Yu-Chen" Date: Wed, 7 Jul 2021 18:53:23 +0800 Subject: [PATCH 039/272] target/s390x: move kvm files into kvm/ move kvm files into kvm/ After the reshuffling, update MAINTAINERS accordingly. Make use of the new directory: target/s390x/kvm/ Signed-off-by: Claudio Fontana Signed-off-by: Cho, Yu-Chen Reviewed-by: Cornelia Huck Reviewed-by: Thomas Huth Message-Id: <20210707105324.23400-14-acho@suse.com> Signed-off-by: Cornelia Huck --- MAINTAINERS | 3 +-- hw/intc/s390_flic_kvm.c | 2 +- hw/s390x/s390-stattrib-kvm.c | 2 +- hw/s390x/tod-kvm.c | 2 +- hw/vfio/ap.c | 2 +- meson.build | 1 + target/s390x/cpu-sysemu.c | 2 +- target/s390x/cpu.c | 2 +- target/s390x/cpu_models.c | 2 +- target/s390x/diag.c | 2 +- target/s390x/interrupt.c | 2 +- target/s390x/{ => kvm}/kvm.c | 0 target/s390x/{ => kvm}/kvm_s390x.h | 0 target/s390x/kvm/meson.build | 17 +++++++++++++++++ target/s390x/kvm/trace-events | 7 +++++++ target/s390x/kvm/trace.h | 1 + target/s390x/machine.c | 2 +- target/s390x/meson.build | 16 +--------------- target/s390x/mmu_helper.c | 2 +- target/s390x/trace-events | 6 ------ 20 files changed, 39 insertions(+), 34 deletions(-) rename target/s390x/{ => kvm}/kvm.c (100%) rename target/s390x/{ => kvm}/kvm_s390x.h (100%) create mode 100644 target/s390x/kvm/meson.build create mode 100644 target/s390x/kvm/trace-events create mode 100644 target/s390x/kvm/trace.h diff --git a/MAINTAINERS b/MAINTAINERS index 4e04800576..11d9ce72aa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -394,8 +394,7 @@ M: Halil Pasic M: Cornelia Huck M: Christian Borntraeger S: Supported -F: target/s390x/kvm.c -F: target/s390x/kvm_s390x.h +F: target/s390x/kvm/ F: target/s390x/ioinst.[ch] F: target/s390x/machine.c F: target/s390x/sigp.c diff --git a/hw/intc/s390_flic_kvm.c b/hw/intc/s390_flic_kvm.c index 929cfa3a68..efe5054182 100644 --- a/hw/intc/s390_flic_kvm.c +++ b/hw/intc/s390_flic_kvm.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "kvm_s390x.h" +#include "kvm/kvm_s390x.h" #include #include "qemu/error-report.h" #include "qemu/module.h" diff --git a/hw/s390x/s390-stattrib-kvm.c b/hw/s390x/s390-stattrib-kvm.c index f0b11a74e4..24cd01382e 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 "sysemu/kvm.h" #include "exec/ram_addr.h" -#include "kvm_s390x.h" +#include "kvm/kvm_s390x.h" Object *kvm_s390_stattrib_create(void) { diff --git a/hw/s390x/tod-kvm.c b/hw/s390x/tod-kvm.c index 0b94477486..ec855811ae 100644 --- a/hw/s390x/tod-kvm.c +++ b/hw/s390x/tod-kvm.c @@ -13,7 +13,7 @@ #include "qemu/module.h" #include "sysemu/runstate.h" #include "hw/s390x/tod.h" -#include "kvm_s390x.h" +#include "kvm/kvm_s390x.h" static void kvm_s390_get_tod_raw(S390TOD *tod, Error **errp) { diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index 4b32aca1a0..e0dd561e85 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -21,7 +21,7 @@ #include "qemu/module.h" #include "qemu/option.h" #include "qemu/config-file.h" -#include "kvm_s390x.h" +#include "kvm/kvm_s390x.h" #include "migration/vmstate.h" #include "hw/qdev-properties.h" #include "hw/s390x/ap-bridge.h" diff --git a/meson.build b/meson.build index 7e12de01be..b458cd33b8 100644 --- a/meson.build +++ b/meson.build @@ -2102,6 +2102,7 @@ if have_system or have_user 'target/ppc', 'target/riscv', 'target/s390x', + 'target/s390x/kvm', 'target/sparc', ] endif diff --git a/target/s390x/cpu-sysemu.c b/target/s390x/cpu-sysemu.c index 16e5301084..df2c6bf694 100644 --- a/target/s390x/cpu-sysemu.c +++ b/target/s390x/cpu-sysemu.c @@ -24,7 +24,7 @@ #include "qapi/error.h" #include "cpu.h" #include "s390x-internal.h" -#include "kvm_s390x.h" +#include "kvm/kvm_s390x.h" #include "sysemu/kvm.h" #include "sysemu/reset.h" #include "qemu/timer.h" diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 5c456f6014..7b7b05f1d3 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -24,7 +24,7 @@ #include "qapi/error.h" #include "cpu.h" #include "s390x-internal.h" -#include "kvm_s390x.h" +#include "kvm/kvm_s390x.h" #include "sysemu/kvm.h" #include "sysemu/reset.h" #include "qemu/module.h" diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 442f06e140..624c7ea971 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "s390x-internal.h" -#include "kvm_s390x.h" +#include "kvm/kvm_s390x.h" #include "sysemu/kvm.h" #include "sysemu/tcg.h" #include "qapi/error.h" diff --git a/target/s390x/diag.c b/target/s390x/diag.c index 8405f69df0..76b01dcd68 100644 --- a/target/s390x/diag.c +++ b/target/s390x/diag.c @@ -21,7 +21,7 @@ #include "hw/s390x/s390-virtio-ccw.h" #include "hw/s390x/pv.h" #include "sysemu/kvm.h" -#include "kvm_s390x.h" +#include "kvm/kvm_s390x.h" int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3) { diff --git a/target/s390x/interrupt.c b/target/s390x/interrupt.c index 734f0c62de..5195f060ec 100644 --- a/target/s390x/interrupt.c +++ b/target/s390x/interrupt.c @@ -9,7 +9,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "kvm_s390x.h" +#include "kvm/kvm_s390x.h" #include "s390x-internal.h" #include "exec/exec-all.h" #include "sysemu/kvm.h" diff --git a/target/s390x/kvm.c b/target/s390x/kvm/kvm.c similarity index 100% rename from target/s390x/kvm.c rename to target/s390x/kvm/kvm.c diff --git a/target/s390x/kvm_s390x.h b/target/s390x/kvm/kvm_s390x.h similarity index 100% rename from target/s390x/kvm_s390x.h rename to target/s390x/kvm/kvm_s390x.h diff --git a/target/s390x/kvm/meson.build b/target/s390x/kvm/meson.build new file mode 100644 index 0000000000..d1356356b1 --- /dev/null +++ b/target/s390x/kvm/meson.build @@ -0,0 +1,17 @@ + +s390x_ss.add(when: 'CONFIG_KVM', if_true: files( + 'kvm.c' +)) + +# Newer kernels on s390 check for an S390_PGSTE program header and +# enable the pgste page table extensions in that case. This makes +# the vm.allocate_pgste sysctl unnecessary. We enable this program +# header if +# - we build on s390x +# - we build the system emulation for s390x (qemu-system-s390x) +# - KVM is enabled +# - the linker supports --s390-pgste +if host_machine.cpu_family() == 's390x' and cc.has_link_argument('-Wl,--s390-pgste') + s390x_softmmu_ss.add(when: 'CONFIG_KVM', + if_true: declare_dependency(link_args: ['-Wl,--s390-pgste'])) +endif diff --git a/target/s390x/kvm/trace-events b/target/s390x/kvm/trace-events new file mode 100644 index 0000000000..5289f5f675 --- /dev/null +++ b/target/s390x/kvm/trace-events @@ -0,0 +1,7 @@ +# See docs/devel/tracing.txt for syntax documentation. + +# kvm.c +kvm_enable_cmma(int rc) "CMMA: enabling with result code %d" +kvm_clear_cmma(int rc) "CMMA: clearing with result code %d" +kvm_failed_cpu_state_set(int cpu_index, uint8_t state, const char *msg) "Warning: Unable to set cpu %d state %" PRIu8 " to KVM: %s" +kvm_assign_subch_ioeventfd(int fd, uint32_t addr, bool assign, int datamatch) "fd: %d sch: @0x%x assign: %d vq: %d" diff --git a/target/s390x/kvm/trace.h b/target/s390x/kvm/trace.h new file mode 100644 index 0000000000..ae195b1306 --- /dev/null +++ b/target/s390x/kvm/trace.h @@ -0,0 +1 @@ +#include "trace/trace-target_s390x_kvm.h" diff --git a/target/s390x/machine.c b/target/s390x/machine.c index 81a8a7ff99..37a076858c 100644 --- a/target/s390x/machine.c +++ b/target/s390x/machine.c @@ -17,7 +17,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "s390x-internal.h" -#include "kvm_s390x.h" +#include "kvm/kvm_s390x.h" #include "migration/vmstate.h" #include "tcg/tcg_s390x.h" #include "sysemu/kvm.h" diff --git a/target/s390x/meson.build b/target/s390x/meson.build index 6c8e03b8fb..ec73bed524 100644 --- a/target/s390x/meson.build +++ b/target/s390x/meson.build @@ -8,8 +8,6 @@ s390x_ss.add(files( 'cpu-dump.c', )) -s390x_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c')) - gen_features = executable('gen-features', 'gen-features.c', native: true, build_by_default: false) @@ -32,22 +30,10 @@ s390x_softmmu_ss.add(files( 'cpu-sysemu.c', )) -# Newer kernels on s390 check for an S390_PGSTE program header and -# enable the pgste page table extensions in that case. This makes -# the vm.allocate_pgste sysctl unnecessary. We enable this program -# header if -# - we build on s390x -# - we build the system emulation for s390x (qemu-system-s390x) -# - KVM is enabled -# - the linker supports --s390-pgste -if host_machine.cpu_family() == 's390x' and cc.has_link_argument('-Wl,--s390-pgste') - s390x_softmmu_ss.add(when: 'CONFIG_KVM', - if_true: declare_dependency(link_args: ['-Wl,--s390-pgste'])) -endif - s390x_user_ss = ss.source_set() subdir('tcg') +subdir('kvm') target_arch += {'s390x': s390x_ss} target_softmmu_arch += {'s390x': s390x_softmmu_ss} diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c index 52fdd86c63..d779a9fc51 100644 --- a/target/s390x/mmu_helper.c +++ b/target/s390x/mmu_helper.c @@ -20,7 +20,7 @@ #include "exec/address-spaces.h" #include "cpu.h" #include "s390x-internal.h" -#include "kvm_s390x.h" +#include "kvm/kvm_s390x.h" #include "sysemu/kvm.h" #include "sysemu/tcg.h" #include "exec/exec-all.h" diff --git a/target/s390x/trace-events b/target/s390x/trace-events index e83a8cf85e..729cb012b4 100644 --- a/target/s390x/trace-events +++ b/target/s390x/trace-events @@ -10,12 +10,6 @@ ioinst_sch_id(const char *insn, int cssid, int ssid, int schid) "IOINST: %s (%x. ioinst_chp_id(const char *insn, int cssid, int chpid) "IOINST: %s (%x.%02x)" ioinst_chsc_cmd(uint16_t cmd, uint16_t len) "IOINST: chsc command 0x%04x, len 0x%04x" -# kvm.c -kvm_enable_cmma(int rc) "CMMA: enabling with result code %d" -kvm_clear_cmma(int rc) "CMMA: clearing with result code %d" -kvm_failed_cpu_state_set(int cpu_index, uint8_t state, const char *msg) "Warning: Unable to set cpu %d state %" PRIu8 " to KVM: %s" -kvm_assign_subch_ioeventfd(int fd, uint32_t addr, bool assign, int datamatch) "fd: %d sch: @0x%x assign: %d vq: %d" - # cpu-sysemu.c cpu_set_state(int cpu_index, uint8_t state) "setting cpu %d state to %" PRIu8 cpu_halt(int cpu_index) "halting cpu %d" From 7ab3eb42b0d795f7321c4fca0ea06cb76a005b04 Mon Sep 17 00:00:00 2001 From: "Cho, Yu-Chen" Date: Wed, 7 Jul 2021 18:53:24 +0800 Subject: [PATCH 040/272] target/s390x: split sysemu part of cpu models split sysemu part of cpu models, also create a tiny _user.c with just the (at least for now), empty implementation of apply_cpu_model. Signed-off-by: Claudio Fontana Signed-off-by: Cho, Yu-Chen Reviewed-by: Thomas Huth Message-Id: <20210707105324.23400-15-acho@suse.com> Signed-off-by: Cornelia Huck --- MAINTAINERS | 1 + target/s390x/cpu_models.c | 417 +----------------------------- target/s390x/cpu_models_sysemu.c | 426 +++++++++++++++++++++++++++++++ target/s390x/cpu_models_user.c | 20 ++ target/s390x/meson.build | 4 + target/s390x/s390x-internal.h | 2 + 6 files changed, 454 insertions(+), 416 deletions(-) create mode 100644 target/s390x/cpu_models_sysemu.c create mode 100644 target/s390x/cpu_models_user.c diff --git a/MAINTAINERS b/MAINTAINERS index 11d9ce72aa..af1edb3e6e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -298,6 +298,7 @@ M: David Hildenbrand S: Maintained F: target/s390x/ F: target/s390x/tcg +F: target/s390x/cpu_models_*.[ch] F: hw/s390x/ F: disas/s390.c F: tests/tcg/s390x/ diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 624c7ea971..4e4598cc77 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -18,18 +18,11 @@ #include "sysemu/tcg.h" #include "qapi/error.h" #include "qapi/visitor.h" -#include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/qemu-print.h" -#include "qapi/qmp/qerror.h" -#include "qapi/qobject-input-visitor.h" -#include "qapi/qmp/qdict.h" #ifndef CONFIG_USER_ONLY -#include "sysemu/arch_init.h" #include "sysemu/sysemu.h" -#include "hw/pci/pci.h" #endif -#include "qapi/qapi-commands-machine-target.h" #include "hw/s390x/pv.h" #define CPUDEF_INIT(_type, _gen, _ec_ga, _mha_pow, _hmfai, _name, _desc) \ @@ -416,381 +409,6 @@ void s390_cpu_list(void) } } -static S390CPUModel *get_max_cpu_model(Error **errp); - -#ifndef CONFIG_USER_ONLY -static void list_add_feat(const char *name, void *opaque); - -static void check_unavailable_features(const S390CPUModel *max_model, - const S390CPUModel *model, - strList **unavailable) -{ - S390FeatBitmap missing; - - /* check general model compatibility */ - if (max_model->def->gen < model->def->gen || - (max_model->def->gen == model->def->gen && - max_model->def->ec_ga < model->def->ec_ga)) { - list_add_feat("type", unavailable); - } - - /* detect missing features if any to properly report them */ - bitmap_andnot(missing, model->features, max_model->features, - S390_FEAT_MAX); - if (!bitmap_empty(missing, S390_FEAT_MAX)) { - s390_feat_bitmap_to_ascii(missing, unavailable, list_add_feat); - } -} - -struct CpuDefinitionInfoListData { - CpuDefinitionInfoList *list; - S390CPUModel *model; -}; - -static void create_cpu_model_list(ObjectClass *klass, void *opaque) -{ - struct CpuDefinitionInfoListData *cpu_list_data = opaque; - CpuDefinitionInfoList **cpu_list = &cpu_list_data->list; - CpuDefinitionInfo *info; - char *name = g_strdup(object_class_get_name(klass)); - S390CPUClass *scc = S390_CPU_CLASS(klass); - - /* strip off the -s390x-cpu */ - g_strrstr(name, "-" TYPE_S390_CPU)[0] = 0; - info = g_new0(CpuDefinitionInfo, 1); - info->name = name; - info->has_migration_safe = true; - info->migration_safe = scc->is_migration_safe; - info->q_static = scc->is_static; - info->q_typename = g_strdup(object_class_get_name(klass)); - /* check for unavailable features */ - if (cpu_list_data->model) { - Object *obj; - S390CPU *sc; - obj = object_new_with_class(klass); - sc = S390_CPU(obj); - if (sc->model) { - info->has_unavailable_features = true; - check_unavailable_features(cpu_list_data->model, sc->model, - &info->unavailable_features); - } - object_unref(obj); - } - - QAPI_LIST_PREPEND(*cpu_list, info); -} - -CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) -{ - struct CpuDefinitionInfoListData list_data = { - .list = NULL, - }; - - list_data.model = get_max_cpu_model(NULL); - - object_class_foreach(create_cpu_model_list, TYPE_S390_CPU, false, - &list_data); - - return list_data.list; -} - -static void cpu_model_from_info(S390CPUModel *model, const CpuModelInfo *info, - Error **errp) -{ - Error *err = NULL; - const QDict *qdict = NULL; - const QDictEntry *e; - Visitor *visitor; - ObjectClass *oc; - S390CPU *cpu; - Object *obj; - - if (info->props) { - qdict = qobject_to(QDict, info->props); - if (!qdict) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "props", "dict"); - return; - } - } - - oc = cpu_class_by_name(TYPE_S390_CPU, info->name); - if (!oc) { - error_setg(errp, "The CPU definition \'%s\' is unknown.", info->name); - return; - } - if (S390_CPU_CLASS(oc)->kvm_required && !kvm_enabled()) { - error_setg(errp, "The CPU definition '%s' requires KVM", info->name); - return; - } - obj = object_new_with_class(oc); - cpu = S390_CPU(obj); - - if (!cpu->model) { - error_setg(errp, "Details about the host CPU model are not available, " - "it cannot be used."); - object_unref(obj); - return; - } - - if (qdict) { - visitor = qobject_input_visitor_new(info->props); - if (!visit_start_struct(visitor, NULL, NULL, 0, errp)) { - visit_free(visitor); - object_unref(obj); - return; - } - for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) { - if (!object_property_set(obj, e->key, visitor, &err)) { - break; - } - } - if (!err) { - visit_check_struct(visitor, &err); - } - visit_end_struct(visitor, NULL); - visit_free(visitor); - if (err) { - error_propagate(errp, err); - object_unref(obj); - return; - } - } - - /* copy the model and throw the cpu away */ - memcpy(model, cpu->model, sizeof(*model)); - object_unref(obj); -} - -static void qdict_add_disabled_feat(const char *name, void *opaque) -{ - qdict_put_bool(opaque, name, false); -} - -static void qdict_add_enabled_feat(const char *name, void *opaque) -{ - qdict_put_bool(opaque, name, true); -} - -/* convert S390CPUDef into a static CpuModelInfo */ -static void cpu_info_from_model(CpuModelInfo *info, const S390CPUModel *model, - bool delta_changes) -{ - QDict *qdict = qdict_new(); - S390FeatBitmap bitmap; - - /* always fallback to the static base model */ - info->name = g_strdup_printf("%s-base", model->def->name); - - if (delta_changes) { - /* features deleted from the base feature set */ - bitmap_andnot(bitmap, model->def->base_feat, model->features, - S390_FEAT_MAX); - if (!bitmap_empty(bitmap, S390_FEAT_MAX)) { - s390_feat_bitmap_to_ascii(bitmap, qdict, qdict_add_disabled_feat); - } - - /* features added to the base feature set */ - bitmap_andnot(bitmap, model->features, model->def->base_feat, - S390_FEAT_MAX); - if (!bitmap_empty(bitmap, S390_FEAT_MAX)) { - s390_feat_bitmap_to_ascii(bitmap, qdict, qdict_add_enabled_feat); - } - } else { - /* expand all features */ - s390_feat_bitmap_to_ascii(model->features, qdict, - qdict_add_enabled_feat); - bitmap_complement(bitmap, model->features, S390_FEAT_MAX); - s390_feat_bitmap_to_ascii(bitmap, qdict, qdict_add_disabled_feat); - } - - if (!qdict_size(qdict)) { - qobject_unref(qdict); - } else { - info->props = QOBJECT(qdict); - info->has_props = true; - } -} - -CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, - CpuModelInfo *model, - Error **errp) -{ - Error *err = NULL; - CpuModelExpansionInfo *expansion_info = NULL; - S390CPUModel s390_model; - bool delta_changes = false; - - /* convert it to our internal representation */ - cpu_model_from_info(&s390_model, model, &err); - if (err) { - error_propagate(errp, err); - return NULL; - } - - if (type == CPU_MODEL_EXPANSION_TYPE_STATIC) { - delta_changes = true; - } else if (type != CPU_MODEL_EXPANSION_TYPE_FULL) { - error_setg(errp, "The requested expansion type is not supported."); - return NULL; - } - - /* convert it back to a static representation */ - expansion_info = g_new0(CpuModelExpansionInfo, 1); - expansion_info->model = g_malloc0(sizeof(*expansion_info->model)); - cpu_info_from_model(expansion_info->model, &s390_model, delta_changes); - return expansion_info; -} - -static void list_add_feat(const char *name, void *opaque) -{ - strList **last = (strList **) opaque; - - QAPI_LIST_PREPEND(*last, g_strdup(name)); -} - -CpuModelCompareInfo *qmp_query_cpu_model_comparison(CpuModelInfo *infoa, - CpuModelInfo *infob, - Error **errp) -{ - Error *err = NULL; - CpuModelCompareResult feat_result, gen_result; - CpuModelCompareInfo *compare_info; - S390FeatBitmap missing, added; - S390CPUModel modela, modelb; - - /* convert both models to our internal representation */ - cpu_model_from_info(&modela, infoa, &err); - if (err) { - error_propagate(errp, err); - return NULL; - } - cpu_model_from_info(&modelb, infob, &err); - if (err) { - error_propagate(errp, err); - return NULL; - } - compare_info = g_new0(CpuModelCompareInfo, 1); - - /* check the cpu generation and ga level */ - if (modela.def->gen == modelb.def->gen) { - if (modela.def->ec_ga == modelb.def->ec_ga) { - /* ec and corresponding bc are identical */ - gen_result = CPU_MODEL_COMPARE_RESULT_IDENTICAL; - } else if (modela.def->ec_ga < modelb.def->ec_ga) { - gen_result = CPU_MODEL_COMPARE_RESULT_SUBSET; - } else { - gen_result = CPU_MODEL_COMPARE_RESULT_SUPERSET; - } - } else if (modela.def->gen < modelb.def->gen) { - gen_result = CPU_MODEL_COMPARE_RESULT_SUBSET; - } else { - gen_result = CPU_MODEL_COMPARE_RESULT_SUPERSET; - } - if (gen_result != CPU_MODEL_COMPARE_RESULT_IDENTICAL) { - /* both models cannot be made identical */ - list_add_feat("type", &compare_info->responsible_properties); - } - - /* check the feature set */ - if (bitmap_equal(modela.features, modelb.features, S390_FEAT_MAX)) { - feat_result = CPU_MODEL_COMPARE_RESULT_IDENTICAL; - } else { - bitmap_andnot(missing, modela.features, modelb.features, S390_FEAT_MAX); - s390_feat_bitmap_to_ascii(missing, - &compare_info->responsible_properties, - list_add_feat); - bitmap_andnot(added, modelb.features, modela.features, S390_FEAT_MAX); - s390_feat_bitmap_to_ascii(added, &compare_info->responsible_properties, - list_add_feat); - if (bitmap_empty(missing, S390_FEAT_MAX)) { - feat_result = CPU_MODEL_COMPARE_RESULT_SUBSET; - } else if (bitmap_empty(added, S390_FEAT_MAX)) { - feat_result = CPU_MODEL_COMPARE_RESULT_SUPERSET; - } else { - feat_result = CPU_MODEL_COMPARE_RESULT_INCOMPATIBLE; - } - } - - /* combine the results */ - if (gen_result == feat_result) { - compare_info->result = gen_result; - } else if (feat_result == CPU_MODEL_COMPARE_RESULT_IDENTICAL) { - compare_info->result = gen_result; - } else if (gen_result == CPU_MODEL_COMPARE_RESULT_IDENTICAL) { - compare_info->result = feat_result; - } else { - compare_info->result = CPU_MODEL_COMPARE_RESULT_INCOMPATIBLE; - } - return compare_info; -} - -CpuModelBaselineInfo *qmp_query_cpu_model_baseline(CpuModelInfo *infoa, - CpuModelInfo *infob, - Error **errp) -{ - Error *err = NULL; - CpuModelBaselineInfo *baseline_info; - S390CPUModel modela, modelb, model; - uint16_t cpu_type; - uint8_t max_gen_ga; - uint8_t max_gen; - - /* convert both models to our internal representation */ - cpu_model_from_info(&modela, infoa, &err); - if (err) { - error_propagate(errp, err); - return NULL; - } - - cpu_model_from_info(&modelb, infob, &err); - if (err) { - error_propagate(errp, err); - return NULL; - } - - /* features both models support */ - bitmap_and(model.features, modela.features, modelb.features, S390_FEAT_MAX); - - /* detect the maximum model not regarding features */ - if (modela.def->gen == modelb.def->gen) { - if (modela.def->type == modelb.def->type) { - cpu_type = modela.def->type; - } else { - cpu_type = 0; - } - max_gen = modela.def->gen; - max_gen_ga = MIN(modela.def->ec_ga, modelb.def->ec_ga); - } else if (modela.def->gen > modelb.def->gen) { - cpu_type = modelb.def->type; - max_gen = modelb.def->gen; - max_gen_ga = modelb.def->ec_ga; - } else { - cpu_type = modela.def->type; - max_gen = modela.def->gen; - max_gen_ga = modela.def->ec_ga; - } - - model.def = s390_find_cpu_def(cpu_type, max_gen, max_gen_ga, - model.features); - - /* models without early base features (esan3) are bad */ - if (!model.def) { - error_setg(errp, "No compatible CPU model could be created as" - " important base features are disabled"); - return NULL; - } - - /* strip off features not part of the max model */ - bitmap_and(model.features, model.features, model.def->full_feat, - S390_FEAT_MAX); - - baseline_info = g_new0(CpuModelBaselineInfo, 1); - baseline_info->model = g_malloc0(sizeof(*baseline_info->model)); - cpu_info_from_model(baseline_info->model, &model, true); - return baseline_info; -} -#endif - static void check_consistency(const S390CPUModel *model) { static int dep[][2] = { @@ -906,7 +524,7 @@ static void check_compatibility(const S390CPUModel *max_model, "available in the configuration: "); } -static S390CPUModel *get_max_cpu_model(Error **errp) +S390CPUModel *get_max_cpu_model(Error **errp) { Error *err = NULL; static S390CPUModel max_model; @@ -931,39 +549,6 @@ static S390CPUModel *get_max_cpu_model(Error **errp) return &max_model; } -static inline void apply_cpu_model(const S390CPUModel *model, Error **errp) -{ -#ifndef CONFIG_USER_ONLY - Error *err = NULL; - static S390CPUModel applied_model; - static bool applied; - - /* - * We have the same model for all VCPUs. KVM can only be configured before - * any VCPUs are defined in KVM. - */ - if (applied) { - if (model && memcmp(&applied_model, model, sizeof(S390CPUModel))) { - error_setg(errp, "Mixed CPU models are not supported on s390x."); - } - return; - } - - if (kvm_enabled()) { - kvm_s390_apply_cpu_model(model, &err); - if (err) { - error_propagate(errp, err); - return; - } - } - - applied = true; - if (model) { - applied_model = *model; - } -#endif -} - void s390_realize_cpu_model(CPUState *cs, Error **errp) { Error *err = NULL; diff --git a/target/s390x/cpu_models_sysemu.c b/target/s390x/cpu_models_sysemu.c new file mode 100644 index 0000000000..05c3ccaaff --- /dev/null +++ b/target/s390x/cpu_models_sysemu.c @@ -0,0 +1,426 @@ +/* + * CPU models for s390x - System Emulation-only + * + * Copyright 2016 IBM Corp. + * + * Author(s): David Hildenbrand + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "s390x-internal.h" +#include "kvm/kvm_s390x.h" +#include "sysemu/kvm.h" +#include "sysemu/tcg.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "qapi/qmp/qerror.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qapi-commands-machine-target.h" + +static void list_add_feat(const char *name, void *opaque); + +static void check_unavailable_features(const S390CPUModel *max_model, + const S390CPUModel *model, + strList **unavailable) +{ + S390FeatBitmap missing; + + /* check general model compatibility */ + if (max_model->def->gen < model->def->gen || + (max_model->def->gen == model->def->gen && + max_model->def->ec_ga < model->def->ec_ga)) { + list_add_feat("type", unavailable); + } + + /* detect missing features if any to properly report them */ + bitmap_andnot(missing, model->features, max_model->features, + S390_FEAT_MAX); + if (!bitmap_empty(missing, S390_FEAT_MAX)) { + s390_feat_bitmap_to_ascii(missing, unavailable, list_add_feat); + } +} + +struct CpuDefinitionInfoListData { + CpuDefinitionInfoList *list; + S390CPUModel *model; +}; + +static void create_cpu_model_list(ObjectClass *klass, void *opaque) +{ + struct CpuDefinitionInfoListData *cpu_list_data = opaque; + CpuDefinitionInfoList **cpu_list = &cpu_list_data->list; + CpuDefinitionInfo *info; + char *name = g_strdup(object_class_get_name(klass)); + S390CPUClass *scc = S390_CPU_CLASS(klass); + + /* strip off the -s390x-cpu */ + g_strrstr(name, "-" TYPE_S390_CPU)[0] = 0; + info = g_new0(CpuDefinitionInfo, 1); + info->name = name; + info->has_migration_safe = true; + info->migration_safe = scc->is_migration_safe; + info->q_static = scc->is_static; + info->q_typename = g_strdup(object_class_get_name(klass)); + /* check for unavailable features */ + if (cpu_list_data->model) { + Object *obj; + S390CPU *sc; + obj = object_new_with_class(klass); + sc = S390_CPU(obj); + if (sc->model) { + info->has_unavailable_features = true; + check_unavailable_features(cpu_list_data->model, sc->model, + &info->unavailable_features); + } + object_unref(obj); + } + + QAPI_LIST_PREPEND(*cpu_list, info); +} + +CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) +{ + struct CpuDefinitionInfoListData list_data = { + .list = NULL, + }; + + list_data.model = get_max_cpu_model(NULL); + + object_class_foreach(create_cpu_model_list, TYPE_S390_CPU, false, + &list_data); + + return list_data.list; +} + +static void cpu_model_from_info(S390CPUModel *model, const CpuModelInfo *info, + Error **errp) +{ + Error *err = NULL; + const QDict *qdict = NULL; + const QDictEntry *e; + Visitor *visitor; + ObjectClass *oc; + S390CPU *cpu; + Object *obj; + + if (info->props) { + qdict = qobject_to(QDict, info->props); + if (!qdict) { + error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "props", "dict"); + return; + } + } + + oc = cpu_class_by_name(TYPE_S390_CPU, info->name); + if (!oc) { + error_setg(errp, "The CPU definition \'%s\' is unknown.", info->name); + return; + } + if (S390_CPU_CLASS(oc)->kvm_required && !kvm_enabled()) { + error_setg(errp, "The CPU definition '%s' requires KVM", info->name); + return; + } + obj = object_new_with_class(oc); + cpu = S390_CPU(obj); + + if (!cpu->model) { + error_setg(errp, "Details about the host CPU model are not available, " + "it cannot be used."); + object_unref(obj); + return; + } + + if (qdict) { + visitor = qobject_input_visitor_new(info->props); + if (!visit_start_struct(visitor, NULL, NULL, 0, errp)) { + visit_free(visitor); + object_unref(obj); + return; + } + for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) { + if (!object_property_set(obj, e->key, visitor, &err)) { + break; + } + } + if (!err) { + visit_check_struct(visitor, &err); + } + visit_end_struct(visitor, NULL); + visit_free(visitor); + if (err) { + error_propagate(errp, err); + object_unref(obj); + return; + } + } + + /* copy the model and throw the cpu away */ + memcpy(model, cpu->model, sizeof(*model)); + object_unref(obj); +} + +static void qdict_add_disabled_feat(const char *name, void *opaque) +{ + qdict_put_bool(opaque, name, false); +} + +static void qdict_add_enabled_feat(const char *name, void *opaque) +{ + qdict_put_bool(opaque, name, true); +} + +/* convert S390CPUDef into a static CpuModelInfo */ +static void cpu_info_from_model(CpuModelInfo *info, const S390CPUModel *model, + bool delta_changes) +{ + QDict *qdict = qdict_new(); + S390FeatBitmap bitmap; + + /* always fallback to the static base model */ + info->name = g_strdup_printf("%s-base", model->def->name); + + if (delta_changes) { + /* features deleted from the base feature set */ + bitmap_andnot(bitmap, model->def->base_feat, model->features, + S390_FEAT_MAX); + if (!bitmap_empty(bitmap, S390_FEAT_MAX)) { + s390_feat_bitmap_to_ascii(bitmap, qdict, qdict_add_disabled_feat); + } + + /* features added to the base feature set */ + bitmap_andnot(bitmap, model->features, model->def->base_feat, + S390_FEAT_MAX); + if (!bitmap_empty(bitmap, S390_FEAT_MAX)) { + s390_feat_bitmap_to_ascii(bitmap, qdict, qdict_add_enabled_feat); + } + } else { + /* expand all features */ + s390_feat_bitmap_to_ascii(model->features, qdict, + qdict_add_enabled_feat); + bitmap_complement(bitmap, model->features, S390_FEAT_MAX); + s390_feat_bitmap_to_ascii(bitmap, qdict, qdict_add_disabled_feat); + } + + if (!qdict_size(qdict)) { + qobject_unref(qdict); + } else { + info->props = QOBJECT(qdict); + info->has_props = true; + } +} + +CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, + CpuModelInfo *model, + Error **errp) +{ + Error *err = NULL; + CpuModelExpansionInfo *expansion_info = NULL; + S390CPUModel s390_model; + bool delta_changes = false; + + /* convert it to our internal representation */ + cpu_model_from_info(&s390_model, model, &err); + if (err) { + error_propagate(errp, err); + return NULL; + } + + if (type == CPU_MODEL_EXPANSION_TYPE_STATIC) { + delta_changes = true; + } else if (type != CPU_MODEL_EXPANSION_TYPE_FULL) { + error_setg(errp, "The requested expansion type is not supported."); + return NULL; + } + + /* convert it back to a static representation */ + expansion_info = g_new0(CpuModelExpansionInfo, 1); + expansion_info->model = g_malloc0(sizeof(*expansion_info->model)); + cpu_info_from_model(expansion_info->model, &s390_model, delta_changes); + return expansion_info; +} + +static void list_add_feat(const char *name, void *opaque) +{ + strList **last = (strList **) opaque; + + QAPI_LIST_PREPEND(*last, g_strdup(name)); +} + +CpuModelCompareInfo *qmp_query_cpu_model_comparison(CpuModelInfo *infoa, + CpuModelInfo *infob, + Error **errp) +{ + Error *err = NULL; + CpuModelCompareResult feat_result, gen_result; + CpuModelCompareInfo *compare_info; + S390FeatBitmap missing, added; + S390CPUModel modela, modelb; + + /* convert both models to our internal representation */ + cpu_model_from_info(&modela, infoa, &err); + if (err) { + error_propagate(errp, err); + return NULL; + } + cpu_model_from_info(&modelb, infob, &err); + if (err) { + error_propagate(errp, err); + return NULL; + } + compare_info = g_new0(CpuModelCompareInfo, 1); + + /* check the cpu generation and ga level */ + if (modela.def->gen == modelb.def->gen) { + if (modela.def->ec_ga == modelb.def->ec_ga) { + /* ec and corresponding bc are identical */ + gen_result = CPU_MODEL_COMPARE_RESULT_IDENTICAL; + } else if (modela.def->ec_ga < modelb.def->ec_ga) { + gen_result = CPU_MODEL_COMPARE_RESULT_SUBSET; + } else { + gen_result = CPU_MODEL_COMPARE_RESULT_SUPERSET; + } + } else if (modela.def->gen < modelb.def->gen) { + gen_result = CPU_MODEL_COMPARE_RESULT_SUBSET; + } else { + gen_result = CPU_MODEL_COMPARE_RESULT_SUPERSET; + } + if (gen_result != CPU_MODEL_COMPARE_RESULT_IDENTICAL) { + /* both models cannot be made identical */ + list_add_feat("type", &compare_info->responsible_properties); + } + + /* check the feature set */ + if (bitmap_equal(modela.features, modelb.features, S390_FEAT_MAX)) { + feat_result = CPU_MODEL_COMPARE_RESULT_IDENTICAL; + } else { + bitmap_andnot(missing, modela.features, modelb.features, S390_FEAT_MAX); + s390_feat_bitmap_to_ascii(missing, + &compare_info->responsible_properties, + list_add_feat); + bitmap_andnot(added, modelb.features, modela.features, S390_FEAT_MAX); + s390_feat_bitmap_to_ascii(added, &compare_info->responsible_properties, + list_add_feat); + if (bitmap_empty(missing, S390_FEAT_MAX)) { + feat_result = CPU_MODEL_COMPARE_RESULT_SUBSET; + } else if (bitmap_empty(added, S390_FEAT_MAX)) { + feat_result = CPU_MODEL_COMPARE_RESULT_SUPERSET; + } else { + feat_result = CPU_MODEL_COMPARE_RESULT_INCOMPATIBLE; + } + } + + /* combine the results */ + if (gen_result == feat_result) { + compare_info->result = gen_result; + } else if (feat_result == CPU_MODEL_COMPARE_RESULT_IDENTICAL) { + compare_info->result = gen_result; + } else if (gen_result == CPU_MODEL_COMPARE_RESULT_IDENTICAL) { + compare_info->result = feat_result; + } else { + compare_info->result = CPU_MODEL_COMPARE_RESULT_INCOMPATIBLE; + } + return compare_info; +} + +CpuModelBaselineInfo *qmp_query_cpu_model_baseline(CpuModelInfo *infoa, + CpuModelInfo *infob, + Error **errp) +{ + Error *err = NULL; + CpuModelBaselineInfo *baseline_info; + S390CPUModel modela, modelb, model; + uint16_t cpu_type; + uint8_t max_gen_ga; + uint8_t max_gen; + + /* convert both models to our internal representation */ + cpu_model_from_info(&modela, infoa, &err); + if (err) { + error_propagate(errp, err); + return NULL; + } + + cpu_model_from_info(&modelb, infob, &err); + if (err) { + error_propagate(errp, err); + return NULL; + } + + /* features both models support */ + bitmap_and(model.features, modela.features, modelb.features, S390_FEAT_MAX); + + /* detect the maximum model not regarding features */ + if (modela.def->gen == modelb.def->gen) { + if (modela.def->type == modelb.def->type) { + cpu_type = modela.def->type; + } else { + cpu_type = 0; + } + max_gen = modela.def->gen; + max_gen_ga = MIN(modela.def->ec_ga, modelb.def->ec_ga); + } else if (modela.def->gen > modelb.def->gen) { + cpu_type = modelb.def->type; + max_gen = modelb.def->gen; + max_gen_ga = modelb.def->ec_ga; + } else { + cpu_type = modela.def->type; + max_gen = modela.def->gen; + max_gen_ga = modela.def->ec_ga; + } + + model.def = s390_find_cpu_def(cpu_type, max_gen, max_gen_ga, + model.features); + + /* models without early base features (esan3) are bad */ + if (!model.def) { + error_setg(errp, "No compatible CPU model could be created as" + " important base features are disabled"); + return NULL; + } + + /* strip off features not part of the max model */ + bitmap_and(model.features, model.features, model.def->full_feat, + S390_FEAT_MAX); + + baseline_info = g_new0(CpuModelBaselineInfo, 1); + baseline_info->model = g_malloc0(sizeof(*baseline_info->model)); + cpu_info_from_model(baseline_info->model, &model, true); + return baseline_info; +} + +void apply_cpu_model(const S390CPUModel *model, Error **errp) +{ + Error *err = NULL; + static S390CPUModel applied_model; + static bool applied; + + /* + * We have the same model for all VCPUs. KVM can only be configured before + * any VCPUs are defined in KVM. + */ + if (applied) { + if (model && memcmp(&applied_model, model, sizeof(S390CPUModel))) { + error_setg(errp, "Mixed CPU models are not supported on s390x."); + } + return; + } + + if (kvm_enabled()) { + kvm_s390_apply_cpu_model(model, &err); + if (err) { + error_propagate(errp, err); + return; + } + } + + applied = true; + if (model) { + applied_model = *model; + } +} diff --git a/target/s390x/cpu_models_user.c b/target/s390x/cpu_models_user.c new file mode 100644 index 0000000000..df24d12d9e --- /dev/null +++ b/target/s390x/cpu_models_user.c @@ -0,0 +1,20 @@ +/* + * CPU models for s390x - User-mode + * + * Copyright 2016 IBM Corp. + * + * Author(s): David Hildenbrand + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "s390x-internal.h" +#include "qapi/error.h" + +void apply_cpu_model(const S390CPUModel *model, Error **errp) +{ +} diff --git a/target/s390x/meson.build b/target/s390x/meson.build index ec73bed524..84c1402a6a 100644 --- a/target/s390x/meson.build +++ b/target/s390x/meson.build @@ -28,9 +28,13 @@ s390x_softmmu_ss.add(files( 'mmu_helper.c', 'sigp.c', 'cpu-sysemu.c', + 'cpu_models_sysemu.c', )) s390x_user_ss = ss.source_set() +s390x_user_ss.add(files( + 'cpu_models_user.c', +)) subdir('tcg') subdir('kvm') diff --git a/target/s390x/s390x-internal.h b/target/s390x/s390x-internal.h index 17edd4d13b..5506f185e8 100644 --- a/target/s390x/s390x-internal.h +++ b/target/s390x/s390x-internal.h @@ -261,6 +261,8 @@ static inline void s390_cpu_unhalt(S390CPU *cpu) /* cpu_models.c */ void s390_cpu_model_class_register_props(ObjectClass *oc); void s390_realize_cpu_model(CPUState *cs, Error **errp); +S390CPUModel *get_max_cpu_model(Error **errp); +void apply_cpu_model(const S390CPUModel *model, Error **errp); ObjectClass *s390_cpu_class_by_name(const char *name); From 410bbee1b23faf25349355b13317078f5a73a441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 17 Jun 2021 13:53:20 +0200 Subject: [PATCH 041/272] hw/input/lm832x: Move lm832x_key_event() declaration to "lm832x.h" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit lm832x_key_event() is specific go LM832x devices, not to the I2C bus API. Move it out of "i2c.h" to a new header. Reviewed-by: Richard Henderson Acked-by: Corey Minyard Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Corey Minyard --- MAINTAINERS | 1 + hw/arm/nseries.c | 1 + hw/input/lm832x.c | 1 + include/hw/i2c/i2c.h | 3 --- include/hw/input/lm832x.h | 26 ++++++++++++++++++++++++++ 5 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 include/hw/input/lm832x.h diff --git a/MAINTAINERS b/MAINTAINERS index 2fde240be9..06517a18a7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -792,6 +792,7 @@ F: hw/input/tsc2005.c F: hw/misc/cbus.c F: hw/rtc/twl92230.c F: include/hw/display/blizzard.h +F: include/hw/input/lm832x.h F: include/hw/input/tsc2xxx.h F: include/hw/misc/cbus.h F: tests/acceptance/machine_arm_n8x0.py diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c index a10d8f53b5..92b6756fbc 100644 --- a/hw/arm/nseries.c +++ b/hw/arm/nseries.c @@ -34,6 +34,7 @@ #include "hw/boards.h" #include "hw/i2c/i2c.h" #include "hw/display/blizzard.h" +#include "hw/input/lm832x.h" #include "hw/input/tsc2xxx.h" #include "hw/misc/cbus.h" #include "hw/sensor/tmp105.h" diff --git a/hw/input/lm832x.c b/hw/input/lm832x.c index 4cb1e9de01..d2b92b457e 100644 --- a/hw/input/lm832x.c +++ b/hw/input/lm832x.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "hw/input/lm832x.h" #include "hw/i2c/i2c.h" #include "hw/irq.h" #include "migration/vmstate.h" diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h index ff4129ea70..850815e707 100644 --- a/include/hw/i2c/i2c.h +++ b/include/hw/i2c/i2c.h @@ -142,9 +142,6 @@ I2CSlave *i2c_slave_create_simple(I2CBus *bus, const char *name, uint8_t addr); */ bool i2c_slave_realize_and_unref(I2CSlave *dev, I2CBus *bus, Error **errp); -/* lm832x.c */ -void lm832x_key_event(DeviceState *dev, int key, int state); - extern const VMStateDescription vmstate_i2c_slave; #define VMSTATE_I2C_SLAVE(_field, _state) { \ diff --git a/include/hw/input/lm832x.h b/include/hw/input/lm832x.h new file mode 100644 index 0000000000..f47e579ff9 --- /dev/null +++ b/include/hw/input/lm832x.h @@ -0,0 +1,26 @@ +/* + * National Semiconductor LM8322/8323 GPIO keyboard & PWM chips. + * + * Copyright (C) 2008 Nokia Corporation + * Written by Andrzej Zaborowski + * + * 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 or + * (at your option) version 3 of the License. + * + * 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 . + */ + +#ifndef HW_INPUT_LM832X +#define HW_INPUT_LM832X + +void lm832x_key_event(DeviceState *dev, int key, int state); + +#endif From 426f53de9c2110b6c2a05fdc1a89f2e0c1a0b97e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 17 Jun 2021 13:53:21 +0200 Subject: [PATCH 042/272] hw/input/lm832x: Define TYPE_LM8323 in public header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define TYPE_LM8323 in the public "hw/input/lm832x.h" header and use it in hw/arm/nseries.c. Reviewed-by: Richard Henderson Acked-by: Corey Minyard Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Corey Minyard --- hw/arm/nseries.c | 2 +- hw/input/lm832x.c | 1 - include/hw/input/lm832x.h | 2 ++ 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c index 92b6756fbc..906c915df7 100644 --- a/hw/arm/nseries.c +++ b/hw/arm/nseries.c @@ -417,7 +417,7 @@ static void n810_kbd_setup(struct n800_s *s) /* Attach the LM8322 keyboard to the I2C bus, * should happen in n8x0_i2c_setup and s->kbd be initialised here. */ s->kbd = DEVICE(i2c_slave_create_simple(omap_i2c_bus(s->mpu->i2c[0]), - "lm8323", N810_LM8323_ADDR)); + TYPE_LM8323, N810_LM8323_ADDR)); qdev_connect_gpio_out(s->kbd, 0, kbd_irq); } diff --git a/hw/input/lm832x.c b/hw/input/lm832x.c index d2b92b457e..19a646d9bb 100644 --- a/hw/input/lm832x.c +++ b/hw/input/lm832x.c @@ -28,7 +28,6 @@ #include "ui/console.h" #include "qom/object.h" -#define TYPE_LM8323 "lm8323" OBJECT_DECLARE_SIMPLE_TYPE(LM823KbdState, LM8323) struct LM823KbdState { diff --git a/include/hw/input/lm832x.h b/include/hw/input/lm832x.h index f47e579ff9..2a58ccf891 100644 --- a/include/hw/input/lm832x.h +++ b/include/hw/input/lm832x.h @@ -21,6 +21,8 @@ #ifndef HW_INPUT_LM832X #define HW_INPUT_LM832X +#define TYPE_LM8323 "lm8323" + void lm832x_key_event(DeviceState *dev, int key, int state); #endif From e91113d0f9481c072f74e8390399e11b93856844 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 17 Jun 2021 13:53:22 +0200 Subject: [PATCH 043/272] hw/display/sm501: Simplify sm501_i2c_write() logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Acked-by: Corey Minyard Reviewed-by: BALATON Zoltan Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Corey Minyard --- hw/display/sm501.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 8789722ef2..f276276f7f 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -1036,8 +1036,9 @@ static void sm501_i2c_write(void *opaque, hwaddr addr, uint64_t value, int res = i2c_start_transfer(s->i2c_bus, s->i2c_addr >> 1, s->i2c_addr & 1); - s->i2c_status |= (res ? SM501_I2C_STATUS_ERROR : 0); - if (!res) { + if (res) { + s->i2c_status |= SM501_I2C_STATUS_ERROR; + } else { int i; for (i = 0; i <= s->i2c_byte_count; i++) { res = i2c_send_recv(s->i2c_bus, &s->i2c_data[i], From 4e7019bd483d1bcd480538138798740e851588b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 17 Jun 2021 13:53:23 +0200 Subject: [PATCH 044/272] hw/display/sm501: Replace i2c_send_recv() by i2c_recv() & i2c_send() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of using the confuse i2c_send_recv(), rewrite to directly call i2c_recv() & i2c_send(), resulting in code easier to review. Reviewed-by: Richard Henderson Acked-by: Corey Minyard Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Corey Minyard --- hw/display/sm501.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index f276276f7f..569661a074 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -1033,17 +1033,18 @@ static void sm501_i2c_write(void *opaque, hwaddr addr, uint64_t value, case SM501_I2C_CONTROL: if (value & SM501_I2C_CONTROL_ENABLE) { if (value & SM501_I2C_CONTROL_START) { + bool is_recv = s->i2c_addr & 1; int res = i2c_start_transfer(s->i2c_bus, s->i2c_addr >> 1, - s->i2c_addr & 1); + is_recv); if (res) { s->i2c_status |= SM501_I2C_STATUS_ERROR; } else { int i; for (i = 0; i <= s->i2c_byte_count; i++) { - res = i2c_send_recv(s->i2c_bus, &s->i2c_data[i], - !(s->i2c_addr & 1)); - if (res) { + if (is_recv) { + s->i2c_data[i] = i2c_recv(s->i2c_bus); + } else if (i2c_send(s->i2c_bus, s->i2c_data[i]) < 0) { s->i2c_status |= SM501_I2C_STATUS_ERROR; return; } From c699bf610bf814de478e81e0f1f375b386170ff5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 17 Jun 2021 13:53:24 +0200 Subject: [PATCH 045/272] hw/i2c/ppc4xx_i2c: Add reference to datasheet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It took me a while to find this model datasheet, since it is an OCR scan. Add a reference to save other developers time. Reviewed-by: Richard Henderson Acked-by: Corey Minyard Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Corey Minyard --- hw/i2c/ppc4xx_i2c.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/i2c/ppc4xx_i2c.c b/hw/i2c/ppc4xx_i2c.c index c0a8e04567..f4c5bc12d3 100644 --- a/hw/i2c/ppc4xx_i2c.c +++ b/hw/i2c/ppc4xx_i2c.c @@ -1,6 +1,8 @@ /* * PPC4xx I2C controller emulation * + * Documentation: PPC405GP User's Manual, Chapter 22. IIC Bus Interface + * * Copyright (c) 2007 Jocelyn Mayer * Copyright (c) 2012 François Revol * Copyright (c) 2016-2018 BALATON Zoltan From f8ffea755c3377b2c4b1800c1709ddd98b19882c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 17 Jun 2021 13:53:25 +0200 Subject: [PATCH 046/272] hw/i2c/ppc4xx_i2c: Replace i2c_send_recv() by i2c_recv() & i2c_send() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of using the confuse i2c_send_recv(), rewrite to directly call i2c_recv() & i2c_send(), resulting in code easier to review. Reviewed-by: Richard Henderson Acked-by: Corey Minyard Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Corey Minyard --- hw/i2c/ppc4xx_i2c.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/hw/i2c/ppc4xx_i2c.c b/hw/i2c/ppc4xx_i2c.c index f4c5bc12d3..75d50f1515 100644 --- a/hw/i2c/ppc4xx_i2c.c +++ b/hw/i2c/ppc4xx_i2c.c @@ -240,11 +240,14 @@ static void ppc4xx_i2c_writeb(void *opaque, hwaddr addr, uint64_t value, i2c->sts &= ~IIC_STS_ERR; } } - if (!(i2c->sts & IIC_STS_ERR) && - i2c_send_recv(i2c->bus, &i2c->mdata[i], !recv)) { - i2c->sts |= IIC_STS_ERR; - i2c->extsts |= IIC_EXTSTS_XFRA; - break; + if (!(i2c->sts & IIC_STS_ERR)) { + if (recv) { + i2c->mdata[i] = i2c_recv(i2c->bus); + } else if (i2c_send(i2c->bus, i2c->mdata[i]) < 0) { + i2c->sts |= IIC_STS_ERR; + i2c->extsts |= IIC_EXTSTS_XFRA; + break; + } } if (value & IIC_CNTL_RPST || !(value & IIC_CNTL_CHT)) { i2c_end_transfer(i2c->bus); From eb8377386d5b5ca3a844b87b5cbe1a6214f86c20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 17 Jun 2021 13:53:26 +0200 Subject: [PATCH 047/272] hw/misc/auxbus: Fix MOT/classic I2C mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since its introduction in commit 6fc7f77fd2 i2c_start_transfer() uses incorrectly the direction of the transfer (the last argument is called 'is_recv'). Fix by inverting the argument, we now have is_recv = !is_write. Fixes: 6fc7f77fd2 ("introduce aux-bus") Reported-by: BALATON Zoltan Reviewed-by: Richard Henderson Acked-by: Corey Minyard Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Corey Minyard --- hw/misc/auxbus.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/misc/auxbus.c b/hw/misc/auxbus.c index 6c099ae2a2..148b070ce4 100644 --- a/hw/misc/auxbus.c +++ b/hw/misc/auxbus.c @@ -139,7 +139,7 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, i2c_end_transfer(i2c_bus); } - if (i2c_start_transfer(i2c_bus, address, is_write)) { + if (i2c_start_transfer(i2c_bus, address, !is_write)) { ret = AUX_I2C_NACK; break; } @@ -170,7 +170,7 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, /* * No transactions started.. */ - if (i2c_start_transfer(i2c_bus, address, is_write)) { + if (i2c_start_transfer(i2c_bus, address, !is_write)) { break; } } else if ((address != bus->last_i2c_address) || @@ -179,7 +179,7 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, * Transaction started but we need to restart.. */ i2c_end_transfer(i2c_bus); - if (i2c_start_transfer(i2c_bus, address, is_write)) { + if (i2c_start_transfer(i2c_bus, address, !is_write)) { break; } } From 80675e193c2b99f148528be47b1ced86ac70ddbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 17 Jun 2021 13:53:27 +0200 Subject: [PATCH 048/272] hw/misc/auxbus: Explode READ_I2C / WRITE_I2C_MOT cases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To allow further simplifications in the following commits, start copying WRITE_I2C code to the READ_I2C, and READ_I2C_MOT to WRITE_I2C_MOT. No logical change. Reviewed-by: Richard Henderson Acked-by: Corey Minyard Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Corey Minyard --- hw/misc/auxbus.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/hw/misc/auxbus.c b/hw/misc/auxbus.c index 148b070ce4..9cc9cf3be3 100644 --- a/hw/misc/auxbus.c +++ b/hw/misc/auxbus.c @@ -133,6 +133,26 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, * Classic I2C transactions.. */ case READ_I2C: + is_write = cmd == READ_I2C ? false : true; + if (i2c_bus_busy(i2c_bus)) { + i2c_end_transfer(i2c_bus); + } + + if (i2c_start_transfer(i2c_bus, address, !is_write)) { + ret = AUX_I2C_NACK; + break; + } + + ret = AUX_I2C_ACK; + while (len > 0) { + if (i2c_send_recv(i2c_bus, data++, is_write) < 0) { + ret = AUX_I2C_NACK; + break; + } + len--; + } + i2c_end_transfer(i2c_bus); + break; case WRITE_I2C: is_write = cmd == READ_I2C ? false : true; if (i2c_bus_busy(i2c_bus)) { @@ -163,6 +183,39 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, * - We changed the address. */ case WRITE_I2C_MOT: + is_write = cmd == READ_I2C_MOT ? false : true; + ret = AUX_I2C_NACK; + if (!i2c_bus_busy(i2c_bus)) { + /* + * No transactions started.. + */ + if (i2c_start_transfer(i2c_bus, address, !is_write)) { + break; + } + } else if ((address != bus->last_i2c_address) || + (bus->last_transaction != cmd)) { + /* + * Transaction started but we need to restart.. + */ + i2c_end_transfer(i2c_bus); + if (i2c_start_transfer(i2c_bus, address, !is_write)) { + break; + } + } + + bus->last_transaction = cmd; + bus->last_i2c_address = address; + while (len > 0) { + if (i2c_send_recv(i2c_bus, data++, is_write) < 0) { + i2c_end_transfer(i2c_bus); + break; + } + len--; + } + if (len == 0) { + ret = AUX_I2C_ACK; + } + break; case READ_I2C_MOT: is_write = cmd == READ_I2C_MOT ? false : true; ret = AUX_I2C_NACK; From 4e367e65c29091951c9e94e108701003c6b61869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 17 Jun 2021 13:53:28 +0200 Subject: [PATCH 049/272] hw/misc/auxbus: Replace 'is_write' boolean by its value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the 'is_write' boolean by directly using its value in place. Reviewed-by: Richard Henderson Acked-by: Corey Minyard Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Corey Minyard --- hw/misc/auxbus.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/hw/misc/auxbus.c b/hw/misc/auxbus.c index 9cc9cf3be3..d96219aef8 100644 --- a/hw/misc/auxbus.c +++ b/hw/misc/auxbus.c @@ -106,7 +106,6 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, AUXReply ret = AUX_NACK; I2CBus *i2c_bus = aux_get_i2c_bus(bus); size_t i; - bool is_write = false; DPRINTF("request at address 0x%" PRIX32 ", command %u, len %u\n", address, cmd, len); @@ -117,11 +116,10 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, */ case WRITE_AUX: case READ_AUX: - is_write = cmd == READ_AUX ? false : true; for (i = 0; i < len; i++) { if (!address_space_rw(&bus->aux_addr_space, address++, MEMTXATTRS_UNSPECIFIED, data++, 1, - is_write)) { + cmd == WRITE_AUX)) { ret = AUX_I2C_ACK; } else { ret = AUX_NACK; @@ -133,19 +131,18 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, * Classic I2C transactions.. */ case READ_I2C: - is_write = cmd == READ_I2C ? false : true; if (i2c_bus_busy(i2c_bus)) { i2c_end_transfer(i2c_bus); } - if (i2c_start_transfer(i2c_bus, address, !is_write)) { + if (i2c_start_transfer(i2c_bus, address, true)) { ret = AUX_I2C_NACK; break; } ret = AUX_I2C_ACK; while (len > 0) { - if (i2c_send_recv(i2c_bus, data++, is_write) < 0) { + if (i2c_send_recv(i2c_bus, data++, false) < 0) { ret = AUX_I2C_NACK; break; } @@ -154,19 +151,18 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, i2c_end_transfer(i2c_bus); break; case WRITE_I2C: - is_write = cmd == READ_I2C ? false : true; if (i2c_bus_busy(i2c_bus)) { i2c_end_transfer(i2c_bus); } - if (i2c_start_transfer(i2c_bus, address, !is_write)) { + if (i2c_start_transfer(i2c_bus, address, false)) { ret = AUX_I2C_NACK; break; } ret = AUX_I2C_ACK; while (len > 0) { - if (i2c_send_recv(i2c_bus, data++, is_write) < 0) { + if (i2c_send_recv(i2c_bus, data++, true) < 0) { ret = AUX_I2C_NACK; break; } @@ -183,13 +179,12 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, * - We changed the address. */ case WRITE_I2C_MOT: - is_write = cmd == READ_I2C_MOT ? false : true; ret = AUX_I2C_NACK; if (!i2c_bus_busy(i2c_bus)) { /* * No transactions started.. */ - if (i2c_start_transfer(i2c_bus, address, !is_write)) { + if (i2c_start_transfer(i2c_bus, address, false)) { break; } } else if ((address != bus->last_i2c_address) || @@ -198,7 +193,7 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, * Transaction started but we need to restart.. */ i2c_end_transfer(i2c_bus); - if (i2c_start_transfer(i2c_bus, address, !is_write)) { + if (i2c_start_transfer(i2c_bus, address, false)) { break; } } @@ -206,7 +201,7 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, bus->last_transaction = cmd; bus->last_i2c_address = address; while (len > 0) { - if (i2c_send_recv(i2c_bus, data++, is_write) < 0) { + if (i2c_send_recv(i2c_bus, data++, true) < 0) { i2c_end_transfer(i2c_bus); break; } @@ -217,13 +212,12 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, } break; case READ_I2C_MOT: - is_write = cmd == READ_I2C_MOT ? false : true; ret = AUX_I2C_NACK; if (!i2c_bus_busy(i2c_bus)) { /* * No transactions started.. */ - if (i2c_start_transfer(i2c_bus, address, !is_write)) { + if (i2c_start_transfer(i2c_bus, address, true)) { break; } } else if ((address != bus->last_i2c_address) || @@ -232,7 +226,7 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, * Transaction started but we need to restart.. */ i2c_end_transfer(i2c_bus); - if (i2c_start_transfer(i2c_bus, address, !is_write)) { + if (i2c_start_transfer(i2c_bus, address, true)) { break; } } @@ -240,7 +234,7 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, bus->last_transaction = cmd; bus->last_i2c_address = address; while (len > 0) { - if (i2c_send_recv(i2c_bus, data++, is_write) < 0) { + if (i2c_send_recv(i2c_bus, data++, false) < 0) { i2c_end_transfer(i2c_bus); break; } From cbecd9f8224827a34857a650ddd9ea1ea2b1163f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 17 Jun 2021 13:53:29 +0200 Subject: [PATCH 050/272] hw/misc/auxbus: Replace i2c_send_recv() by i2c_recv() & i2c_send() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of using the confuse i2c_send_recv(), replace i2c_send_recv(send = true) by i2c_send() and i2c_send_recv(send = false) by i2c_recv(). During the replacement we also change a while() statement by for(). The resulting code is easier to review. Reviewed-by: Richard Henderson Acked-by: Corey Minyard Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Corey Minyard --- hw/misc/auxbus.c | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/hw/misc/auxbus.c b/hw/misc/auxbus.c index d96219aef8..44aa9730bc 100644 --- a/hw/misc/auxbus.c +++ b/hw/misc/auxbus.c @@ -141,12 +141,8 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, } ret = AUX_I2C_ACK; - while (len > 0) { - if (i2c_send_recv(i2c_bus, data++, false) < 0) { - ret = AUX_I2C_NACK; - break; - } - len--; + for (i = 0; i < len; i++) { + data[i] = i2c_recv(i2c_bus); } i2c_end_transfer(i2c_bus); break; @@ -161,12 +157,11 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, } ret = AUX_I2C_ACK; - while (len > 0) { - if (i2c_send_recv(i2c_bus, data++, true) < 0) { + for (i = 0; i < len; i++) { + if (i2c_send(i2c_bus, data[i]) < 0) { ret = AUX_I2C_NACK; break; } - len--; } i2c_end_transfer(i2c_bus); break; @@ -200,15 +195,13 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, bus->last_transaction = cmd; bus->last_i2c_address = address; - while (len > 0) { - if (i2c_send_recv(i2c_bus, data++, true) < 0) { + ret = AUX_I2C_ACK; + for (i = 0; i < len; i++) { + if (i2c_send(i2c_bus, data[i]) < 0) { i2c_end_transfer(i2c_bus); + ret = AUX_I2C_NACK; break; } - len--; - } - if (len == 0) { - ret = AUX_I2C_ACK; } break; case READ_I2C_MOT: @@ -233,16 +226,10 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, bus->last_transaction = cmd; bus->last_i2c_address = address; - while (len > 0) { - if (i2c_send_recv(i2c_bus, data++, false) < 0) { - i2c_end_transfer(i2c_bus); - break; - } - len--; - } - if (len == 0) { - ret = AUX_I2C_ACK; + for (i = 0; i < len; i++) { + data[i] = i2c_recv(i2c_bus); } + ret = AUX_I2C_ACK; break; default: qemu_log_mask(LOG_UNIMP, "AUX cmd=%u not implemented\n", cmd); From 2038a2907ce69f8b59e65ed8b4ac6f5c4f823fec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 17 Jun 2021 13:53:30 +0200 Subject: [PATCH 051/272] hw/i2c: Remove confusing i2c_send_recv() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We replaced all the i2c_send_recv() calls by the clearer i2c_recv() and i2c_send(), so we can remove this confusing API. Reviewed-by: Richard Henderson Acked-by: Corey Minyard Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Corey Minyard --- hw/i2c/core.c | 50 +++++++++++++++++++------------------------- include/hw/i2c/i2c.h | 1 - 2 files changed, 21 insertions(+), 30 deletions(-) diff --git a/hw/i2c/core.c b/hw/i2c/core.c index 3a7bae311d..27a66df7f3 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -188,50 +188,42 @@ void i2c_end_transfer(I2CBus *bus) bus->broadcast = false; } -int i2c_send_recv(I2CBus *bus, uint8_t *data, bool send) +int i2c_send(I2CBus *bus, uint8_t data) { I2CSlaveClass *sc; I2CSlave *s; I2CNode *node; int ret = 0; - if (send) { - QLIST_FOREACH(node, &bus->current_devs, next) { - s = node->elt; - sc = I2C_SLAVE_GET_CLASS(s); - if (sc->send) { - trace_i2c_send(s->address, *data); - ret = ret || sc->send(s, *data); - } else { - ret = -1; - } + QLIST_FOREACH(node, &bus->current_devs, next) { + s = node->elt; + sc = I2C_SLAVE_GET_CLASS(s); + if (sc->send) { + trace_i2c_send(s->address, data); + ret = ret || sc->send(s, data); + } else { + ret = -1; } - return ret ? -1 : 0; - } else { - ret = 0xff; - if (!QLIST_EMPTY(&bus->current_devs) && !bus->broadcast) { - sc = I2C_SLAVE_GET_CLASS(QLIST_FIRST(&bus->current_devs)->elt); - if (sc->recv) { - s = QLIST_FIRST(&bus->current_devs)->elt; - ret = sc->recv(s); - trace_i2c_recv(s->address, ret); - } - } - *data = ret; - return 0; } -} -int i2c_send(I2CBus *bus, uint8_t data) -{ - return i2c_send_recv(bus, &data, true); + return ret ? -1 : 0; } uint8_t i2c_recv(I2CBus *bus) { uint8_t data = 0xff; + I2CSlaveClass *sc; + I2CSlave *s; + + if (!QLIST_EMPTY(&bus->current_devs) && !bus->broadcast) { + sc = I2C_SLAVE_GET_CLASS(QLIST_FIRST(&bus->current_devs)->elt); + if (sc->recv) { + s = QLIST_FIRST(&bus->current_devs)->elt; + data = sc->recv(s); + trace_i2c_recv(s->address, data); + } + } - i2c_send_recv(bus, &data, false); return data; } diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h index 850815e707..99635b837a 100644 --- a/include/hw/i2c/i2c.h +++ b/include/hw/i2c/i2c.h @@ -84,7 +84,6 @@ int i2c_bus_busy(I2CBus *bus); int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv); void i2c_end_transfer(I2CBus *bus); void i2c_nack(I2CBus *bus); -int i2c_send_recv(I2CBus *bus, uint8_t *data, bool send); int i2c_send(I2CBus *bus, uint8_t data); uint8_t i2c_recv(I2CBus *bus); bool i2c_scan_bus(I2CBus *bus, uint8_t address, bool broadcast, From c8665a5997aa892c48f649df0aa72d0e41f8aca8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 17 Jun 2021 13:53:31 +0200 Subject: [PATCH 052/272] hw/i2c: Rename i2c_set_slave_address() -> i2c_slave_set_address() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Other functions from I2C slave API are named "i2c_slave_XXX()". Follow that pattern with set_address(). Add docstring along. No logical change. Patch created mechanically using: $ sed -i s/i2c_set_slave_address/i2c_slave_set_address/ \ $(git grep -l i2c_set_slave_address) Reviewed-by: Richard Henderson Acked-by: Corey Minyard Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Corey Minyard --- hw/arm/pxa2xx.c | 2 +- hw/arm/spitz.c | 4 ++-- hw/display/ati.c | 2 +- hw/display/sm501.c | 2 +- hw/display/xlnx_dp.c | 2 +- hw/i2c/core.c | 2 +- hw/i2c/imx_i2c.c | 2 +- include/hw/i2c/i2c.h | 8 +++++++- 8 files changed, 15 insertions(+), 9 deletions(-) diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index fdc4955e95..15a247efae 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -1437,7 +1437,7 @@ static void pxa2xx_i2c_write(void *opaque, hwaddr addr, break; case ISAR: - i2c_set_slave_address(I2C_SLAVE(s->slave), value & 0x7f); + i2c_slave_set_address(I2C_SLAVE(s->slave), value & 0x7f); break; case IDBR: diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c index 0e2626125e..5aab0b8565 100644 --- a/hw/arm/spitz.c +++ b/hw/arm/spitz.c @@ -769,9 +769,9 @@ static void spitz_wm8750_addr(void *opaque, int line, int level) { I2CSlave *wm = (I2CSlave *) opaque; if (level) - i2c_set_slave_address(wm, SPITZ_WM_ADDRH); + i2c_slave_set_address(wm, SPITZ_WM_ADDRH); else - i2c_set_slave_address(wm, SPITZ_WM_ADDRL); + i2c_slave_set_address(wm, SPITZ_WM_ADDRL); } static void spitz_i2c_setup(PXA2xxState *cpu) diff --git a/hw/display/ati.c b/hw/display/ati.c index 4c3ad8f47b..31f22754dc 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -968,7 +968,7 @@ static void ati_vga_realize(PCIDevice *dev, Error **errp) I2CBus *i2cbus = i2c_init_bus(DEVICE(s), "ati-vga.ddc"); bitbang_i2c_init(&s->bbi2c, i2cbus); I2CSlave *i2cddc = I2C_SLAVE(qdev_new(TYPE_I2CDDC)); - i2c_set_slave_address(i2cddc, 0x50); + i2c_slave_set_address(i2cddc, 0x50); qdev_realize_and_unref(DEVICE(i2cddc), BUS(i2cbus), &error_abort); /* mmio register space */ diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 569661a074..663c37e7f2 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -1828,7 +1828,7 @@ static void sm501_init(SM501State *s, DeviceState *dev, s->i2c_bus = i2c_init_bus(dev, "sm501.i2c"); /* ddc */ I2CDDCState *ddc = I2CDDC(qdev_new(TYPE_I2CDDC)); - i2c_set_slave_address(I2C_SLAVE(ddc), 0x50); + i2c_slave_set_address(I2C_SLAVE(ddc), 0x50); qdev_realize_and_unref(DEVICE(ddc), BUS(s->i2c_bus), &error_abort); /* mmio */ diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c index 4fd6aeb18b..2bb7a5441a 100644 --- a/hw/display/xlnx_dp.c +++ b/hw/display/xlnx_dp.c @@ -1253,7 +1253,7 @@ static void xlnx_dp_init(Object *obj) object_property_add_child(OBJECT(s), "dpcd", OBJECT(s->dpcd)); s->edid = I2CDDC(qdev_new("i2c-ddc")); - i2c_set_slave_address(I2C_SLAVE(s->edid), 0x50); + i2c_slave_set_address(I2C_SLAVE(s->edid), 0x50); object_property_add_child(OBJECT(s), "edid", OBJECT(s->edid)); fifo8_create(&s->rx_fifo, 16); diff --git a/hw/i2c/core.c b/hw/i2c/core.c index 27a66df7f3..6af24c9e79 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -66,7 +66,7 @@ I2CBus *i2c_init_bus(DeviceState *parent, const char *name) return bus; } -void i2c_set_slave_address(I2CSlave *dev, uint8_t address) +void i2c_slave_set_address(I2CSlave *dev, uint8_t address) { dev->address = address; } diff --git a/hw/i2c/imx_i2c.c b/hw/i2c/imx_i2c.c index 2e02e1c4fa..9792583fea 100644 --- a/hw/i2c/imx_i2c.c +++ b/hw/i2c/imx_i2c.c @@ -171,7 +171,7 @@ static void imx_i2c_write(void *opaque, hwaddr offset, switch (offset) { case IADR_ADDR: s->iadr = value & IADR_MASK; - /* i2c_set_slave_address(s->bus, (uint8_t)s->iadr); */ + /* i2c_slave_set_address(s->bus, (uint8_t)s->iadr); */ break; case IFDR_ADDR: s->ifdr = value & IFDR_MASK; diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h index 99635b837a..2adf521b27 100644 --- a/include/hw/i2c/i2c.h +++ b/include/hw/i2c/i2c.h @@ -79,7 +79,6 @@ struct I2CBus { }; I2CBus *i2c_init_bus(DeviceState *parent, const char *name); -void i2c_set_slave_address(I2CSlave *dev, uint8_t address); int i2c_bus_busy(I2CBus *bus); int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv); void i2c_end_transfer(I2CBus *bus); @@ -141,6 +140,13 @@ I2CSlave *i2c_slave_create_simple(I2CBus *bus, const char *name, uint8_t addr); */ bool i2c_slave_realize_and_unref(I2CSlave *dev, I2CBus *bus, Error **errp); +/** + * Set the I2C bus address of a slave device + * @dev: I2C slave device + * @address: I2C address of the slave when put on a bus + */ +void i2c_slave_set_address(I2CSlave *dev, uint8_t address); + extern const VMStateDescription vmstate_i2c_slave; #define VMSTATE_I2C_SLAVE(_field, _state) { \ From e656e387973b1c11f1b2b8c073a4ab1ed33504a2 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Thu, 17 Jun 2021 13:53:32 +0200 Subject: [PATCH 053/272] hw/i2c: Make i2c_start_transfer() direction argument a boolean MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the argument representing the direction of the transfer a boolean type. Rename the boolean argument as 'is_recv' to match i2c_recv_send(). Document the function prototype. Signed-off-by: BALATON Zoltan Message-Id: <20200621145235.9E241745712@zero.eik.bme.hu> [PMD: Split patch, added docstring] Reviewed-by: Richard Henderson Acked-by: Corey Minyard Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Corey Minyard --- hw/i2c/core.c | 4 ++-- include/hw/i2c/i2c.h | 12 +++++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/hw/i2c/core.c b/hw/i2c/core.c index 6af24c9e79..6639ca8c2e 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -115,7 +115,7 @@ bool i2c_scan_bus(I2CBus *bus, uint8_t address, bool broadcast, * without releasing the bus. If that fails, the bus is still * in a transaction. */ -int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv) +int i2c_start_transfer(I2CBus *bus, uint8_t address, bool is_recv) { I2CSlaveClass *sc; I2CNode *node; @@ -157,7 +157,7 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv) if (sc->event) { trace_i2c_event("start", s->address); - rv = sc->event(s, recv ? I2C_START_RECV : I2C_START_SEND); + rv = sc->event(s, is_recv ? I2C_START_RECV : I2C_START_SEND); if (rv && !bus->broadcast) { if (bus_scanned) { /* First call, terminate the transfer. */ diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h index 2adf521b27..21f2dba1bf 100644 --- a/include/hw/i2c/i2c.h +++ b/include/hw/i2c/i2c.h @@ -80,7 +80,17 @@ struct I2CBus { I2CBus *i2c_init_bus(DeviceState *parent, const char *name); int i2c_bus_busy(I2CBus *bus); -int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv); + +/** + * i2c_start_transfer: start a transfer on an I2C bus. + * + * @bus: #I2CBus to be used + * @address: address of the slave + * @is_recv: indicates the transfer direction + * + * Returns: 0 on success, -1 on error + */ +int i2c_start_transfer(I2CBus *bus, uint8_t address, bool is_recv); void i2c_end_transfer(I2CBus *bus); void i2c_nack(I2CBus *bus); int i2c_send(I2CBus *bus, uint8_t data); From 265caf45c6157f6b23f16292152ed9da5d2d1982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 17 Jun 2021 13:53:33 +0200 Subject: [PATCH 054/272] hw/i2c: Extract i2c_do_start_transfer() from i2c_start_transfer() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To allow further simplications, extract i2c_do_start_transfer() from i2c_start_transfer(). This is mostly the same function, but the former is static and takes an enum argument. Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Corey Minyard --- hw/i2c/core.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/hw/i2c/core.c b/hw/i2c/core.c index 6639ca8c2e..5483bf95a3 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -114,8 +114,11 @@ bool i2c_scan_bus(I2CBus *bus, uint8_t address, bool broadcast, * protocol uses a start transfer to switch from write to read mode * without releasing the bus. If that fails, the bus is still * in a transaction. + * + * @event must be I2C_START_RECV or I2C_START_SEND. */ -int i2c_start_transfer(I2CBus *bus, uint8_t address, bool is_recv) +static int i2c_do_start_transfer(I2CBus *bus, uint8_t address, + enum i2c_event event) { I2CSlaveClass *sc; I2CNode *node; @@ -157,7 +160,7 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, bool is_recv) if (sc->event) { trace_i2c_event("start", s->address); - rv = sc->event(s, is_recv ? I2C_START_RECV : I2C_START_SEND); + rv = sc->event(s, event); if (rv && !bus->broadcast) { if (bus_scanned) { /* First call, terminate the transfer. */ @@ -170,6 +173,13 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, bool is_recv) return 0; } +int i2c_start_transfer(I2CBus *bus, uint8_t address, bool is_recv) +{ + return i2c_do_start_transfer(bus, address, is_recv + ? I2C_START_RECV + : I2C_START_SEND); +} + void i2c_end_transfer(I2CBus *bus) { I2CSlaveClass *sc; From 90603c5b894eae0e4c8a4a6fdde622143142489c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 17 Jun 2021 13:53:34 +0200 Subject: [PATCH 055/272] hw/i2c: Introduce i2c_start_recv() and i2c_start_send() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To ease reviewing code using the I2C bus API, introduce the i2c_start_recv() and i2c_start_send() helpers which don't take the confusing 'is_recv' boolean argument. Use these new helpers in the SMBus / AUX bus models. Suggested-by: Richard Henderson Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Corey Minyard --- hw/i2c/core.c | 10 ++++++++++ hw/i2c/pm_smbus.c | 4 ++-- hw/i2c/smbus_master.c | 22 +++++++++++----------- hw/misc/auxbus.c | 12 ++++++------ include/hw/i2c/i2c.h | 24 ++++++++++++++++++++++++ 5 files changed, 53 insertions(+), 19 deletions(-) diff --git a/hw/i2c/core.c b/hw/i2c/core.c index 5483bf95a3..416372ad00 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -180,6 +180,16 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, bool is_recv) : I2C_START_SEND); } +int i2c_start_recv(I2CBus *bus, uint8_t address) +{ + return i2c_do_start_transfer(bus, address, I2C_START_RECV); +} + +int i2c_start_send(I2CBus *bus, uint8_t address) +{ + return i2c_do_start_transfer(bus, address, I2C_START_SEND); +} + void i2c_end_transfer(I2CBus *bus) { I2CSlaveClass *sc; diff --git a/hw/i2c/pm_smbus.c b/hw/i2c/pm_smbus.c index 06e1e5321b..d7eae548cb 100644 --- a/hw/i2c/pm_smbus.c +++ b/hw/i2c/pm_smbus.c @@ -128,14 +128,14 @@ static void smb_transaction(PMSMBus *s) * So at least Linux may or may not set the read bit here. * So just ignore the read bit for this command. */ - if (i2c_start_transfer(bus, addr, 0)) { + if (i2c_start_send(bus, addr)) { goto error; } ret = i2c_send(bus, s->smb_data1); if (ret) { goto error; } - if (i2c_start_transfer(bus, addr, 1)) { + if (i2c_start_recv(bus, addr)) { goto error; } s->in_i2c_block_read = true; diff --git a/hw/i2c/smbus_master.c b/hw/i2c/smbus_master.c index dc43b8637d..6a53c34e70 100644 --- a/hw/i2c/smbus_master.c +++ b/hw/i2c/smbus_master.c @@ -29,7 +29,7 @@ int smbus_receive_byte(I2CBus *bus, uint8_t addr) { uint8_t data; - if (i2c_start_transfer(bus, addr, 1)) { + if (i2c_start_recv(bus, addr)) { return -1; } data = i2c_recv(bus); @@ -40,7 +40,7 @@ int smbus_receive_byte(I2CBus *bus, uint8_t addr) int smbus_send_byte(I2CBus *bus, uint8_t addr, uint8_t data) { - if (i2c_start_transfer(bus, addr, 0)) { + if (i2c_start_send(bus, addr)) { return -1; } i2c_send(bus, data); @@ -51,11 +51,11 @@ int smbus_send_byte(I2CBus *bus, uint8_t addr, uint8_t data) int smbus_read_byte(I2CBus *bus, uint8_t addr, uint8_t command) { uint8_t data; - if (i2c_start_transfer(bus, addr, 0)) { + if (i2c_start_send(bus, addr)) { return -1; } i2c_send(bus, command); - if (i2c_start_transfer(bus, addr, 1)) { + if (i2c_start_recv(bus, addr)) { i2c_end_transfer(bus); return -1; } @@ -67,7 +67,7 @@ int smbus_read_byte(I2CBus *bus, uint8_t addr, uint8_t command) int smbus_write_byte(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t data) { - if (i2c_start_transfer(bus, addr, 0)) { + if (i2c_start_send(bus, addr)) { return -1; } i2c_send(bus, command); @@ -79,11 +79,11 @@ int smbus_write_byte(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t data) int smbus_read_word(I2CBus *bus, uint8_t addr, uint8_t command) { uint16_t data; - if (i2c_start_transfer(bus, addr, 0)) { + if (i2c_start_send(bus, addr)) { return -1; } i2c_send(bus, command); - if (i2c_start_transfer(bus, addr, 1)) { + if (i2c_start_recv(bus, addr)) { i2c_end_transfer(bus); return -1; } @@ -96,7 +96,7 @@ int smbus_read_word(I2CBus *bus, uint8_t addr, uint8_t command) int smbus_write_word(I2CBus *bus, uint8_t addr, uint8_t command, uint16_t data) { - if (i2c_start_transfer(bus, addr, 0)) { + if (i2c_start_send(bus, addr)) { return -1; } i2c_send(bus, command); @@ -113,12 +113,12 @@ int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, int i; if (send_cmd) { - if (i2c_start_transfer(bus, addr, 0)) { + if (i2c_start_send(bus, addr)) { return -1; } i2c_send(bus, command); } - if (i2c_start_transfer(bus, addr, 1)) { + if (i2c_start_recv(bus, addr)) { if (send_cmd) { i2c_end_transfer(bus); } @@ -149,7 +149,7 @@ int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, len = 32; } - if (i2c_start_transfer(bus, addr, 0)) { + if (i2c_start_send(bus, addr)) { return -1; } i2c_send(bus, command); diff --git a/hw/misc/auxbus.c b/hw/misc/auxbus.c index 44aa9730bc..434ff8d910 100644 --- a/hw/misc/auxbus.c +++ b/hw/misc/auxbus.c @@ -135,7 +135,7 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, i2c_end_transfer(i2c_bus); } - if (i2c_start_transfer(i2c_bus, address, true)) { + if (i2c_start_recv(i2c_bus, address)) { ret = AUX_I2C_NACK; break; } @@ -151,7 +151,7 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, i2c_end_transfer(i2c_bus); } - if (i2c_start_transfer(i2c_bus, address, false)) { + if (i2c_start_send(i2c_bus, address)) { ret = AUX_I2C_NACK; break; } @@ -179,7 +179,7 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, /* * No transactions started.. */ - if (i2c_start_transfer(i2c_bus, address, false)) { + if (i2c_start_send(i2c_bus, address)) { break; } } else if ((address != bus->last_i2c_address) || @@ -188,7 +188,7 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, * Transaction started but we need to restart.. */ i2c_end_transfer(i2c_bus); - if (i2c_start_transfer(i2c_bus, address, false)) { + if (i2c_start_send(i2c_bus, address)) { break; } } @@ -210,7 +210,7 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, /* * No transactions started.. */ - if (i2c_start_transfer(i2c_bus, address, true)) { + if (i2c_start_recv(i2c_bus, address)) { break; } } else if ((address != bus->last_i2c_address) || @@ -219,7 +219,7 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, * Transaction started but we need to restart.. */ i2c_end_transfer(i2c_bus); - if (i2c_start_transfer(i2c_bus, address, true)) { + if (i2c_start_recv(i2c_bus, address)) { break; } } diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h index 21f2dba1bf..5ca3b708c0 100644 --- a/include/hw/i2c/i2c.h +++ b/include/hw/i2c/i2c.h @@ -88,9 +88,33 @@ int i2c_bus_busy(I2CBus *bus); * @address: address of the slave * @is_recv: indicates the transfer direction * + * When @is_recv is a known boolean constant, use the + * i2c_start_recv() or i2c_start_send() helper instead. + * * Returns: 0 on success, -1 on error */ int i2c_start_transfer(I2CBus *bus, uint8_t address, bool is_recv); + +/** + * i2c_start_recv: start a 'receive' transfer on an I2C bus. + * + * @bus: #I2CBus to be used + * @address: address of the slave + * + * Returns: 0 on success, -1 on error + */ +int i2c_start_recv(I2CBus *bus, uint8_t address); + +/** + * i2c_start_send: start a 'send' transfer on an I2C bus. + * + * @bus: #I2CBus to be used + * @address: address of the slave + * + * Returns: 0 on success, -1 on error + */ +int i2c_start_send(I2CBus *bus, uint8_t address); + void i2c_end_transfer(I2CBus *bus); void i2c_nack(I2CBus *bus); int i2c_send(I2CBus *bus, uint8_t data); From 9e7449901d33ed0ddc0c432b15896019e3aec4f1 Mon Sep 17 00:00:00 2001 From: Jinhua Cao Date: Fri, 25 Jun 2021 10:12:32 +0800 Subject: [PATCH 056/272] ipmi/sim: fix watchdog_expired data type error in IPMIBmcSim struct 1) watchdog_expired is set bool which value could only be 0 or 1, but watchdog_expired every bit mean different Timer Use. 2) Use the command -ipmitool mc get watchdog- to query ipmi-watchdog status in guest. ... [root@localhost ~]# ipmitool mc watchdog get Watchdog Timer Use: SMS/OS (0x44) Watchdog Timer Is: Started/Running Watchdog Timer Actions: Hard Reset (0x01) Pre-timeout interval: 0 seconds Timer Expiration Flags: 0x00 Initial Countdown: 60 sec Present Countdown: 57 sec ... bool for watchdog_expired results -Timer Expiration Flags- always be 0x00 or 0x01, but the -Timer Expiration Flags- indicts the Timer Use after timeout. So change watchdog_expired data type from bool to uint8_t to fix this problem. Signed-off-by: Jinhua Cao Message-Id: <20210625021232.73614-1-caojinhua1@huawei.com> [I checked, a bool and uint8 are the same size for the vmstate transfer, so this should be fine.] Signed-off-by: Corey Minyard --- hw/ipmi/ipmi_bmc_sim.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index 55fb81fa5a..905e091094 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -189,7 +189,7 @@ struct IPMIBmcSim { uint8_t watchdog_use; uint8_t watchdog_action; uint8_t watchdog_pretimeout; /* In seconds */ - bool watchdog_expired; + uint8_t watchdog_expired; uint16_t watchdog_timeout; /* in 100's of milliseconds */ bool watchdog_running; @@ -2110,7 +2110,7 @@ static const VMStateDescription vmstate_ipmi_sim = { VMSTATE_UINT8(watchdog_use, IPMIBmcSim), VMSTATE_UINT8(watchdog_action, IPMIBmcSim), VMSTATE_UINT8(watchdog_pretimeout, IPMIBmcSim), - VMSTATE_BOOL(watchdog_expired, IPMIBmcSim), + VMSTATE_UINT8(watchdog_expired, IPMIBmcSim), VMSTATE_UINT16(watchdog_timeout, IPMIBmcSim), VMSTATE_BOOL(watchdog_running, IPMIBmcSim), VMSTATE_BOOL(watchdog_preaction_ran, IPMIBmcSim), From 3746d5c15e70570be265e55c838429db97ef94ab Mon Sep 17 00:00:00 2001 From: Titus Rwantare Date: Thu, 8 Jul 2021 10:25:52 -0700 Subject: [PATCH 057/272] hw/i2c: add support for PMBus QEMU has support for SMBus devices, and PMBus is a more specific implementation of SMBus. The additions made in this commit makes it easier to add new PMBus devices to QEMU. https://pmbus.org/specification-archives/ Reviewed-by: Joel Stanley Reviewed-by: Hao Wu Signed-off-by: Titus Rwantare Message-Id: <20210708172556.1868139-2-titusr@google.com> Signed-off-by: Corey Minyard --- hw/arm/Kconfig | 1 + hw/i2c/Kconfig | 4 + hw/i2c/meson.build | 1 + hw/i2c/pmbus_device.c | 1612 +++++++++++++++++++++++++++++++++ include/hw/i2c/pmbus_device.h | 517 +++++++++++ 5 files changed, 2135 insertions(+) create mode 100644 hw/i2c/pmbus_device.c create mode 100644 include/hw/i2c/pmbus_device.h diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 647b5c8b43..2ed1b09510 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -375,6 +375,7 @@ config NPCM7XX select ARM_GIC select AT24C # EEPROM select PL310 # cache controller + select PMBUS select SERIAL select SSI select UNIMP diff --git a/hw/i2c/Kconfig b/hw/i2c/Kconfig index 8d120a25d5..8217cb5041 100644 --- a/hw/i2c/Kconfig +++ b/hw/i2c/Kconfig @@ -32,3 +32,7 @@ config MPC_I2C config PCA954X bool select I2C + +config PMBUS + bool + select SMBUS diff --git a/hw/i2c/meson.build b/hw/i2c/meson.build index dd3aef02b2..d3df273251 100644 --- a/hw/i2c/meson.build +++ b/hw/i2c/meson.build @@ -15,4 +15,5 @@ i2c_ss.add(when: 'CONFIG_VERSATILE_I2C', if_true: files('versatile_i2c.c')) i2c_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_i2c.c')) i2c_ss.add(when: 'CONFIG_PPC4XX', if_true: files('ppc4xx_i2c.c')) i2c_ss.add(when: 'CONFIG_PCA954X', if_true: files('i2c_mux_pca954x.c')) +i2c_ss.add(when: 'CONFIG_PMBUS', if_true: files('pmbus_device.c')) softmmu_ss.add_all(when: 'CONFIG_I2C', if_true: i2c_ss) diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c new file mode 100644 index 0000000000..24f8f522d9 --- /dev/null +++ b/hw/i2c/pmbus_device.c @@ -0,0 +1,1612 @@ +/* + * PMBus wrapper over SMBus + * + * Copyright 2021 Google LLC + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include +#include +#include "hw/i2c/pmbus_device.h" +#include "migration/vmstate.h" +#include "qemu/module.h" +#include "qemu/log.h" + +uint16_t pmbus_data2direct_mode(PMBusCoefficients c, uint32_t value) +{ + /* R is usually negative to fit large readings into 16 bits */ + uint16_t y = (c.m * value + c.b) * pow(10, c.R); + return y; +} + +uint32_t pmbus_direct_mode2data(PMBusCoefficients c, uint16_t value) +{ + /* X = (Y * 10^-R - b) / m */ + uint32_t x = (value / pow(10, c.R) - c.b) / c.m; + return x; +} + +void pmbus_send(PMBusDevice *pmdev, const uint8_t *data, uint16_t len) +{ + if (pmdev->out_buf_len + len > SMBUS_DATA_MAX_LEN) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMBus device tried to send too much data"); + len = 0; + } + + for (int i = len - 1; i >= 0; i--) { + pmdev->out_buf[i + pmdev->out_buf_len] = data[len - i - 1]; + } + pmdev->out_buf_len += len; +} + +/* Internal only, convert unsigned ints to the little endian bus */ +static void pmbus_send_uint(PMBusDevice *pmdev, uint64_t data, uint8_t size) +{ + uint8_t bytes[8]; + g_assert(size <= 8); + + for (int i = 0; i < size; i++) { + bytes[i] = data & 0xFF; + data = data >> 8; + } + pmbus_send(pmdev, bytes, size); +} + +void pmbus_send8(PMBusDevice *pmdev, uint8_t data) +{ + pmbus_send_uint(pmdev, data, 1); +} + +void pmbus_send16(PMBusDevice *pmdev, uint16_t data) +{ + pmbus_send_uint(pmdev, data, 2); +} + +void pmbus_send32(PMBusDevice *pmdev, uint32_t data) +{ + pmbus_send_uint(pmdev, data, 4); +} + +void pmbus_send64(PMBusDevice *pmdev, uint64_t data) +{ + pmbus_send_uint(pmdev, data, 8); +} + +void pmbus_send_string(PMBusDevice *pmdev, const char *data) +{ + size_t len = strlen(data); + g_assert(len > 0); + g_assert(len + pmdev->out_buf_len < SMBUS_DATA_MAX_LEN); + pmdev->out_buf[len + pmdev->out_buf_len] = len; + + for (int i = len - 1; i >= 0; i--) { + pmdev->out_buf[i + pmdev->out_buf_len] = data[len - 1 - i]; + } + pmdev->out_buf_len += len + 1; +} + + +static uint64_t pmbus_receive_uint(const uint8_t *buf, uint8_t len) +{ + uint64_t ret = 0; + + /* Exclude command code from return value */ + buf++; + len--; + + for (int i = len - 1; i >= 0; i--) { + ret = ret << 8 | buf[i]; + } + return ret; +} + +uint8_t pmbus_receive8(PMBusDevice *pmdev) +{ + if (pmdev->in_buf_len - 1 != 1) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: length mismatch. Expected 1 byte, got %d bytes\n", + __func__, pmdev->in_buf_len - 1); + } + return pmbus_receive_uint(pmdev->in_buf, pmdev->in_buf_len); +} + +uint16_t pmbus_receive16(PMBusDevice *pmdev) +{ + if (pmdev->in_buf_len - 1 != 2) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: length mismatch. Expected 2 bytes, got %d bytes\n", + __func__, pmdev->in_buf_len - 1); + } + return pmbus_receive_uint(pmdev->in_buf, pmdev->in_buf_len); +} + +uint32_t pmbus_receive32(PMBusDevice *pmdev) +{ + if (pmdev->in_buf_len - 1 != 4) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: length mismatch. Expected 4 bytes, got %d bytes\n", + __func__, pmdev->in_buf_len - 1); + } + return pmbus_receive_uint(pmdev->in_buf, pmdev->in_buf_len); +} + +uint64_t pmbus_receive64(PMBusDevice *pmdev) +{ + if (pmdev->in_buf_len - 1 != 8) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: length mismatch. Expected 8 bytes, got %d bytes\n", + __func__, pmdev->in_buf_len - 1); + } + return pmbus_receive_uint(pmdev->in_buf, pmdev->in_buf_len); +} + +static uint8_t pmbus_out_buf_pop(PMBusDevice *pmdev) +{ + if (pmdev->out_buf_len == 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: tried to read from empty buffer", + __func__); + return 0xFF; + } + uint8_t data = pmdev->out_buf[pmdev->out_buf_len - 1]; + pmdev->out_buf_len--; + return data; +} + +static void pmbus_quick_cmd(SMBusDevice *smd, uint8_t read) +{ + PMBusDevice *pmdev = PMBUS_DEVICE(smd); + PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev); + + if (pmdc->quick_cmd) { + pmdc->quick_cmd(pmdev, read); + } +} + +static void pmbus_pages_alloc(PMBusDevice *pmdev) +{ + /* some PMBus devices don't use the PAGE command, so they get 1 page */ + PMBusDeviceClass *k = PMBUS_DEVICE_GET_CLASS(pmdev); + if (k->device_num_pages == 0) { + k->device_num_pages = 1; + } + pmdev->num_pages = k->device_num_pages; + pmdev->pages = g_new0(PMBusPage, k->device_num_pages); +} + +void pmbus_check_limits(PMBusDevice *pmdev) +{ + for (int i = 0; i < pmdev->num_pages; i++) { + if ((pmdev->pages[i].operation & PB_OP_ON) == 0) { + continue; /* don't check powered off devices */ + } + + if (pmdev->pages[i].read_vout > pmdev->pages[i].vout_ov_fault_limit) { + pmdev->pages[i].status_word |= PB_STATUS_VOUT; + pmdev->pages[i].status_vout |= PB_STATUS_VOUT_OV_FAULT; + } + + if (pmdev->pages[i].read_vout > pmdev->pages[i].vout_ov_warn_limit) { + pmdev->pages[i].status_word |= PB_STATUS_VOUT; + pmdev->pages[i].status_vout |= PB_STATUS_VOUT_OV_WARN; + } + + if (pmdev->pages[i].read_vout < pmdev->pages[i].vout_uv_warn_limit) { + pmdev->pages[i].status_word |= PB_STATUS_VOUT; + pmdev->pages[i].status_vout |= PB_STATUS_VOUT_UV_WARN; + } + + if (pmdev->pages[i].read_vout < pmdev->pages[i].vout_uv_fault_limit) { + pmdev->pages[i].status_word |= PB_STATUS_VOUT; + pmdev->pages[i].status_vout |= PB_STATUS_VOUT_UV_FAULT; + } + + if (pmdev->pages[i].read_vin > pmdev->pages[i].vin_ov_warn_limit) { + pmdev->pages[i].status_word |= PB_STATUS_INPUT; + pmdev->pages[i].status_input |= PB_STATUS_INPUT_VIN_OV_WARN; + } + + if (pmdev->pages[i].read_vin < pmdev->pages[i].vin_uv_warn_limit) { + pmdev->pages[i].status_word |= PB_STATUS_INPUT; + pmdev->pages[i].status_input |= PB_STATUS_INPUT_VIN_UV_WARN; + } + + if (pmdev->pages[i].read_iout > pmdev->pages[i].iout_oc_warn_limit) { + pmdev->pages[i].status_word |= PB_STATUS_IOUT_POUT; + pmdev->pages[i].status_iout |= PB_STATUS_IOUT_OC_WARN; + } + + if (pmdev->pages[i].read_iout > pmdev->pages[i].iout_oc_fault_limit) { + pmdev->pages[i].status_word |= PB_STATUS_IOUT_POUT; + pmdev->pages[i].status_iout |= PB_STATUS_IOUT_OC_FAULT; + } + + if (pmdev->pages[i].read_pin > pmdev->pages[i].pin_op_warn_limit) { + pmdev->pages[i].status_word |= PB_STATUS_INPUT; + pmdev->pages[i].status_input |= PB_STATUS_INPUT_PIN_OP_WARN; + } + + if (pmdev->pages[i].read_temperature_1 + > pmdev->pages[i].ot_fault_limit) { + pmdev->pages[i].status_word |= PB_STATUS_TEMPERATURE; + pmdev->pages[i].status_temperature |= PB_STATUS_OT_FAULT; + } + + if (pmdev->pages[i].read_temperature_1 + > pmdev->pages[i].ot_warn_limit) { + pmdev->pages[i].status_word |= PB_STATUS_TEMPERATURE; + pmdev->pages[i].status_temperature |= PB_STATUS_OT_WARN; + } + } +} + +static uint8_t pmbus_receive_byte(SMBusDevice *smd) +{ + PMBusDevice *pmdev = PMBUS_DEVICE(smd); + PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev); + uint8_t ret = 0xFF; + uint8_t index = pmdev->page; + + if (pmdev->out_buf_len != 0) { + ret = pmbus_out_buf_pop(pmdev); + return ret; + } + + switch (pmdev->code) { + case PMBUS_PAGE: + pmbus_send8(pmdev, pmdev->page); + break; + + case PMBUS_OPERATION: /* R/W byte */ + pmbus_send8(pmdev, pmdev->pages[index].operation); + break; + + case PMBUS_ON_OFF_CONFIG: /* R/W byte */ + pmbus_send8(pmdev, pmdev->pages[index].on_off_config); + break; + + case PMBUS_PHASE: /* R/W byte */ + pmbus_send8(pmdev, pmdev->pages[index].phase); + break; + + case PMBUS_WRITE_PROTECT: /* R/W byte */ + pmbus_send8(pmdev, pmdev->pages[index].write_protect); + break; + + case PMBUS_CAPABILITY: + pmbus_send8(pmdev, pmdev->capability); + break; + + case PMBUS_VOUT_MODE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MODE) { + pmbus_send8(pmdev, pmdev->pages[index].vout_mode); + } else { + goto passthough; + } + break; + + case PMBUS_VOUT_COMMAND: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmbus_send16(pmdev, pmdev->pages[index].vout_command); + } else { + goto passthough; + } + break; + + case PMBUS_VOUT_TRIM: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmbus_send16(pmdev, pmdev->pages[index].vout_trim); + } else { + goto passthough; + } + break; + + case PMBUS_VOUT_CAL_OFFSET: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmbus_send16(pmdev, pmdev->pages[index].vout_cal_offset); + } else { + goto passthough; + } + break; + + case PMBUS_VOUT_MAX: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmbus_send16(pmdev, pmdev->pages[index].vout_max); + } else { + goto passthough; + } + break; + + case PMBUS_VOUT_MARGIN_HIGH: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) { + pmbus_send16(pmdev, pmdev->pages[index].vout_margin_high); + } else { + goto passthough; + } + break; + + case PMBUS_VOUT_MARGIN_LOW: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) { + pmbus_send16(pmdev, pmdev->pages[index].vout_margin_low); + } else { + goto passthough; + } + break; + + case PMBUS_VOUT_TRANSITION_RATE: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmbus_send16(pmdev, pmdev->pages[index].vout_transition_rate); + } else { + goto passthough; + } + break; + + case PMBUS_VOUT_DROOP: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmbus_send16(pmdev, pmdev->pages[index].vout_droop); + } else { + goto passthough; + } + break; + + case PMBUS_VOUT_SCALE_LOOP: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmbus_send16(pmdev, pmdev->pages[index].vout_scale_loop); + } else { + goto passthough; + } + break; + + case PMBUS_VOUT_SCALE_MONITOR: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmbus_send16(pmdev, pmdev->pages[index].vout_scale_monitor); + } else { + goto passthough; + } + break; + + /* TODO: implement coefficients support */ + + case PMBUS_POUT_MAX: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_POUT) { + pmbus_send16(pmdev, pmdev->pages[index].pout_max); + } else { + goto passthough; + } + break; + + case PMBUS_VIN_ON: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VIN) { + pmbus_send16(pmdev, pmdev->pages[index].vin_on); + } else { + goto passthough; + } + break; + + case PMBUS_VIN_OFF: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VIN) { + pmbus_send16(pmdev, pmdev->pages[index].vin_off); + } else { + goto passthough; + } + break; + + case PMBUS_IOUT_CAL_GAIN: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_IOUT_GAIN) { + pmbus_send16(pmdev, pmdev->pages[index].iout_cal_gain); + } else { + goto passthough; + } + break; + + case PMBUS_VOUT_OV_FAULT_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmbus_send16(pmdev, pmdev->pages[index].vout_ov_fault_limit); + } else { + goto passthough; + } + break; + + case PMBUS_VOUT_OV_FAULT_RESPONSE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmbus_send8(pmdev, pmdev->pages[index].vout_ov_fault_response); + } else { + goto passthough; + } + break; + + case PMBUS_VOUT_OV_WARN_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmbus_send16(pmdev, pmdev->pages[index].vout_ov_warn_limit); + } else { + goto passthough; + } + break; + + case PMBUS_VOUT_UV_WARN_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmbus_send16(pmdev, pmdev->pages[index].vout_uv_warn_limit); + } else { + goto passthough; + } + break; + + case PMBUS_VOUT_UV_FAULT_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmbus_send16(pmdev, pmdev->pages[index].vout_uv_fault_limit); + } else { + goto passthough; + } + break; + + case PMBUS_VOUT_UV_FAULT_RESPONSE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmbus_send8(pmdev, pmdev->pages[index].vout_uv_fault_response); + } else { + goto passthough; + } + break; + + case PMBUS_IOUT_OC_FAULT_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { + pmbus_send16(pmdev, pmdev->pages[index].iout_oc_fault_limit); + } else { + goto passthough; + } + break; + + case PMBUS_IOUT_OC_FAULT_RESPONSE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { + pmbus_send8(pmdev, pmdev->pages[index].iout_oc_fault_response); + } else { + goto passthough; + } + break; + + case PMBUS_IOUT_OC_LV_FAULT_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { + pmbus_send16(pmdev, pmdev->pages[index].iout_oc_lv_fault_limit); + } else { + goto passthough; + } + break; + + case PMBUS_IOUT_OC_LV_FAULT_RESPONSE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { + pmbus_send8(pmdev, pmdev->pages[index].iout_oc_lv_fault_response); + } else { + goto passthough; + } + break; + + case PMBUS_IOUT_OC_WARN_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { + pmbus_send16(pmdev, pmdev->pages[index].iout_oc_warn_limit); + } else { + goto passthough; + } + break; + + case PMBUS_IOUT_UC_FAULT_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { + pmbus_send16(pmdev, pmdev->pages[index].iout_uc_fault_limit); + } else { + goto passthough; + } + break; + + case PMBUS_IOUT_UC_FAULT_RESPONSE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { + pmbus_send8(pmdev, pmdev->pages[index].iout_uc_fault_response); + } else { + goto passthough; + } + break; + + case PMBUS_OT_FAULT_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { + pmbus_send16(pmdev, pmdev->pages[index].ot_fault_limit); + } else { + goto passthough; + } + break; + + case PMBUS_OT_FAULT_RESPONSE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { + pmbus_send8(pmdev, pmdev->pages[index].ot_fault_response); + } else { + goto passthough; + } + break; + + case PMBUS_OT_WARN_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { + pmbus_send16(pmdev, pmdev->pages[index].ot_warn_limit); + } else { + goto passthough; + } + break; + + case PMBUS_UT_WARN_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { + pmbus_send16(pmdev, pmdev->pages[index].ut_warn_limit); + } else { + goto passthough; + } + break; + + case PMBUS_UT_FAULT_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { + pmbus_send16(pmdev, pmdev->pages[index].ut_fault_limit); + } else { + goto passthough; + } + break; + + case PMBUS_UT_FAULT_RESPONSE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { + pmbus_send8(pmdev, pmdev->pages[index].ut_fault_response); + } else { + goto passthough; + } + break; + + case PMBUS_VIN_OV_FAULT_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VIN) { + pmbus_send16(pmdev, pmdev->pages[index].vin_ov_fault_limit); + } else { + goto passthough; + } + break; + + case PMBUS_VIN_OV_FAULT_RESPONSE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_VIN) { + pmbus_send8(pmdev, pmdev->pages[index].vin_ov_fault_response); + } else { + goto passthough; + } + break; + + case PMBUS_VIN_OV_WARN_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VIN) { + pmbus_send16(pmdev, pmdev->pages[index].vin_ov_warn_limit); + } else { + goto passthough; + } + break; + + case PMBUS_VIN_UV_WARN_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VIN) { + pmbus_send16(pmdev, pmdev->pages[index].vin_uv_warn_limit); + } else { + goto passthough; + } + break; + + case PMBUS_VIN_UV_FAULT_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VIN) { + pmbus_send16(pmdev, pmdev->pages[index].vin_uv_fault_limit); + } else { + goto passthough; + } + break; + + case PMBUS_VIN_UV_FAULT_RESPONSE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_VIN) { + pmbus_send8(pmdev, pmdev->pages[index].vin_uv_fault_response); + } else { + goto passthough; + } + break; + + case PMBUS_IIN_OC_FAULT_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_IIN) { + pmbus_send16(pmdev, pmdev->pages[index].iin_oc_fault_limit); + } else { + goto passthough; + } + break; + + case PMBUS_IIN_OC_FAULT_RESPONSE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_IIN) { + pmbus_send8(pmdev, pmdev->pages[index].iin_oc_fault_response); + } else { + goto passthough; + } + break; + + case PMBUS_IIN_OC_WARN_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_IIN) { + pmbus_send16(pmdev, pmdev->pages[index].iin_oc_warn_limit); + } else { + goto passthough; + } + break; + + case PMBUS_POUT_OP_FAULT_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_POUT) { + pmbus_send16(pmdev, pmdev->pages[index].pout_op_fault_limit); + } else { + goto passthough; + } + break; + + case PMBUS_POUT_OP_FAULT_RESPONSE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_POUT) { + pmbus_send8(pmdev, pmdev->pages[index].pout_op_fault_response); + } else { + goto passthough; + } + break; + + case PMBUS_POUT_OP_WARN_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_POUT) { + pmbus_send16(pmdev, pmdev->pages[index].pout_op_warn_limit); + } else { + goto passthough; + } + break; + + case PMBUS_PIN_OP_WARN_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_PIN) { + pmbus_send16(pmdev, pmdev->pages[index].pin_op_warn_limit); + } else { + goto passthough; + } + break; + + case PMBUS_STATUS_BYTE: /* R/W byte */ + pmbus_send8(pmdev, pmdev->pages[index].status_word & 0xFF); + break; + + case PMBUS_STATUS_WORD: /* R/W word */ + pmbus_send16(pmdev, pmdev->pages[index].status_word); + break; + + case PMBUS_STATUS_VOUT: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmbus_send8(pmdev, pmdev->pages[index].status_vout); + } else { + goto passthough; + } + break; + + case PMBUS_STATUS_IOUT: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { + pmbus_send8(pmdev, pmdev->pages[index].status_iout); + } else { + goto passthough; + } + break; + + case PMBUS_STATUS_INPUT: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_VIN || + pmdev->pages[index].page_flags & PB_HAS_IIN || + pmdev->pages[index].page_flags & PB_HAS_PIN) { + pmbus_send8(pmdev, pmdev->pages[index].status_input); + } else { + goto passthough; + } + break; + + case PMBUS_STATUS_TEMPERATURE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { + pmbus_send8(pmdev, pmdev->pages[index].status_temperature); + } else { + goto passthough; + } + break; + + case PMBUS_STATUS_CML: /* R/W byte */ + pmbus_send8(pmdev, pmdev->pages[index].status_cml); + break; + + case PMBUS_STATUS_OTHER: /* R/W byte */ + pmbus_send8(pmdev, pmdev->pages[index].status_other); + break; + + case PMBUS_READ_EIN: /* Read-Only block 5 bytes */ + if (pmdev->pages[index].page_flags & PB_HAS_EIN) { + pmbus_send(pmdev, pmdev->pages[index].read_ein, 5); + } else { + goto passthough; + } + break; + + case PMBUS_READ_EOUT: /* Read-Only block 5 bytes */ + if (pmdev->pages[index].page_flags & PB_HAS_EOUT) { + pmbus_send(pmdev, pmdev->pages[index].read_eout, 5); + } else { + goto passthough; + } + break; + + case PMBUS_READ_VIN: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_VIN) { + pmbus_send16(pmdev, pmdev->pages[index].read_vin); + } else { + goto passthough; + } + break; + + case PMBUS_READ_IIN: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_IIN) { + pmbus_send16(pmdev, pmdev->pages[index].read_iin); + } else { + goto passthough; + } + break; + + case PMBUS_READ_VOUT: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmbus_send16(pmdev, pmdev->pages[index].read_vout); + } else { + goto passthough; + } + break; + + case PMBUS_READ_IOUT: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { + pmbus_send16(pmdev, pmdev->pages[index].read_iout); + } else { + goto passthough; + } + break; + + case PMBUS_READ_TEMPERATURE_1: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { + pmbus_send16(pmdev, pmdev->pages[index].read_temperature_1); + } else { + goto passthough; + } + break; + + case PMBUS_READ_TEMPERATURE_2: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_TEMP2) { + pmbus_send16(pmdev, pmdev->pages[index].read_temperature_2); + } else { + goto passthough; + } + break; + + case PMBUS_READ_TEMPERATURE_3: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_TEMP3) { + pmbus_send16(pmdev, pmdev->pages[index].read_temperature_3); + } else { + goto passthough; + } + break; + + case PMBUS_READ_POUT: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_POUT) { + pmbus_send16(pmdev, pmdev->pages[index].read_pout); + } else { + goto passthough; + } + break; + + case PMBUS_READ_PIN: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_PIN) { + pmbus_send16(pmdev, pmdev->pages[index].read_pin); + } else { + goto passthough; + } + break; + + case PMBUS_REVISION: /* Read-Only byte */ + pmbus_send8(pmdev, pmdev->pages[index].revision); + break; + + case PMBUS_MFR_ID: /* R/W block */ + if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) { + pmbus_send_string(pmdev, pmdev->pages[index].mfr_id); + } else { + goto passthough; + } + break; + + case PMBUS_MFR_MODEL: /* R/W block */ + if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) { + pmbus_send_string(pmdev, pmdev->pages[index].mfr_model); + } else { + goto passthough; + } + break; + + case PMBUS_MFR_REVISION: /* R/W block */ + if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) { + pmbus_send_string(pmdev, pmdev->pages[index].mfr_revision); + } else { + goto passthough; + } + break; + + case PMBUS_MFR_LOCATION: /* R/W block */ + if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) { + pmbus_send_string(pmdev, pmdev->pages[index].mfr_location); + } else { + goto passthough; + } + break; + + case PMBUS_MFR_VIN_MIN: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_VIN_RATING) { + pmbus_send16(pmdev, pmdev->pages[index].mfr_vin_min); + } else { + goto passthough; + } + break; + + case PMBUS_MFR_VIN_MAX: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_VIN_RATING) { + pmbus_send16(pmdev, pmdev->pages[index].mfr_vin_max); + } else { + goto passthough; + } + break; + + case PMBUS_MFR_IIN_MAX: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_IIN_RATING) { + pmbus_send16(pmdev, pmdev->pages[index].mfr_iin_max); + } else { + goto passthough; + } + break; + + case PMBUS_MFR_PIN_MAX: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_PIN_RATING) { + pmbus_send16(pmdev, pmdev->pages[index].mfr_pin_max); + } else { + goto passthough; + } + break; + + case PMBUS_MFR_VOUT_MIN: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) { + pmbus_send16(pmdev, pmdev->pages[index].mfr_vout_min); + } else { + goto passthough; + } + break; + + case PMBUS_MFR_VOUT_MAX: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) { + pmbus_send16(pmdev, pmdev->pages[index].mfr_vout_max); + } else { + goto passthough; + } + break; + + case PMBUS_MFR_IOUT_MAX: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_IOUT_RATING) { + pmbus_send16(pmdev, pmdev->pages[index].mfr_iout_max); + } else { + goto passthough; + } + break; + + case PMBUS_MFR_POUT_MAX: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_POUT_RATING) { + pmbus_send16(pmdev, pmdev->pages[index].mfr_pout_max); + } else { + goto passthough; + } + break; + + case PMBUS_MFR_MAX_TEMP_1: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) { + pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_1); + } else { + goto passthough; + } + break; + + case PMBUS_MFR_MAX_TEMP_2: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) { + pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_2); + } else { + goto passthough; + } + break; + + case PMBUS_MFR_MAX_TEMP_3: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) { + pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_3); + } else { + goto passthough; + } + break; + + case PMBUS_CLEAR_FAULTS: /* Send Byte */ + case PMBUS_PAGE_PLUS_WRITE: /* Block Write-only */ + case PMBUS_STORE_DEFAULT_ALL: /* Send Byte */ + case PMBUS_RESTORE_DEFAULT_ALL: /* Send Byte */ + case PMBUS_STORE_DEFAULT_CODE: /* Write-only Byte */ + case PMBUS_RESTORE_DEFAULT_CODE: /* Write-only Byte */ + case PMBUS_STORE_USER_ALL: /* Send Byte */ + case PMBUS_RESTORE_USER_ALL: /* Send Byte */ + case PMBUS_STORE_USER_CODE: /* Write-only Byte */ + case PMBUS_RESTORE_USER_CODE: /* Write-only Byte */ + case PMBUS_QUERY: /* Write-Only */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: reading from write only register 0x%02x\n", + __func__, pmdev->code); + break; + +passthough: + default: + /* Pass through read request if not handled */ + if (pmdc->receive_byte) { + ret = pmdc->receive_byte(pmdev); + } + break; + } + + if (pmdev->out_buf_len != 0) { + ret = pmbus_out_buf_pop(pmdev); + return ret; + } + + return ret; +} + +/* + * PMBus clear faults command applies to all status registers, existing faults + * should separately get re-asserted. + */ +static void pmbus_clear_faults(PMBusDevice *pmdev) +{ + for (uint8_t i = 0; i < pmdev->num_pages; i++) { + pmdev->pages[i].status_word = 0; + pmdev->pages[i].status_vout = 0; + pmdev->pages[i].status_iout = 0; + pmdev->pages[i].status_input = 0; + pmdev->pages[i].status_temperature = 0; + pmdev->pages[i].status_cml = 0; + pmdev->pages[i].status_other = 0; + pmdev->pages[i].status_mfr_specific = 0; + pmdev->pages[i].status_fans_1_2 = 0; + pmdev->pages[i].status_fans_3_4 = 0; + } + +} + +/* + * PMBus operation is used to turn On and Off PSUs + * Therefore, default value for the Operation should be PB_OP_ON or 0x80 + */ +static void pmbus_operation(PMBusDevice *pmdev) +{ + uint8_t index = pmdev->page; + if ((pmdev->pages[index].operation & PB_OP_ON) == 0) { + pmdev->pages[index].read_vout = 0; + pmdev->pages[index].read_iout = 0; + pmdev->pages[index].read_pout = 0; + return; + } + + if (pmdev->pages[index].operation & (PB_OP_ON | PB_OP_MARGIN_HIGH)) { + pmdev->pages[index].read_vout = pmdev->pages[index].vout_margin_high; + } + + if (pmdev->pages[index].operation & (PB_OP_ON | PB_OP_MARGIN_LOW)) { + pmdev->pages[index].read_vout = pmdev->pages[index].vout_margin_low; + } + pmbus_check_limits(pmdev); +} + +static int pmbus_write_data(SMBusDevice *smd, uint8_t *buf, uint8_t len) +{ + PMBusDevice *pmdev = PMBUS_DEVICE(smd); + PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev); + int ret = 0; + uint8_t index; + + if (len == 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__); + return -1; + } + + if (!pmdev->pages) { /* allocate memory for pages on first use */ + pmbus_pages_alloc(pmdev); + } + + pmdev->in_buf_len = len; + pmdev->in_buf = buf; + + pmdev->code = buf[0]; /* PMBus command code */ + if (len == 1) { /* Single length writes are command codes only */ + return 0; + } + + if (pmdev->code == PMBUS_PAGE) { + pmdev->page = pmbus_receive8(pmdev); + return 0; + } + /* loop through all the pages when 0xFF is received */ + if (pmdev->page == PB_ALL_PAGES) { + for (int i = 0; i < pmdev->num_pages; i++) { + pmdev->page = i; + pmbus_write_data(smd, buf, len); + } + pmdev->page = PB_ALL_PAGES; + return 0; + } + + index = pmdev->page; + + switch (pmdev->code) { + case PMBUS_OPERATION: /* R/W byte */ + pmdev->pages[index].operation = pmbus_receive8(pmdev); + pmbus_operation(pmdev); + break; + + case PMBUS_ON_OFF_CONFIG: /* R/W byte */ + pmdev->pages[index].on_off_config = pmbus_receive8(pmdev); + break; + + case PMBUS_CLEAR_FAULTS: /* Send Byte */ + pmbus_clear_faults(pmdev); + break; + + case PMBUS_PHASE: /* R/W byte */ + pmdev->pages[index].phase = pmbus_receive8(pmdev); + break; + + case PMBUS_PAGE_PLUS_WRITE: /* Block Write-only */ + case PMBUS_WRITE_PROTECT: /* R/W byte */ + pmdev->pages[index].write_protect = pmbus_receive8(pmdev); + break; + + case PMBUS_VOUT_MODE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MODE) { + pmdev->pages[index].vout_mode = pmbus_receive8(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_VOUT_COMMAND: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmdev->pages[index].vout_command = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_VOUT_TRIM: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmdev->pages[index].vout_trim = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_VOUT_CAL_OFFSET: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmdev->pages[index].vout_cal_offset = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_VOUT_MAX: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmdev->pages[index].vout_max = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_VOUT_MARGIN_HIGH: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) { + pmdev->pages[index].vout_margin_high = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_VOUT_MARGIN_LOW: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) { + pmdev->pages[index].vout_margin_low = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_VOUT_TRANSITION_RATE: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmdev->pages[index].vout_transition_rate = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_VOUT_DROOP: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmdev->pages[index].vout_droop = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_VOUT_SCALE_LOOP: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmdev->pages[index].vout_scale_loop = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_VOUT_SCALE_MONITOR: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmdev->pages[index].vout_scale_monitor = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_POUT_MAX: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmdev->pages[index].pout_max = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_VIN_ON: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VIN) { + pmdev->pages[index].vin_on = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_VIN_OFF: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VIN) { + pmdev->pages[index].vin_off = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_IOUT_CAL_GAIN: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_IOUT_GAIN) { + pmdev->pages[index].iout_cal_gain = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_VOUT_OV_FAULT_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmdev->pages[index].vout_ov_fault_limit = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_VOUT_OV_FAULT_RESPONSE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmdev->pages[index].vout_ov_fault_response = pmbus_receive8(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_VOUT_OV_WARN_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmdev->pages[index].vout_ov_warn_limit = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_VOUT_UV_WARN_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmdev->pages[index].vout_uv_warn_limit = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_VOUT_UV_FAULT_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmdev->pages[index].vout_uv_fault_limit = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_VOUT_UV_FAULT_RESPONSE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmdev->pages[index].vout_uv_fault_response = pmbus_receive8(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_IOUT_OC_FAULT_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { + pmdev->pages[index].iout_oc_fault_limit = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_IOUT_OC_FAULT_RESPONSE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { + pmdev->pages[index].iout_oc_fault_response = pmbus_receive8(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_IOUT_OC_LV_FAULT_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { + pmdev->pages[index].iout_oc_lv_fault_limit = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_IOUT_OC_LV_FAULT_RESPONSE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { + pmdev->pages[index].iout_oc_lv_fault_response + = pmbus_receive8(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_IOUT_OC_WARN_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { + pmdev->pages[index].iout_oc_warn_limit = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_IOUT_UC_FAULT_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { + pmdev->pages[index].iout_uc_fault_limit = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_IOUT_UC_FAULT_RESPONSE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { + pmdev->pages[index].iout_uc_fault_response = pmbus_receive8(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_OT_FAULT_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { + pmdev->pages[index].ot_fault_limit = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_OT_FAULT_RESPONSE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { + pmdev->pages[index].ot_fault_response = pmbus_receive8(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_OT_WARN_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { + pmdev->pages[index].ot_warn_limit = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_UT_WARN_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { + pmdev->pages[index].ut_warn_limit = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_UT_FAULT_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { + pmdev->pages[index].ut_fault_limit = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_UT_FAULT_RESPONSE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { + pmdev->pages[index].ut_fault_response = pmbus_receive8(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_VIN_OV_FAULT_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VIN) { + pmdev->pages[index].vin_ov_fault_limit = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_VIN_OV_FAULT_RESPONSE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_VIN) { + pmdev->pages[index].vin_ov_fault_response = pmbus_receive8(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_VIN_OV_WARN_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VIN) { + pmdev->pages[index].vin_ov_warn_limit = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_VIN_UV_WARN_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VIN) { + pmdev->pages[index].vin_uv_warn_limit = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_VIN_UV_FAULT_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VIN) { + pmdev->pages[index].vin_uv_fault_limit = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_VIN_UV_FAULT_RESPONSE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_VIN) { + pmdev->pages[index].vin_uv_fault_response = pmbus_receive8(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_IIN_OC_FAULT_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_IIN) { + pmdev->pages[index].iin_oc_fault_limit = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_IIN_OC_FAULT_RESPONSE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_IIN) { + pmdev->pages[index].iin_oc_fault_response = pmbus_receive8(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_IIN_OC_WARN_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_IIN) { + pmdev->pages[index].iin_oc_warn_limit = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_POUT_OP_FAULT_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmdev->pages[index].pout_op_fault_limit = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_POUT_OP_FAULT_RESPONSE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmdev->pages[index].pout_op_fault_response = pmbus_receive8(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_POUT_OP_WARN_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmdev->pages[index].pout_op_warn_limit = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_PIN_OP_WARN_LIMIT: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_PIN) { + pmdev->pages[index].pin_op_warn_limit = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_STATUS_BYTE: /* R/W byte */ + pmdev->pages[index].status_word = pmbus_receive8(pmdev); + break; + + case PMBUS_STATUS_WORD: /* R/W word */ + pmdev->pages[index].status_word = pmbus_receive16(pmdev); + break; + + case PMBUS_STATUS_VOUT: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { + pmdev->pages[index].status_vout = pmbus_receive8(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_STATUS_IOUT: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_IOUT) { + pmdev->pages[index].status_iout = pmbus_receive8(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_STATUS_INPUT: /* R/W byte */ + pmdev->pages[index].status_input = pmbus_receive8(pmdev); + break; + + case PMBUS_STATUS_TEMPERATURE: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) { + pmdev->pages[index].status_temperature = pmbus_receive8(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_STATUS_CML: /* R/W byte */ + pmdev->pages[index].status_cml = pmbus_receive8(pmdev); + break; + + case PMBUS_STATUS_OTHER: /* R/W byte */ + pmdev->pages[index].status_other = pmbus_receive8(pmdev); + break; + + case PMBUS_PAGE_PLUS_READ: /* Block Read-only */ + case PMBUS_CAPABILITY: /* Read-Only byte */ + case PMBUS_COEFFICIENTS: /* Read-only block 5 bytes */ + case PMBUS_READ_EIN: /* Read-Only block 5 bytes */ + case PMBUS_READ_EOUT: /* Read-Only block 5 bytes */ + case PMBUS_READ_VIN: /* Read-Only word */ + case PMBUS_READ_IIN: /* Read-Only word */ + case PMBUS_READ_VCAP: /* Read-Only word */ + case PMBUS_READ_VOUT: /* Read-Only word */ + case PMBUS_READ_IOUT: /* Read-Only word */ + case PMBUS_READ_TEMPERATURE_1: /* Read-Only word */ + case PMBUS_READ_TEMPERATURE_2: /* Read-Only word */ + case PMBUS_READ_TEMPERATURE_3: /* Read-Only word */ + case PMBUS_READ_FAN_SPEED_1: /* Read-Only word */ + case PMBUS_READ_FAN_SPEED_2: /* Read-Only word */ + case PMBUS_READ_FAN_SPEED_3: /* Read-Only word */ + case PMBUS_READ_FAN_SPEED_4: /* Read-Only word */ + case PMBUS_READ_DUTY_CYCLE: /* Read-Only word */ + case PMBUS_READ_FREQUENCY: /* Read-Only word */ + case PMBUS_READ_POUT: /* Read-Only word */ + case PMBUS_READ_PIN: /* Read-Only word */ + case PMBUS_REVISION: /* Read-Only byte */ + case PMBUS_APP_PROFILE_SUPPORT: /* Read-Only block-read */ + case PMBUS_MFR_VIN_MIN: /* Read-Only word */ + case PMBUS_MFR_VIN_MAX: /* Read-Only word */ + case PMBUS_MFR_IIN_MAX: /* Read-Only word */ + case PMBUS_MFR_PIN_MAX: /* Read-Only word */ + case PMBUS_MFR_VOUT_MIN: /* Read-Only word */ + case PMBUS_MFR_VOUT_MAX: /* Read-Only word */ + case PMBUS_MFR_IOUT_MAX: /* Read-Only word */ + case PMBUS_MFR_POUT_MAX: /* Read-Only word */ + case PMBUS_MFR_TAMBIENT_MAX: /* Read-Only word */ + case PMBUS_MFR_TAMBIENT_MIN: /* Read-Only word */ + case PMBUS_MFR_EFFICIENCY_LL: /* Read-Only block 14 bytes */ + case PMBUS_MFR_EFFICIENCY_HL: /* Read-Only block 14 bytes */ + case PMBUS_MFR_PIN_ACCURACY: /* Read-Only byte */ + case PMBUS_IC_DEVICE_ID: /* Read-Only block-read */ + case PMBUS_IC_DEVICE_REV: /* Read-Only block-read */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: writing to read-only register 0x%02x\n", + __func__, pmdev->code); + break; + +passthrough: + /* Unimplimented registers get passed to the device */ + default: + if (pmdc->write_data) { + ret = pmdc->write_data(pmdev, buf, len); + } + break; + } + pmbus_check_limits(pmdev); + pmdev->in_buf_len = 0; + return ret; +} + +int pmbus_page_config(PMBusDevice *pmdev, uint8_t index, uint64_t flags) +{ + if (!pmdev->pages) { /* allocate memory for pages on first use */ + pmbus_pages_alloc(pmdev); + } + + /* The 0xFF page is special for commands applying to all pages */ + if (index == PB_ALL_PAGES) { + for (int i = 0; i < pmdev->num_pages; i++) { + pmdev->pages[i].page_flags = flags; + } + return 0; + } + + if (index > pmdev->num_pages - 1) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: index %u is out of range\n", + __func__, index); + return -1; + } + + pmdev->pages[index].page_flags = flags; + + return 0; +} + +/* TODO: include pmbus page info in vmstate */ +const VMStateDescription vmstate_pmbus_device = { + .name = TYPE_PMBUS_DEVICE, + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_SMBUS_DEVICE(smb, PMBusDevice), + VMSTATE_UINT8(num_pages, PMBusDevice), + VMSTATE_UINT8(code, PMBusDevice), + VMSTATE_UINT8(page, PMBusDevice), + VMSTATE_UINT8(capability, PMBusDevice), + VMSTATE_END_OF_LIST() + } +}; + +static void pmbus_device_finalize(Object *obj) +{ + PMBusDevice *pmdev = PMBUS_DEVICE(obj); + g_free(pmdev->pages); +} + +static void pmbus_device_class_init(ObjectClass *klass, void *data) +{ + SMBusDeviceClass *k = SMBUS_DEVICE_CLASS(klass); + + k->quick_cmd = pmbus_quick_cmd; + k->write_data = pmbus_write_data; + k->receive_byte = pmbus_receive_byte; +} + +static const TypeInfo pmbus_device_type_info = { + .name = TYPE_PMBUS_DEVICE, + .parent = TYPE_SMBUS_DEVICE, + .instance_size = sizeof(PMBusDevice), + .instance_finalize = pmbus_device_finalize, + .abstract = true, + .class_size = sizeof(PMBusDeviceClass), + .class_init = pmbus_device_class_init, +}; + +static void pmbus_device_register_types(void) +{ + type_register_static(&pmbus_device_type_info); +} + +type_init(pmbus_device_register_types) diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h new file mode 100644 index 0000000000..62bd38c83f --- /dev/null +++ b/include/hw/i2c/pmbus_device.h @@ -0,0 +1,517 @@ +/* + * QEMU PMBus device emulation + * + * Copyright 2021 Google LLC + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_PMBUS_DEVICE_H +#define HW_PMBUS_DEVICE_H + +#include "qemu/bitops.h" +#include "hw/i2c/smbus_slave.h" + +enum pmbus_registers { + PMBUS_PAGE = 0x00, /* R/W byte */ + PMBUS_OPERATION = 0x01, /* R/W byte */ + PMBUS_ON_OFF_CONFIG = 0x02, /* R/W byte */ + PMBUS_CLEAR_FAULTS = 0x03, /* Send Byte */ + PMBUS_PHASE = 0x04, /* R/W byte */ + PMBUS_PAGE_PLUS_WRITE = 0x05, /* Block Write-only */ + PMBUS_PAGE_PLUS_READ = 0x06, /* Block Read-only */ + PMBUS_WRITE_PROTECT = 0x10, /* R/W byte */ + PMBUS_STORE_DEFAULT_ALL = 0x11, /* Send Byte */ + PMBUS_RESTORE_DEFAULT_ALL = 0x12, /* Send Byte */ + PMBUS_STORE_DEFAULT_CODE = 0x13, /* Write-only Byte */ + PMBUS_RESTORE_DEFAULT_CODE = 0x14, /* Write-only Byte */ + PMBUS_STORE_USER_ALL = 0x15, /* Send Byte */ + PMBUS_RESTORE_USER_ALL = 0x16, /* Send Byte */ + PMBUS_STORE_USER_CODE = 0x17, /* Write-only Byte */ + PMBUS_RESTORE_USER_CODE = 0x18, /* Write-only Byte */ + PMBUS_CAPABILITY = 0x19, /* Read-Only byte */ + PMBUS_QUERY = 0x1A, /* Write-Only */ + PMBUS_SMBALERT_MASK = 0x1B, /* Block read, Word write */ + PMBUS_VOUT_MODE = 0x20, /* R/W byte */ + PMBUS_VOUT_COMMAND = 0x21, /* R/W word */ + PMBUS_VOUT_TRIM = 0x22, /* R/W word */ + PMBUS_VOUT_CAL_OFFSET = 0x23, /* R/W word */ + PMBUS_VOUT_MAX = 0x24, /* R/W word */ + PMBUS_VOUT_MARGIN_HIGH = 0x25, /* R/W word */ + PMBUS_VOUT_MARGIN_LOW = 0x26, /* R/W word */ + PMBUS_VOUT_TRANSITION_RATE = 0x27, /* R/W word */ + PMBUS_VOUT_DROOP = 0x28, /* R/W word */ + PMBUS_VOUT_SCALE_LOOP = 0x29, /* R/W word */ + PMBUS_VOUT_SCALE_MONITOR = 0x2A, /* R/W word */ + PMBUS_COEFFICIENTS = 0x30, /* Read-only block 5 bytes */ + PMBUS_POUT_MAX = 0x31, /* R/W word */ + PMBUS_MAX_DUTY = 0x32, /* R/W word */ + PMBUS_FREQUENCY_SWITCH = 0x33, /* R/W word */ + PMBUS_VIN_ON = 0x35, /* R/W word */ + PMBUS_VIN_OFF = 0x36, /* R/W word */ + PMBUS_INTERLEAVE = 0x37, /* R/W word */ + PMBUS_IOUT_CAL_GAIN = 0x38, /* R/W word */ + PMBUS_IOUT_CAL_OFFSET = 0x39, /* R/W word */ + PMBUS_FAN_CONFIG_1_2 = 0x3A, /* R/W byte */ + PMBUS_FAN_COMMAND_1 = 0x3B, /* R/W word */ + PMBUS_FAN_COMMAND_2 = 0x3C, /* R/W word */ + PMBUS_FAN_CONFIG_3_4 = 0x3D, /* R/W byte */ + PMBUS_FAN_COMMAND_3 = 0x3E, /* R/W word */ + PMBUS_FAN_COMMAND_4 = 0x3F, /* R/W word */ + PMBUS_VOUT_OV_FAULT_LIMIT = 0x40, /* R/W word */ + PMBUS_VOUT_OV_FAULT_RESPONSE = 0x41, /* R/W byte */ + PMBUS_VOUT_OV_WARN_LIMIT = 0x42, /* R/W word */ + PMBUS_VOUT_UV_WARN_LIMIT = 0x43, /* R/W word */ + PMBUS_VOUT_UV_FAULT_LIMIT = 0x44, /* R/W word */ + PMBUS_VOUT_UV_FAULT_RESPONSE = 0x45, /* R/W byte */ + PMBUS_IOUT_OC_FAULT_LIMIT = 0x46, /* R/W word */ + PMBUS_IOUT_OC_FAULT_RESPONSE = 0x47, /* R/W byte */ + PMBUS_IOUT_OC_LV_FAULT_LIMIT = 0x48, /* R/W word */ + PMBUS_IOUT_OC_LV_FAULT_RESPONSE = 0x49, /* R/W byte */ + PMBUS_IOUT_OC_WARN_LIMIT = 0x4A, /* R/W word */ + PMBUS_IOUT_UC_FAULT_LIMIT = 0x4B, /* R/W word */ + PMBUS_IOUT_UC_FAULT_RESPONSE = 0x4C, /* R/W byte */ + PMBUS_OT_FAULT_LIMIT = 0x4F, /* R/W word */ + PMBUS_OT_FAULT_RESPONSE = 0x50, /* R/W byte */ + PMBUS_OT_WARN_LIMIT = 0x51, /* R/W word */ + PMBUS_UT_WARN_LIMIT = 0x52, /* R/W word */ + PMBUS_UT_FAULT_LIMIT = 0x53, /* R/W word */ + PMBUS_UT_FAULT_RESPONSE = 0x54, /* R/W byte */ + PMBUS_VIN_OV_FAULT_LIMIT = 0x55, /* R/W word */ + PMBUS_VIN_OV_FAULT_RESPONSE = 0x56, /* R/W byte */ + PMBUS_VIN_OV_WARN_LIMIT = 0x57, /* R/W word */ + PMBUS_VIN_UV_WARN_LIMIT = 0x58, /* R/W word */ + PMBUS_VIN_UV_FAULT_LIMIT = 0x59, /* R/W word */ + PMBUS_VIN_UV_FAULT_RESPONSE = 0x5A, /* R/W byte */ + PMBUS_IIN_OC_FAULT_LIMIT = 0x5B, /* R/W word */ + PMBUS_IIN_OC_FAULT_RESPONSE = 0x5C, /* R/W byte */ + PMBUS_IIN_OC_WARN_LIMIT = 0x5D, /* R/W word */ + PMBUS_POWER_GOOD_ON = 0x5E, /* R/W word */ + PMBUS_POWER_GOOD_OFF = 0x5F, /* R/W word */ + PMBUS_TON_DELAY = 0x60, /* R/W word */ + PMBUS_TON_RISE = 0x61, /* R/W word */ + PMBUS_TON_MAX_FAULT_LIMIT = 0x62, /* R/W word */ + PMBUS_TON_MAX_FAULT_RESPONSE = 0x63, /* R/W byte */ + PMBUS_TOFF_DELAY = 0x64, /* R/W word */ + PMBUS_TOFF_FALL = 0x65, /* R/W word */ + PMBUS_TOFF_MAX_WARN_LIMIT = 0x66, /* R/W word */ + PMBUS_POUT_OP_FAULT_LIMIT = 0x68, /* R/W word */ + PMBUS_POUT_OP_FAULT_RESPONSE = 0x69, /* R/W byte */ + PMBUS_POUT_OP_WARN_LIMIT = 0x6A, /* R/W word */ + PMBUS_PIN_OP_WARN_LIMIT = 0x6B, /* R/W word */ + PMBUS_STATUS_BYTE = 0x78, /* R/W byte */ + PMBUS_STATUS_WORD = 0x79, /* R/W word */ + PMBUS_STATUS_VOUT = 0x7A, /* R/W byte */ + PMBUS_STATUS_IOUT = 0x7B, /* R/W byte */ + PMBUS_STATUS_INPUT = 0x7C, /* R/W byte */ + PMBUS_STATUS_TEMPERATURE = 0x7D, /* R/W byte */ + PMBUS_STATUS_CML = 0x7E, /* R/W byte */ + PMBUS_STATUS_OTHER = 0x7F, /* R/W byte */ + PMBUS_STATUS_MFR_SPECIFIC = 0x80, /* R/W byte */ + PMBUS_STATUS_FANS_1_2 = 0x81, /* R/W byte */ + PMBUS_STATUS_FANS_3_4 = 0x82, /* R/W byte */ + PMBUS_READ_EIN = 0x86, /* Read-Only block 5 bytes */ + PMBUS_READ_EOUT = 0x87, /* Read-Only block 5 bytes */ + PMBUS_READ_VIN = 0x88, /* Read-Only word */ + PMBUS_READ_IIN = 0x89, /* Read-Only word */ + PMBUS_READ_VCAP = 0x8A, /* Read-Only word */ + PMBUS_READ_VOUT = 0x8B, /* Read-Only word */ + PMBUS_READ_IOUT = 0x8C, /* Read-Only word */ + PMBUS_READ_TEMPERATURE_1 = 0x8D, /* Read-Only word */ + PMBUS_READ_TEMPERATURE_2 = 0x8E, /* Read-Only word */ + PMBUS_READ_TEMPERATURE_3 = 0x8F, /* Read-Only word */ + PMBUS_READ_FAN_SPEED_1 = 0x90, /* Read-Only word */ + PMBUS_READ_FAN_SPEED_2 = 0x91, /* Read-Only word */ + PMBUS_READ_FAN_SPEED_3 = 0x92, /* Read-Only word */ + PMBUS_READ_FAN_SPEED_4 = 0x93, /* Read-Only word */ + PMBUS_READ_DUTY_CYCLE = 0x94, /* Read-Only word */ + PMBUS_READ_FREQUENCY = 0x95, /* Read-Only word */ + PMBUS_READ_POUT = 0x96, /* Read-Only word */ + PMBUS_READ_PIN = 0x97, /* Read-Only word */ + PMBUS_REVISION = 0x98, /* Read-Only byte */ + PMBUS_MFR_ID = 0x99, /* R/W block */ + PMBUS_MFR_MODEL = 0x9A, /* R/W block */ + PMBUS_MFR_REVISION = 0x9B, /* R/W block */ + PMBUS_MFR_LOCATION = 0x9C, /* R/W block */ + PMBUS_MFR_DATE = 0x9D, /* R/W block */ + PMBUS_MFR_SERIAL = 0x9E, /* R/W block */ + PMBUS_APP_PROFILE_SUPPORT = 0x9F, /* Read-Only block-read */ + PMBUS_MFR_VIN_MIN = 0xA0, /* Read-Only word */ + PMBUS_MFR_VIN_MAX = 0xA1, /* Read-Only word */ + PMBUS_MFR_IIN_MAX = 0xA2, /* Read-Only word */ + PMBUS_MFR_PIN_MAX = 0xA3, /* Read-Only word */ + PMBUS_MFR_VOUT_MIN = 0xA4, /* Read-Only word */ + PMBUS_MFR_VOUT_MAX = 0xA5, /* Read-Only word */ + PMBUS_MFR_IOUT_MAX = 0xA6, /* Read-Only word */ + PMBUS_MFR_POUT_MAX = 0xA7, /* Read-Only word */ + PMBUS_MFR_TAMBIENT_MAX = 0xA8, /* Read-Only word */ + PMBUS_MFR_TAMBIENT_MIN = 0xA9, /* Read-Only word */ + PMBUS_MFR_EFFICIENCY_LL = 0xAA, /* Read-Only block 14 bytes */ + PMBUS_MFR_EFFICIENCY_HL = 0xAB, /* Read-Only block 14 bytes */ + PMBUS_MFR_PIN_ACCURACY = 0xAC, /* Read-Only byte */ + PMBUS_IC_DEVICE_ID = 0xAD, /* Read-Only block-read */ + PMBUS_IC_DEVICE_REV = 0xAE, /* Read-Only block-read */ + PMBUS_MFR_MAX_TEMP_1 = 0xC0, /* R/W word */ + PMBUS_MFR_MAX_TEMP_2 = 0xC1, /* R/W word */ + PMBUS_MFR_MAX_TEMP_3 = 0xC2, /* R/W word */ +}; + +/* STATUS_WORD */ +#define PB_STATUS_VOUT BIT(15) +#define PB_STATUS_IOUT_POUT BIT(14) +#define PB_STATUS_INPUT BIT(13) +#define PB_STATUS_WORD_MFR BIT(12) +#define PB_STATUS_POWER_GOOD_N BIT(11) +#define PB_STATUS_FAN BIT(10) +#define PB_STATUS_OTHER BIT(9) +#define PB_STATUS_UNKNOWN BIT(8) +/* STATUS_BYTE */ +#define PB_STATUS_BUSY BIT(7) +#define PB_STATUS_OFF BIT(6) +#define PB_STATUS_VOUT_OV BIT(5) +#define PB_STATUS_IOUT_OC BIT(4) +#define PB_STATUS_VIN_UV BIT(3) +#define PB_STATUS_TEMPERATURE BIT(2) +#define PB_STATUS_CML BIT(1) +#define PB_STATUS_NONE_ABOVE BIT(0) + +/* STATUS_VOUT */ +#define PB_STATUS_VOUT_OV_FAULT BIT(7) /* Output Overvoltage Fault */ +#define PB_STATUS_VOUT_OV_WARN BIT(6) /* Output Overvoltage Warning */ +#define PB_STATUS_VOUT_UV_WARN BIT(5) /* Output Undervoltage Warning */ +#define PB_STATUS_VOUT_UV_FAULT BIT(4) /* Output Undervoltage Fault */ +#define PB_STATUS_VOUT_MAX BIT(3) +#define PB_STATUS_VOUT_TON_MAX_FAULT BIT(2) +#define PB_STATUS_VOUT_TOFF_MAX_WARN BIT(1) + +/* STATUS_IOUT */ +#define PB_STATUS_IOUT_OC_FAULT BIT(7) /* Output Overcurrent Fault */ +#define PB_STATUS_IOUT_OC_LV_FAULT BIT(6) /* Output OC And Low Voltage Fault */ +#define PB_STATUS_IOUT_OC_WARN BIT(5) /* Output Overcurrent Warning */ +#define PB_STATUS_IOUT_UC_FAULT BIT(4) /* Output Undercurrent Fault */ +#define PB_STATUS_CURR_SHARE BIT(3) /* Current Share Fault */ +#define PB_STATUS_PWR_LIM_MODE BIT(2) /* In Power Limiting Mode */ +#define PB_STATUS_POUT_OP_FAULT BIT(1) /* Output Overpower Fault */ +#define PB_STATUS_POUT_OP_WARN BIT(0) /* Output Overpower Warning */ + +/* STATUS_INPUT */ +#define PB_STATUS_INPUT_VIN_OV_FAULT BIT(7) /* Input Overvoltage Fault */ +#define PB_STATUS_INPUT_VIN_OV_WARN BIT(6) /* Input Overvoltage Warning */ +#define PB_STATUS_INPUT_VIN_UV_WARN BIT(5) /* Input Undervoltage Warning */ +#define PB_STATUS_INPUT_VIN_UV_FAULT BIT(4) /* Input Undervoltage Fault */ +#define PB_STATUS_INPUT_IIN_OC_FAULT BIT(2) /* Input Overcurrent Fault */ +#define PB_STATUS_INPUT_IIN_OC_WARN BIT(1) /* Input Overcurrent Warning */ +#define PB_STATUS_INPUT_PIN_OP_WARN BIT(0) /* Input Overpower Warning */ + +/* STATUS_TEMPERATURE */ +#define PB_STATUS_OT_FAULT BIT(7) /* Overtemperature Fault */ +#define PB_STATUS_OT_WARN BIT(6) /* Overtemperature Warning */ +#define PB_STATUS_UT_WARN BIT(5) /* Undertemperature Warning */ +#define PB_STATUS_UT_FAULT BIT(4) /* Undertemperature Fault */ + +/* STATUS_CML */ +#define PB_CML_FAULT_INVALID_CMD BIT(7) /* Invalid/Unsupported Command */ +#define PB_CML_FAULT_INVALID_DATA BIT(6) /* Invalid/Unsupported Data */ +#define PB_CML_FAULT_PEC BIT(5) /* Packet Error Check Failed */ +#define PB_CML_FAULT_MEMORY BIT(4) /* Memory Fault Detected */ +#define PB_CML_FAULT_PROCESSOR BIT(3) /* Processor Fault Detected */ +#define PB_CML_FAULT_OTHER_COMM BIT(1) /* Other communication fault */ +#define PB_CML_FAULT_OTHER_MEM_LOGIC BIT(0) /* Other Memory Or Logic Fault */ + +/* OPERATION*/ +#define PB_OP_ON BIT(7) /* PSU is switched on */ +#define PB_OP_MARGIN_HIGH BIT(5) /* PSU vout is set to margin high */ +#define PB_OP_MARGIN_LOW BIT(4) /* PSU vout is set to margin low */ + +/* PAGES */ +#define PB_MAX_PAGES 0x1F +#define PB_ALL_PAGES 0xFF + +#define TYPE_PMBUS_DEVICE "pmbus-device" +OBJECT_DECLARE_TYPE(PMBusDevice, PMBusDeviceClass, + PMBUS_DEVICE) + +/* flags */ +#define PB_HAS_COEFFICIENTS BIT_ULL(9) +#define PB_HAS_VIN BIT_ULL(10) +#define PB_HAS_VOUT BIT_ULL(11) +#define PB_HAS_VOUT_MARGIN BIT_ULL(12) +#define PB_HAS_VIN_RATING BIT_ULL(13) +#define PB_HAS_VOUT_RATING BIT_ULL(14) +#define PB_HAS_VOUT_MODE BIT_ULL(15) +#define PB_HAS_IOUT BIT_ULL(21) +#define PB_HAS_IIN BIT_ULL(22) +#define PB_HAS_IOUT_RATING BIT_ULL(23) +#define PB_HAS_IIN_RATING BIT_ULL(24) +#define PB_HAS_IOUT_GAIN BIT_ULL(25) +#define PB_HAS_POUT BIT_ULL(30) +#define PB_HAS_PIN BIT_ULL(31) +#define PB_HAS_EIN BIT_ULL(32) +#define PB_HAS_EOUT BIT_ULL(33) +#define PB_HAS_POUT_RATING BIT_ULL(34) +#define PB_HAS_PIN_RATING BIT_ULL(35) +#define PB_HAS_TEMPERATURE BIT_ULL(40) +#define PB_HAS_TEMP2 BIT_ULL(41) +#define PB_HAS_TEMP3 BIT_ULL(42) +#define PB_HAS_TEMP_RATING BIT_ULL(43) +#define PB_HAS_MFR_INFO BIT_ULL(50) + +struct PMBusDeviceClass { + SMBusDeviceClass parent_class; + uint8_t device_num_pages; + + /** + * Implement quick_cmd, receive byte, and write_data to support non-standard + * PMBus functionality + */ + void (*quick_cmd)(PMBusDevice *dev, uint8_t read); + int (*write_data)(PMBusDevice *dev, const uint8_t *buf, uint8_t len); + uint8_t (*receive_byte)(PMBusDevice *dev); +}; + +/* + * According to the spec, each page may offer the full range of PMBus commands + * available for each output or non-PMBus device. + * Therefore, we can't assume that any registers will always be the same across + * all pages. + * The page 0xFF is intended for writes to all pages + */ +typedef struct PMBusPage { + uint64_t page_flags; + + uint8_t page; /* R/W byte */ + uint8_t operation; /* R/W byte */ + uint8_t on_off_config; /* R/W byte */ + uint8_t write_protect; /* R/W byte */ + uint8_t phase; /* R/W byte */ + uint8_t vout_mode; /* R/W byte */ + uint16_t vout_command; /* R/W word */ + uint16_t vout_trim; /* R/W word */ + uint16_t vout_cal_offset; /* R/W word */ + uint16_t vout_max; /* R/W word */ + uint16_t vout_margin_high; /* R/W word */ + uint16_t vout_margin_low; /* R/W word */ + uint16_t vout_transition_rate; /* R/W word */ + uint16_t vout_droop; /* R/W word */ + uint16_t vout_scale_loop; /* R/W word */ + uint16_t vout_scale_monitor; /* R/W word */ + uint8_t coefficients[5]; /* Read-only block 5 bytes */ + uint16_t pout_max; /* R/W word */ + uint16_t max_duty; /* R/W word */ + uint16_t frequency_switch; /* R/W word */ + uint16_t vin_on; /* R/W word */ + uint16_t vin_off; /* R/W word */ + uint16_t iout_cal_gain; /* R/W word */ + uint16_t iout_cal_offset; /* R/W word */ + uint8_t fan_config_1_2; /* R/W byte */ + uint16_t fan_command_1; /* R/W word */ + uint16_t fan_command_2; /* R/W word */ + uint8_t fan_config_3_4; /* R/W byte */ + uint16_t fan_command_3; /* R/W word */ + uint16_t fan_command_4; /* R/W word */ + uint16_t vout_ov_fault_limit; /* R/W word */ + uint8_t vout_ov_fault_response; /* R/W byte */ + uint16_t vout_ov_warn_limit; /* R/W word */ + uint16_t vout_uv_warn_limit; /* R/W word */ + uint16_t vout_uv_fault_limit; /* R/W word */ + uint8_t vout_uv_fault_response; /* R/W byte */ + uint16_t iout_oc_fault_limit; /* R/W word */ + uint8_t iout_oc_fault_response; /* R/W byte */ + uint16_t iout_oc_lv_fault_limit; /* R/W word */ + uint8_t iout_oc_lv_fault_response; /* R/W byte */ + uint16_t iout_oc_warn_limit; /* R/W word */ + uint16_t iout_uc_fault_limit; /* R/W word */ + uint8_t iout_uc_fault_response; /* R/W byte */ + uint16_t ot_fault_limit; /* R/W word */ + uint8_t ot_fault_response; /* R/W byte */ + uint16_t ot_warn_limit; /* R/W word */ + uint16_t ut_warn_limit; /* R/W word */ + uint16_t ut_fault_limit; /* R/W word */ + uint8_t ut_fault_response; /* R/W byte */ + uint16_t vin_ov_fault_limit; /* R/W word */ + uint8_t vin_ov_fault_response; /* R/W byte */ + uint16_t vin_ov_warn_limit; /* R/W word */ + uint16_t vin_uv_warn_limit; /* R/W word */ + uint16_t vin_uv_fault_limit; /* R/W word */ + uint8_t vin_uv_fault_response; /* R/W byte */ + uint16_t iin_oc_fault_limit; /* R/W word */ + uint8_t iin_oc_fault_response; /* R/W byte */ + uint16_t iin_oc_warn_limit; /* R/W word */ + uint16_t power_good_on; /* R/W word */ + uint16_t power_good_off; /* R/W word */ + uint16_t ton_delay; /* R/W word */ + uint16_t ton_rise; /* R/W word */ + uint16_t ton_max_fault_limit; /* R/W word */ + uint8_t ton_max_fault_response; /* R/W byte */ + uint16_t toff_delay; /* R/W word */ + uint16_t toff_fall; /* R/W word */ + uint16_t toff_max_warn_limit; /* R/W word */ + uint16_t pout_op_fault_limit; /* R/W word */ + uint8_t pout_op_fault_response; /* R/W byte */ + uint16_t pout_op_warn_limit; /* R/W word */ + uint16_t pin_op_warn_limit; /* R/W word */ + uint16_t status_word; /* R/W word */ + uint8_t status_vout; /* R/W byte */ + uint8_t status_iout; /* R/W byte */ + uint8_t status_input; /* R/W byte */ + uint8_t status_temperature; /* R/W byte */ + uint8_t status_cml; /* R/W byte */ + uint8_t status_other; /* R/W byte */ + uint8_t status_mfr_specific; /* R/W byte */ + uint8_t status_fans_1_2; /* R/W byte */ + uint8_t status_fans_3_4; /* R/W byte */ + uint8_t read_ein[5]; /* Read-Only block 5 bytes */ + uint8_t read_eout[5]; /* Read-Only block 5 bytes */ + uint16_t read_vin; /* Read-Only word */ + uint16_t read_iin; /* Read-Only word */ + uint16_t read_vcap; /* Read-Only word */ + uint16_t read_vout; /* Read-Only word */ + uint16_t read_iout; /* Read-Only word */ + uint16_t read_temperature_1; /* Read-Only word */ + uint16_t read_temperature_2; /* Read-Only word */ + uint16_t read_temperature_3; /* Read-Only word */ + uint16_t read_fan_speed_1; /* Read-Only word */ + uint16_t read_fan_speed_2; /* Read-Only word */ + uint16_t read_fan_speed_3; /* Read-Only word */ + uint16_t read_fan_speed_4; /* Read-Only word */ + uint16_t read_duty_cycle; /* Read-Only word */ + uint16_t read_frequency; /* Read-Only word */ + uint16_t read_pout; /* Read-Only word */ + uint16_t read_pin; /* Read-Only word */ + uint8_t revision; /* Read-Only byte */ + const char *mfr_id; /* R/W block */ + const char *mfr_model; /* R/W block */ + const char *mfr_revision; /* R/W block */ + const char *mfr_location; /* R/W block */ + const char *mfr_date; /* R/W block */ + const char *mfr_serial; /* R/W block */ + const char *app_profile_support; /* Read-Only block-read */ + uint16_t mfr_vin_min; /* Read-Only word */ + uint16_t mfr_vin_max; /* Read-Only word */ + uint16_t mfr_iin_max; /* Read-Only word */ + uint16_t mfr_pin_max; /* Read-Only word */ + uint16_t mfr_vout_min; /* Read-Only word */ + uint16_t mfr_vout_max; /* Read-Only word */ + uint16_t mfr_iout_max; /* Read-Only word */ + uint16_t mfr_pout_max; /* Read-Only word */ + uint16_t mfr_tambient_max; /* Read-Only word */ + uint16_t mfr_tambient_min; /* Read-Only word */ + uint8_t mfr_efficiency_ll[14]; /* Read-Only block 14 bytes */ + uint8_t mfr_efficiency_hl[14]; /* Read-Only block 14 bytes */ + uint8_t mfr_pin_accuracy; /* Read-Only byte */ + uint16_t mfr_max_temp_1; /* R/W word */ + uint16_t mfr_max_temp_2; /* R/W word */ + uint16_t mfr_max_temp_3; /* R/W word */ +} PMBusPage; + +/* State */ +struct PMBusDevice { + SMBusDevice smb; + + uint8_t num_pages; + uint8_t code; + uint8_t page; + + /* + * PMBus registers are stored in a PMBusPage structure allocated by + * calling pmbus_pages_alloc() + */ + PMBusPage *pages; + uint8_t capability; + + + int32_t in_buf_len; + uint8_t *in_buf; + int32_t out_buf_len; + uint8_t out_buf[SMBUS_DATA_MAX_LEN]; +}; + +/** + * Direct mode coefficients + * @var m - mantissa + * @var b - offset + * @var R - exponent + */ +typedef struct PMBusCoefficients { + int32_t m; /* mantissa */ + int64_t b; /* offset */ + int32_t R; /* exponent */ +} PMBusCoefficients; + +/** + * Convert sensor values to direct mode format + * + * Y = (m * x - b) * 10^R + * + * @return uint32_t + */ +uint16_t pmbus_data2direct_mode(PMBusCoefficients c, uint32_t value); + +/** + * Convert direct mode formatted data into sensor reading + * + * X = (Y * 10^-R - b) / m + * + * @return uint32_t + */ +uint32_t pmbus_direct_mode2data(PMBusCoefficients c, uint16_t value); + +/** + * @brief Send a block of data over PMBus + * Assumes that the bytes in the block are already ordered correctly, + * also assumes the length has been prepended to the block if necessary + * | low_byte | ... | high_byte | + * @param state - maintains state of the PMBus device + * @param data - byte array to be sent by device + * @param len - number + */ +void pmbus_send(PMBusDevice *state, const uint8_t *data, uint16_t len); +void pmbus_send8(PMBusDevice *state, uint8_t data); +void pmbus_send16(PMBusDevice *state, uint16_t data); +void pmbus_send32(PMBusDevice *state, uint32_t data); +void pmbus_send64(PMBusDevice *state, uint64_t data); + +/** + * @brief Send a string over PMBus with length prepended. + * Length is calculated using str_len() + */ +void pmbus_send_string(PMBusDevice *state, const char *data); + +/** + * @brief Receive data over PMBus + * These methods help track how much data is being received over PMBus + * Log to GUEST_ERROR if too much or too little is sent. + */ +uint8_t pmbus_receive8(PMBusDevice *pmdev); +uint16_t pmbus_receive16(PMBusDevice *pmdev); +uint32_t pmbus_receive32(PMBusDevice *pmdev); +uint64_t pmbus_receive64(PMBusDevice *pmdev); + +/** + * PMBus page config must be called before any page is first used. + * It will allocate memory for all the pages if needed. + * Passed in flags overwrite existing flags if any. + * @param page_index the page to which the flags are applied, setting page_index + * to 0xFF applies the passed in flags to all pages. + * @param flags + */ +int pmbus_page_config(PMBusDevice *pmdev, uint8_t page_index, uint64_t flags); + +/** + * Update the status registers when sensor values change. + * Useful if modifying sensors through qmp, this way status registers get + * updated + */ +void pmbus_check_limits(PMBusDevice *pmdev); + +extern const VMStateDescription vmstate_pmbus_device; + +#define VMSTATE_PMBUS_DEVICE(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(PMBusDevice), \ + .vmsd = &vmstate_pmbus_device, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, PMBusDevice), \ +} + +#endif From c93488f16b70debc0c168b4117531623b03d6bf0 Mon Sep 17 00:00:00 2001 From: Titus Rwantare Date: Thu, 8 Jul 2021 10:25:53 -0700 Subject: [PATCH 058/272] hw/misc: add ADM1272 device The ADM1272 is a PMBus compliant Hot Swap Controller and Digital Power Monitor by Analog Devices. This commit adds support for interfacing with it, and support for setting and monitoring sensor limits. Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADM1272.pdf Reviewed-by: Joel Stanley Reviewed-by: Hao Wu Signed-off-by: Titus Rwantare Message-Id: <20210708172556.1868139-3-titusr@google.com> [Moved the device to the sensor directory] [remove include of trace.h, it is not needed] Signed-off-by: Corey Minyard --- hw/arm/Kconfig | 1 + hw/sensor/Kconfig | 4 + hw/sensor/adm1272.c | 543 ++++++++++++++++++++++++++++++++++++++++++ hw/sensor/meson.build | 1 + 4 files changed, 549 insertions(+) create mode 100644 hw/sensor/adm1272.c diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 2ed1b09510..ae43f0a661 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -372,6 +372,7 @@ config XLNX_VERSAL config NPCM7XX bool select A9MPCORE + select ADM1272 select ARM_GIC select AT24C # EEPROM select PL310 # cache controller diff --git a/hw/sensor/Kconfig b/hw/sensor/Kconfig index 097cb8f11e..a34ffcb024 100644 --- a/hw/sensor/Kconfig +++ b/hw/sensor/Kconfig @@ -9,3 +9,7 @@ config TMP421 config EMC141X bool depends on I2C + +config ADM1272 + bool + depends on I2C diff --git a/hw/sensor/adm1272.c b/hw/sensor/adm1272.c new file mode 100644 index 0000000000..7310c769be --- /dev/null +++ b/hw/sensor/adm1272.c @@ -0,0 +1,543 @@ +/* + * Analog Devices ADM1272 High Voltage Positive Hot Swap Controller and Digital + * Power Monitor with PMBus + * + * Copyright 2021 Google LLC + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include +#include "hw/i2c/pmbus_device.h" +#include "hw/irq.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "qemu/log.h" +#include "qemu/module.h" + +#define TYPE_ADM1272 "adm1272" +#define ADM1272(obj) OBJECT_CHECK(ADM1272State, (obj), TYPE_ADM1272) + +#define ADM1272_RESTART_TIME 0xCC +#define ADM1272_MFR_PEAK_IOUT 0xD0 +#define ADM1272_MFR_PEAK_VIN 0xD1 +#define ADM1272_MFR_PEAK_VOUT 0xD2 +#define ADM1272_MFR_PMON_CONTROL 0xD3 +#define ADM1272_MFR_PMON_CONFIG 0xD4 +#define ADM1272_MFR_ALERT1_CONFIG 0xD5 +#define ADM1272_MFR_ALERT2_CONFIG 0xD6 +#define ADM1272_MFR_PEAK_TEMPERATURE 0xD7 +#define ADM1272_MFR_DEVICE_CONFIG 0xD8 +#define ADM1272_MFR_POWER_CYCLE 0xD9 +#define ADM1272_MFR_PEAK_PIN 0xDA +#define ADM1272_MFR_READ_PIN_EXT 0xDB +#define ADM1272_MFR_READ_EIN_EXT 0xDC + +#define ADM1272_HYSTERESIS_LOW 0xF2 +#define ADM1272_HYSTERESIS_HIGH 0xF3 +#define ADM1272_STATUS_HYSTERESIS 0xF4 +#define ADM1272_STATUS_GPIO 0xF5 +#define ADM1272_STRT_UP_IOUT_LIM 0xF6 + +/* Defaults */ +#define ADM1272_OPERATION_DEFAULT 0x80 +#define ADM1272_CAPABILITY_DEFAULT 0xB0 +#define ADM1272_CAPABILITY_NO_PEC 0x30 +#define ADM1272_DIRECT_MODE 0x40 +#define ADM1272_HIGH_LIMIT_DEFAULT 0x0FFF +#define ADM1272_PIN_OP_DEFAULT 0x7FFF +#define ADM1272_PMBUS_REVISION_DEFAULT 0x22 +#define ADM1272_MFR_ID_DEFAULT "ADI" +#define ADM1272_MODEL_DEFAULT "ADM1272-A1" +#define ADM1272_MFR_DEFAULT_REVISION "25" +#define ADM1272_DEFAULT_DATE "160301" +#define ADM1272_RESTART_TIME_DEFAULT 0x64 +#define ADM1272_PMON_CONTROL_DEFAULT 0x1 +#define ADM1272_PMON_CONFIG_DEFAULT 0x3F35 +#define ADM1272_DEVICE_CONFIG_DEFAULT 0x8 +#define ADM1272_HYSTERESIS_HIGH_DEFAULT 0xFFFF +#define ADM1272_STRT_UP_IOUT_LIM_DEFAULT 0x000F +#define ADM1272_VOLT_DEFAULT 12000 +#define ADM1272_IOUT_DEFAULT 25000 +#define ADM1272_PWR_DEFAULT 300 /* 12V 25A */ +#define ADM1272_SHUNT 300 /* micro-ohms */ +#define ADM1272_VOLTAGE_COEFF_DEFAULT 1 +#define ADM1272_CURRENT_COEFF_DEFAULT 3 +#define ADM1272_PWR_COEFF_DEFAULT 7 +#define ADM1272_IOUT_OFFSET 0x5000 +#define ADM1272_IOUT_OFFSET 0x5000 + + +typedef struct ADM1272State { + PMBusDevice parent; + + uint64_t ein_ext; + uint32_t pin_ext; + uint8_t restart_time; + + uint16_t peak_vin; + uint16_t peak_vout; + uint16_t peak_iout; + uint16_t peak_temperature; + uint16_t peak_pin; + + uint8_t pmon_control; + uint16_t pmon_config; + uint16_t alert1_config; + uint16_t alert2_config; + uint16_t device_config; + + uint16_t hysteresis_low; + uint16_t hysteresis_high; + uint8_t status_hysteresis; + uint8_t status_gpio; + + uint16_t strt_up_iout_lim; + +} ADM1272State; + +static const PMBusCoefficients adm1272_coefficients[] = { + [0] = { 6770, 0, -2 }, /* voltage, vrange 60V */ + [1] = { 4062, 0, -2 }, /* voltage, vrange 100V */ + [2] = { 1326, 20480, -1 }, /* current, vsense range 15mV */ + [3] = { 663, 20480, -1 }, /* current, vsense range 30mV */ + [4] = { 3512, 0, -2 }, /* power, vrange 60V, irange 15mV */ + [5] = { 21071, 0, -3 }, /* power, vrange 100V, irange 15mV */ + [6] = { 17561, 0, -3 }, /* power, vrange 60V, irange 30mV */ + [7] = { 10535, 0, -3 }, /* power, vrange 100V, irange 30mV */ + [8] = { 42, 31871, -1 }, /* temperature */ +}; + +static void adm1272_check_limits(ADM1272State *s) +{ + PMBusDevice *pmdev = PMBUS_DEVICE(s); + + pmbus_check_limits(pmdev); + + if (pmdev->pages[0].read_vout > s->peak_vout) { + s->peak_vout = pmdev->pages[0].read_vout; + } + + if (pmdev->pages[0].read_vin > s->peak_vin) { + s->peak_vin = pmdev->pages[0].read_vin; + } + + if (pmdev->pages[0].read_iout > s->peak_iout) { + s->peak_iout = pmdev->pages[0].read_iout; + } + + if (pmdev->pages[0].read_temperature_1 > s->peak_temperature) { + s->peak_temperature = pmdev->pages[0].read_temperature_1; + } + + if (pmdev->pages[0].read_pin > s->peak_pin) { + s->peak_pin = pmdev->pages[0].read_pin; + } +} + +static uint16_t adm1272_millivolts_to_direct(uint32_t value) +{ + PMBusCoefficients c = adm1272_coefficients[ADM1272_VOLTAGE_COEFF_DEFAULT]; + c.b = c.b * 1000; + c.R = c.R - 3; + return pmbus_data2direct_mode(c, value); +} + +static uint32_t adm1272_direct_to_millivolts(uint16_t value) +{ + PMBusCoefficients c = adm1272_coefficients[ADM1272_VOLTAGE_COEFF_DEFAULT]; + c.b = c.b * 1000; + c.R = c.R - 3; + return pmbus_direct_mode2data(c, value); +} + +static uint16_t adm1272_milliamps_to_direct(uint32_t value) +{ + PMBusCoefficients c = adm1272_coefficients[ADM1272_CURRENT_COEFF_DEFAULT]; + /* Y = (m * r_sense * x - b) * 10^R */ + c.m = c.m * ADM1272_SHUNT / 1000; /* micro-ohms */ + c.b = c.b * 1000; + c.R = c.R - 3; + return pmbus_data2direct_mode(c, value); +} + +static uint32_t adm1272_direct_to_milliamps(uint16_t value) +{ + PMBusCoefficients c = adm1272_coefficients[ADM1272_CURRENT_COEFF_DEFAULT]; + c.m = c.m * ADM1272_SHUNT / 1000; + c.b = c.b * 1000; + c.R = c.R - 3; + return pmbus_direct_mode2data(c, value); +} + +static uint16_t adm1272_watts_to_direct(uint32_t value) +{ + PMBusCoefficients c = adm1272_coefficients[ADM1272_PWR_COEFF_DEFAULT]; + c.m = c.m * ADM1272_SHUNT / 1000; + return pmbus_data2direct_mode(c, value); +} + +static uint32_t adm1272_direct_to_watts(uint16_t value) +{ + PMBusCoefficients c = adm1272_coefficients[ADM1272_PWR_COEFF_DEFAULT]; + c.m = c.m * ADM1272_SHUNT / 1000; + return pmbus_direct_mode2data(c, value); +} + +static void adm1272_exit_reset(Object *obj) +{ + ADM1272State *s = ADM1272(obj); + PMBusDevice *pmdev = PMBUS_DEVICE(obj); + + pmdev->page = 0; + pmdev->pages[0].operation = ADM1272_OPERATION_DEFAULT; + + + pmdev->capability = ADM1272_CAPABILITY_NO_PEC; + pmdev->pages[0].revision = ADM1272_PMBUS_REVISION_DEFAULT; + pmdev->pages[0].vout_mode = ADM1272_DIRECT_MODE; + pmdev->pages[0].vout_ov_warn_limit = ADM1272_HIGH_LIMIT_DEFAULT; + pmdev->pages[0].vout_uv_warn_limit = 0; + pmdev->pages[0].iout_oc_warn_limit = ADM1272_HIGH_LIMIT_DEFAULT; + pmdev->pages[0].ot_fault_limit = ADM1272_HIGH_LIMIT_DEFAULT; + pmdev->pages[0].ot_warn_limit = ADM1272_HIGH_LIMIT_DEFAULT; + pmdev->pages[0].vin_ov_warn_limit = ADM1272_HIGH_LIMIT_DEFAULT; + pmdev->pages[0].vin_uv_warn_limit = 0; + pmdev->pages[0].pin_op_warn_limit = ADM1272_PIN_OP_DEFAULT; + + pmdev->pages[0].status_word = 0; + pmdev->pages[0].status_vout = 0; + pmdev->pages[0].status_iout = 0; + pmdev->pages[0].status_input = 0; + pmdev->pages[0].status_temperature = 0; + pmdev->pages[0].status_mfr_specific = 0; + + pmdev->pages[0].read_vin + = adm1272_millivolts_to_direct(ADM1272_VOLT_DEFAULT); + pmdev->pages[0].read_vout + = adm1272_millivolts_to_direct(ADM1272_VOLT_DEFAULT); + pmdev->pages[0].read_iout + = adm1272_milliamps_to_direct(ADM1272_IOUT_DEFAULT); + pmdev->pages[0].read_temperature_1 = 0; + pmdev->pages[0].read_pin = adm1272_watts_to_direct(ADM1272_PWR_DEFAULT); + pmdev->pages[0].revision = ADM1272_PMBUS_REVISION_DEFAULT; + pmdev->pages[0].mfr_id = ADM1272_MFR_ID_DEFAULT; + pmdev->pages[0].mfr_model = ADM1272_MODEL_DEFAULT; + pmdev->pages[0].mfr_revision = ADM1272_MFR_DEFAULT_REVISION; + pmdev->pages[0].mfr_date = ADM1272_DEFAULT_DATE; + + s->pin_ext = 0; + s->ein_ext = 0; + s->restart_time = ADM1272_RESTART_TIME_DEFAULT; + + s->peak_vin = 0; + s->peak_vout = 0; + s->peak_iout = 0; + s->peak_temperature = 0; + s->peak_pin = 0; + + s->pmon_control = ADM1272_PMON_CONTROL_DEFAULT; + s->pmon_config = ADM1272_PMON_CONFIG_DEFAULT; + s->alert1_config = 0; + s->alert2_config = 0; + s->device_config = ADM1272_DEVICE_CONFIG_DEFAULT; + + s->hysteresis_low = 0; + s->hysteresis_high = ADM1272_HYSTERESIS_HIGH_DEFAULT; + s->status_hysteresis = 0; + s->status_gpio = 0; + + s->strt_up_iout_lim = ADM1272_STRT_UP_IOUT_LIM_DEFAULT; +} + +static uint8_t adm1272_read_byte(PMBusDevice *pmdev) +{ + ADM1272State *s = ADM1272(pmdev); + + switch (pmdev->code) { + case ADM1272_RESTART_TIME: + pmbus_send8(pmdev, s->restart_time); + break; + + case ADM1272_MFR_PEAK_IOUT: + pmbus_send16(pmdev, s->peak_iout); + break; + + case ADM1272_MFR_PEAK_VIN: + pmbus_send16(pmdev, s->peak_vin); + break; + + case ADM1272_MFR_PEAK_VOUT: + pmbus_send16(pmdev, s->peak_vout); + break; + + case ADM1272_MFR_PMON_CONTROL: + pmbus_send8(pmdev, s->pmon_control); + break; + + case ADM1272_MFR_PMON_CONFIG: + pmbus_send16(pmdev, s->pmon_config); + break; + + case ADM1272_MFR_ALERT1_CONFIG: + pmbus_send16(pmdev, s->alert1_config); + break; + + case ADM1272_MFR_ALERT2_CONFIG: + pmbus_send16(pmdev, s->alert2_config); + break; + + case ADM1272_MFR_PEAK_TEMPERATURE: + pmbus_send16(pmdev, s->peak_temperature); + break; + + case ADM1272_MFR_DEVICE_CONFIG: + pmbus_send16(pmdev, s->device_config); + break; + + case ADM1272_MFR_PEAK_PIN: + pmbus_send16(pmdev, s->peak_pin); + break; + + case ADM1272_MFR_READ_PIN_EXT: + pmbus_send32(pmdev, s->pin_ext); + break; + + case ADM1272_MFR_READ_EIN_EXT: + pmbus_send64(pmdev, s->ein_ext); + break; + + case ADM1272_HYSTERESIS_LOW: + pmbus_send16(pmdev, s->hysteresis_low); + break; + + case ADM1272_HYSTERESIS_HIGH: + pmbus_send16(pmdev, s->hysteresis_high); + break; + + case ADM1272_STATUS_HYSTERESIS: + pmbus_send16(pmdev, s->status_hysteresis); + break; + + case ADM1272_STATUS_GPIO: + pmbus_send16(pmdev, s->status_gpio); + break; + + case ADM1272_STRT_UP_IOUT_LIM: + pmbus_send16(pmdev, s->strt_up_iout_lim); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: reading from unsupported register: 0x%02x\n", + __func__, pmdev->code); + return 0xFF; + break; + } + + return 0; +} + +static int adm1272_write_data(PMBusDevice *pmdev, const uint8_t *buf, + uint8_t len) +{ + ADM1272State *s = ADM1272(pmdev); + + if (len == 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__); + return -1; + } + + pmdev->code = buf[0]; /* PMBus command code */ + + if (len == 1) { + return 0; + } + + /* Exclude command code from buffer */ + buf++; + len--; + + switch (pmdev->code) { + + case ADM1272_RESTART_TIME: + s->restart_time = pmbus_receive8(pmdev); + break; + + case ADM1272_MFR_PMON_CONTROL: + s->pmon_control = pmbus_receive8(pmdev); + break; + + case ADM1272_MFR_PMON_CONFIG: + s->pmon_config = pmbus_receive16(pmdev); + break; + + case ADM1272_MFR_ALERT1_CONFIG: + s->alert1_config = pmbus_receive16(pmdev); + break; + + case ADM1272_MFR_ALERT2_CONFIG: + s->alert2_config = pmbus_receive16(pmdev); + break; + + case ADM1272_MFR_DEVICE_CONFIG: + s->device_config = pmbus_receive16(pmdev); + break; + + case ADM1272_MFR_POWER_CYCLE: + adm1272_exit_reset((Object *)s); + break; + + case ADM1272_HYSTERESIS_LOW: + s->hysteresis_low = pmbus_receive16(pmdev); + break; + + case ADM1272_HYSTERESIS_HIGH: + s->hysteresis_high = pmbus_receive16(pmdev); + break; + + case ADM1272_STRT_UP_IOUT_LIM: + s->strt_up_iout_lim = pmbus_receive16(pmdev); + adm1272_check_limits(s); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: writing to unsupported register: 0x%02x\n", + __func__, pmdev->code); + break; + } + return 0; +} + +static void adm1272_get(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) +{ + uint16_t value; + + if (strcmp(name, "vin") == 0 || strcmp(name, "vout") == 0) { + value = adm1272_direct_to_millivolts(*(uint16_t *)opaque); + } else if (strcmp(name, "iout") == 0) { + value = adm1272_direct_to_milliamps(*(uint16_t *)opaque); + } else if (strcmp(name, "pin") == 0) { + value = adm1272_direct_to_watts(*(uint16_t *)opaque); + } else { + value = *(uint16_t *)opaque; + } + + visit_type_uint16(v, name, &value, errp); +} + +static void adm1272_set(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) +{ + ADM1272State *s = ADM1272(obj); + uint16_t *internal = opaque; + uint16_t value; + + if (!visit_type_uint16(v, name, &value, errp)) { + return; + } + + if (strcmp(name, "vin") == 0 || strcmp(name, "vout") == 0) { + *internal = adm1272_millivolts_to_direct(value); + } else if (strcmp(name, "iout") == 0) { + *internal = adm1272_milliamps_to_direct(value); + } else if (strcmp(name, "pin") == 0) { + *internal = adm1272_watts_to_direct(value); + } else { + *internal = value; + } + + adm1272_check_limits(s); +} + +static const VMStateDescription vmstate_adm1272 = { + .name = "ADM1272", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]){ + VMSTATE_PMBUS_DEVICE(parent, ADM1272State), + VMSTATE_UINT64(ein_ext, ADM1272State), + VMSTATE_UINT32(pin_ext, ADM1272State), + VMSTATE_UINT8(restart_time, ADM1272State), + + VMSTATE_UINT16(peak_vin, ADM1272State), + VMSTATE_UINT16(peak_vout, ADM1272State), + VMSTATE_UINT16(peak_iout, ADM1272State), + VMSTATE_UINT16(peak_temperature, ADM1272State), + VMSTATE_UINT16(peak_pin, ADM1272State), + + VMSTATE_UINT8(pmon_control, ADM1272State), + VMSTATE_UINT16(pmon_config, ADM1272State), + VMSTATE_UINT16(alert1_config, ADM1272State), + VMSTATE_UINT16(alert2_config, ADM1272State), + VMSTATE_UINT16(device_config, ADM1272State), + + VMSTATE_UINT16(hysteresis_low, ADM1272State), + VMSTATE_UINT16(hysteresis_high, ADM1272State), + VMSTATE_UINT8(status_hysteresis, ADM1272State), + VMSTATE_UINT8(status_gpio, ADM1272State), + + VMSTATE_UINT16(strt_up_iout_lim, ADM1272State), + VMSTATE_END_OF_LIST() + } +}; + +static void adm1272_init(Object *obj) +{ + PMBusDevice *pmdev = PMBUS_DEVICE(obj); + uint64_t flags = PB_HAS_VOUT_MODE | PB_HAS_VOUT | PB_HAS_VIN | PB_HAS_IOUT | + PB_HAS_PIN | PB_HAS_TEMPERATURE | PB_HAS_MFR_INFO; + + pmbus_page_config(pmdev, 0, flags); + + object_property_add(obj, "vin", "uint16", + adm1272_get, + adm1272_set, NULL, &pmdev->pages[0].read_vin); + + object_property_add(obj, "vout", "uint16", + adm1272_get, + adm1272_set, NULL, &pmdev->pages[0].read_vout); + + object_property_add(obj, "iout", "uint16", + adm1272_get, + adm1272_set, NULL, &pmdev->pages[0].read_iout); + + object_property_add(obj, "pin", "uint16", + adm1272_get, + adm1272_set, NULL, &pmdev->pages[0].read_pin); + +} + +static void adm1272_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + PMBusDeviceClass *k = PMBUS_DEVICE_CLASS(klass); + + dc->desc = "Analog Devices ADM1272 Hot Swap controller"; + dc->vmsd = &vmstate_adm1272; + k->write_data = adm1272_write_data; + k->receive_byte = adm1272_read_byte; + k->device_num_pages = 1; + + rc->phases.exit = adm1272_exit_reset; +} + +static const TypeInfo adm1272_info = { + .name = TYPE_ADM1272, + .parent = TYPE_PMBUS_DEVICE, + .instance_size = sizeof(ADM1272State), + .instance_init = adm1272_init, + .class_init = adm1272_class_init, +}; + +static void adm1272_register_types(void) +{ + type_register_static(&adm1272_info); +} + +type_init(adm1272_register_types) diff --git a/hw/sensor/meson.build b/hw/sensor/meson.build index 9e0f3ab1fd..8b319b5826 100644 --- a/hw/sensor/meson.build +++ b/hw/sensor/meson.build @@ -1,3 +1,4 @@ softmmu_ss.add(when: 'CONFIG_TMP105', if_true: files('tmp105.c')) softmmu_ss.add(when: 'CONFIG_TMP421', if_true: files('tmp421.c')) softmmu_ss.add(when: 'CONFIG_EMC141X', if_true: files('emc141x.c')) +softmmu_ss.add(when: 'CONFIG_ADM1272', if_true: files('adm1272.c')) From c0167539bae6da6baa07fe660d6bed07ed16c3e5 Mon Sep 17 00:00:00 2001 From: Titus Rwantare Date: Thu, 8 Jul 2021 10:25:54 -0700 Subject: [PATCH 059/272] tests/qtest: add tests for ADM1272 device model Signed-off-by: Titus Rwantare Reviewed-by: Joel Stanley Message-Id: <20210708172556.1868139-4-titusr@google.com> Signed-off-by: Corey Minyard --- tests/qtest/adm1272-test.c | 445 +++++++++++++++++++++++++++++++++++++ tests/qtest/meson.build | 1 + 2 files changed, 446 insertions(+) create mode 100644 tests/qtest/adm1272-test.c diff --git a/tests/qtest/adm1272-test.c b/tests/qtest/adm1272-test.c new file mode 100644 index 0000000000..63f8514801 --- /dev/null +++ b/tests/qtest/adm1272-test.c @@ -0,0 +1,445 @@ +/* + * QTests for the ADM1272 hotswap controller + * + * Copyright 2021 Google LLC + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include +#include "hw/i2c/pmbus_device.h" +#include "libqtest-single.h" +#include "libqos/qgraph.h" +#include "libqos/i2c.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qnum.h" +#include "qemu/bitops.h" + +#define TEST_ID "adm1272-test" +#define TEST_ADDR (0x10) + +#define ADM1272_RESTART_TIME 0xCC +#define ADM1272_MFR_PEAK_IOUT 0xD0 +#define ADM1272_MFR_PEAK_VIN 0xD1 +#define ADM1272_MFR_PEAK_VOUT 0xD2 +#define ADM1272_MFR_PMON_CONTROL 0xD3 +#define ADM1272_MFR_PMON_CONFIG 0xD4 +#define ADM1272_MFR_ALERT1_CONFIG 0xD5 +#define ADM1272_MFR_ALERT2_CONFIG 0xD6 +#define ADM1272_MFR_PEAK_TEMPERATURE 0xD7 +#define ADM1272_MFR_DEVICE_CONFIG 0xD8 +#define ADM1272_MFR_POWER_CYCLE 0xD9 +#define ADM1272_MFR_PEAK_PIN 0xDA +#define ADM1272_MFR_READ_PIN_EXT 0xDB +#define ADM1272_MFR_READ_EIN_EXT 0xDC + +#define ADM1272_HYSTERESIS_LOW 0xF2 +#define ADM1272_HYSTERESIS_HIGH 0xF3 +#define ADM1272_STATUS_HYSTERESIS 0xF4 +#define ADM1272_STATUS_GPIO 0xF5 +#define ADM1272_STRT_UP_IOUT_LIM 0xF6 + +/* Defaults */ +#define ADM1272_OPERATION_DEFAULT 0x80 +#define ADM1272_CAPABILITY_DEFAULT 0xB0 +#define ADM1272_CAPABILITY_NO_PEC 0x30 +#define ADM1272_DIRECT_MODE 0x40 +#define ADM1272_HIGH_LIMIT_DEFAULT 0x0FFF +#define ADM1272_PIN_OP_DEFAULT 0x7FFF +#define ADM1272_PMBUS_REVISION_DEFAULT 0x22 +#define ADM1272_MFR_ID_DEFAULT "ADI" +#define ADM1272_MODEL_DEFAULT "ADM1272-A1" +#define ADM1272_MFR_DEFAULT_REVISION "25" +#define ADM1272_DEFAULT_DATE "160301" +#define ADM1272_RESTART_TIME_DEFAULT 0x64 +#define ADM1272_PMON_CONTROL_DEFAULT 0x1 +#define ADM1272_PMON_CONFIG_DEFAULT 0x3F35 +#define ADM1272_DEVICE_CONFIG_DEFAULT 0x8 +#define ADM1272_HYSTERESIS_HIGH_DEFAULT 0xFFFF +#define ADM1272_STRT_UP_IOUT_LIM_DEFAULT 0x000F +#define ADM1272_VOLT_DEFAULT 12000 +#define ADM1272_IOUT_DEFAULT 25000 +#define ADM1272_PWR_DEFAULT 300 /* 12V 25A */ +#define ADM1272_SHUNT 300 /* micro-ohms */ +#define ADM1272_VOLTAGE_COEFF_DEFAULT 1 +#define ADM1272_CURRENT_COEFF_DEFAULT 3 +#define ADM1272_PWR_COEFF_DEFAULT 7 +#define ADM1272_IOUT_OFFSET 0x5000 +#define ADM1272_IOUT_OFFSET 0x5000 + +static const PMBusCoefficients adm1272_coefficients[] = { + [0] = { 6770, 0, -2 }, /* voltage, vrange 60V */ + [1] = { 4062, 0, -2 }, /* voltage, vrange 100V */ + [2] = { 1326, 20480, -1 }, /* current, vsense range 15mV */ + [3] = { 663, 20480, -1 }, /* current, vsense range 30mV */ + [4] = { 3512, 0, -2 }, /* power, vrange 60V, irange 15mV */ + [5] = { 21071, 0, -3 }, /* power, vrange 100V, irange 15mV */ + [6] = { 17561, 0, -3 }, /* power, vrange 60V, irange 30mV */ + [7] = { 10535, 0, -3 }, /* power, vrange 100V, irange 30mV */ + [8] = { 42, 31871, -1 }, /* temperature */ +}; + +uint16_t pmbus_data2direct_mode(PMBusCoefficients c, uint32_t value) +{ + /* R is usually negative to fit large readings into 16 bits */ + uint16_t y = (c.m * value + c.b) * pow(10, c.R); + return y; +} + +uint32_t pmbus_direct_mode2data(PMBusCoefficients c, uint16_t value) +{ + /* X = (Y * 10^-R - b) / m */ + uint32_t x = (value / pow(10, c.R) - c.b) / c.m; + return x; +} + + +static uint16_t adm1272_millivolts_to_direct(uint32_t value) +{ + PMBusCoefficients c = adm1272_coefficients[ADM1272_VOLTAGE_COEFF_DEFAULT]; + c.b = c.b * 1000; + c.R = c.R - 3; + return pmbus_data2direct_mode(c, value); +} + +static uint32_t adm1272_direct_to_millivolts(uint16_t value) +{ + PMBusCoefficients c = adm1272_coefficients[ADM1272_VOLTAGE_COEFF_DEFAULT]; + c.b = c.b * 1000; + c.R = c.R - 3; + return pmbus_direct_mode2data(c, value); +} + +static uint16_t adm1272_milliamps_to_direct(uint32_t value) +{ + PMBusCoefficients c = adm1272_coefficients[ADM1272_CURRENT_COEFF_DEFAULT]; + /* Y = (m * r_sense * x - b) * 10^R */ + c.m = c.m * ADM1272_SHUNT / 1000; /* micro-ohms */ + c.b = c.b * 1000; + c.R = c.R - 3; + return pmbus_data2direct_mode(c, value); +} + +static uint32_t adm1272_direct_to_milliamps(uint16_t value) +{ + PMBusCoefficients c = adm1272_coefficients[ADM1272_CURRENT_COEFF_DEFAULT]; + c.m = c.m * ADM1272_SHUNT / 1000; + c.b = c.b * 1000; + c.R = c.R - 3; + return pmbus_direct_mode2data(c, value); +} + +static uint16_t adm1272_watts_to_direct(uint32_t value) +{ + PMBusCoefficients c = adm1272_coefficients[ADM1272_PWR_COEFF_DEFAULT]; + c.m = c.m * ADM1272_SHUNT / 1000; + return pmbus_data2direct_mode(c, value); +} + +static uint32_t adm1272_direct_to_watts(uint16_t value) +{ + PMBusCoefficients c = adm1272_coefficients[ADM1272_PWR_COEFF_DEFAULT]; + c.m = c.m * ADM1272_SHUNT / 1000; + return pmbus_direct_mode2data(c, value); +} + +static uint16_t qmp_adm1272_get(const char *id, const char *property) +{ + QDict *response; + uint64_t ret; + + response = qmp("{ 'execute': 'qom-get', 'arguments': { 'path': %s, " + "'property': %s } }", id, property); + g_assert(qdict_haskey(response, "return")); + ret = qnum_get_uint(qobject_to(QNum, qdict_get(response, "return"))); + qobject_unref(response); + return ret; +} + +static void qmp_adm1272_set(const char *id, + const char *property, + uint16_t value) +{ + QDict *response; + + response = qmp("{ 'execute': 'qom-set', 'arguments': { 'path': %s, " + "'property': %s, 'value': %u } }", id, property, value); + g_assert(qdict_haskey(response, "return")); + qobject_unref(response); +} + +/* PMBus commands are little endian vs i2c_set16 in i2c.h which is big endian */ +static uint16_t adm1272_i2c_get16(QI2CDevice *i2cdev, uint8_t reg) +{ + uint8_t resp[2]; + i2c_read_block(i2cdev, reg, resp, sizeof(resp)); + return (resp[1] << 8) | resp[0]; +} + +/* PMBus commands are little endian vs i2c_set16 in i2c.h which is big endian */ +static void adm1272_i2c_set16(QI2CDevice *i2cdev, uint8_t reg, uint16_t value) +{ + uint8_t data[2]; + + data[0] = value & 255; + data[1] = value >> 8; + i2c_write_block(i2cdev, reg, data, sizeof(data)); +} + +static void test_defaults(void *obj, void *data, QGuestAllocator *alloc) +{ + uint16_t value, i2c_value; + int16_t err; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + value = qmp_adm1272_get(TEST_ID, "vout"); + err = ADM1272_VOLT_DEFAULT - value; + g_assert_cmpuint(abs(err), <, ADM1272_VOLT_DEFAULT / 20); + + i2c_value = i2c_get8(i2cdev, PMBUS_OPERATION); + g_assert_cmphex(i2c_value, ==, ADM1272_OPERATION_DEFAULT); + + i2c_value = i2c_get8(i2cdev, PMBUS_VOUT_MODE); + g_assert_cmphex(i2c_value, ==, ADM1272_DIRECT_MODE); + + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_VOUT_OV_WARN_LIMIT); + g_assert_cmphex(i2c_value, ==, ADM1272_HIGH_LIMIT_DEFAULT); + + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_VOUT_UV_WARN_LIMIT); + g_assert_cmphex(i2c_value, ==, 0); + + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_IOUT_OC_WARN_LIMIT); + g_assert_cmphex(i2c_value, ==, ADM1272_HIGH_LIMIT_DEFAULT); + + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_OT_FAULT_LIMIT); + g_assert_cmphex(i2c_value, ==, ADM1272_HIGH_LIMIT_DEFAULT); + + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_OT_WARN_LIMIT); + g_assert_cmphex(i2c_value, ==, ADM1272_HIGH_LIMIT_DEFAULT); + + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_VIN_OV_WARN_LIMIT); + g_assert_cmphex(i2c_value, ==, ADM1272_HIGH_LIMIT_DEFAULT); + + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_VIN_UV_WARN_LIMIT); + g_assert_cmphex(i2c_value, ==, 0); + + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_PIN_OP_WARN_LIMIT); + g_assert_cmphex(i2c_value, ==, ADM1272_PIN_OP_DEFAULT); + + i2c_value = i2c_get8(i2cdev, PMBUS_REVISION); + g_assert_cmphex(i2c_value, ==, ADM1272_PMBUS_REVISION_DEFAULT); + + i2c_value = i2c_get8(i2cdev, ADM1272_MFR_PMON_CONTROL); + g_assert_cmphex(i2c_value, ==, ADM1272_PMON_CONTROL_DEFAULT); + + i2c_value = adm1272_i2c_get16(i2cdev, ADM1272_MFR_PMON_CONFIG); + g_assert_cmphex(i2c_value, ==, ADM1272_PMON_CONFIG_DEFAULT); + + i2c_value = adm1272_i2c_get16(i2cdev, ADM1272_MFR_DEVICE_CONFIG); + g_assert_cmphex(i2c_value, ==, ADM1272_DEVICE_CONFIG_DEFAULT); + + i2c_value = adm1272_i2c_get16(i2cdev, ADM1272_HYSTERESIS_HIGH); + g_assert_cmphex(i2c_value, ==, ADM1272_HYSTERESIS_HIGH_DEFAULT); + + i2c_value = adm1272_i2c_get16(i2cdev, ADM1272_STRT_UP_IOUT_LIM); + g_assert_cmphex(i2c_value, ==, ADM1272_STRT_UP_IOUT_LIM_DEFAULT); +} + +/* test qmp access */ +static void test_tx_rx(void *obj, void *data, QGuestAllocator *alloc) +{ + uint16_t i2c_value, value, i2c_voltage, i2c_pwr, lossy_value; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + /* converting to direct mode is lossy - we generate the same loss here */ + lossy_value = + adm1272_direct_to_millivolts(adm1272_millivolts_to_direct(1000)); + qmp_adm1272_set(TEST_ID, "vin", 1000); + value = qmp_adm1272_get(TEST_ID, "vin"); + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_VIN); + i2c_voltage = adm1272_direct_to_millivolts(i2c_value); + g_assert_cmpuint(value, ==, i2c_voltage); + g_assert_cmpuint(i2c_voltage, ==, lossy_value); + + lossy_value = + adm1272_direct_to_millivolts(adm1272_millivolts_to_direct(1500)); + qmp_adm1272_set(TEST_ID, "vout", 1500); + value = qmp_adm1272_get(TEST_ID, "vout"); + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_VOUT); + i2c_voltage = adm1272_direct_to_millivolts(i2c_value); + g_assert_cmpuint(value, ==, i2c_voltage); + g_assert_cmpuint(i2c_voltage, ==, lossy_value); + + lossy_value = + adm1272_direct_to_milliamps(adm1272_milliamps_to_direct(1600)); + qmp_adm1272_set(TEST_ID, "iout", 1600); + value = qmp_adm1272_get(TEST_ID, "iout"); + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_IOUT); + i2c_value = adm1272_direct_to_milliamps(i2c_value); + g_assert_cmphex(value, ==, i2c_value); + g_assert_cmphex(i2c_value, ==, lossy_value); + + lossy_value = + adm1272_direct_to_watts(adm1272_watts_to_direct(320)); + qmp_adm1272_set(TEST_ID, "pin", 320); + value = qmp_adm1272_get(TEST_ID, "pin"); + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_PIN); + i2c_pwr = adm1272_direct_to_watts(i2c_value); + g_assert_cmphex(value, ==, i2c_pwr); + g_assert_cmphex(i2c_pwr, ==, lossy_value); +} + +/* test r/w registers */ +static void test_rw_regs(void *obj, void *data, QGuestAllocator *alloc) +{ + uint16_t i2c_value; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + adm1272_i2c_set16(i2cdev, PMBUS_VOUT_OV_WARN_LIMIT, 0xABCD); + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_VOUT_OV_WARN_LIMIT); + g_assert_cmphex(i2c_value, ==, 0xABCD); + + adm1272_i2c_set16(i2cdev, PMBUS_VOUT_UV_WARN_LIMIT, 0xCDEF); + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_VOUT_UV_WARN_LIMIT); + g_assert_cmphex(i2c_value, ==, 0xCDEF); + + adm1272_i2c_set16(i2cdev, PMBUS_IOUT_OC_WARN_LIMIT, 0x1234); + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_IOUT_OC_WARN_LIMIT); + g_assert_cmphex(i2c_value, ==, 0x1234); + + adm1272_i2c_set16(i2cdev, PMBUS_OT_FAULT_LIMIT, 0x5678); + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_OT_FAULT_LIMIT); + g_assert_cmphex(i2c_value, ==, 0x5678); + + adm1272_i2c_set16(i2cdev, PMBUS_OT_WARN_LIMIT, 0xABDC); + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_OT_WARN_LIMIT); + g_assert_cmphex(i2c_value, ==, 0xABDC); + + adm1272_i2c_set16(i2cdev, PMBUS_VIN_OV_WARN_LIMIT, 0xCDEF); + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_VIN_OV_WARN_LIMIT); + g_assert_cmphex(i2c_value, ==, 0xCDEF); + + adm1272_i2c_set16(i2cdev, PMBUS_VIN_UV_WARN_LIMIT, 0x2345); + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_VIN_UV_WARN_LIMIT); + g_assert_cmphex(i2c_value, ==, 0x2345); + + i2c_set8(i2cdev, ADM1272_RESTART_TIME, 0xF8); + i2c_value = i2c_get8(i2cdev, ADM1272_RESTART_TIME); + g_assert_cmphex(i2c_value, ==, 0xF8); + + i2c_set8(i2cdev, ADM1272_MFR_PMON_CONTROL, 0); + i2c_value = i2c_get8(i2cdev, ADM1272_MFR_PMON_CONTROL); + g_assert_cmpuint(i2c_value, ==, 0); + + adm1272_i2c_set16(i2cdev, ADM1272_MFR_PMON_CONFIG, 0xDEF0); + i2c_value = adm1272_i2c_get16(i2cdev, ADM1272_MFR_PMON_CONFIG); + g_assert_cmphex(i2c_value, ==, 0xDEF0); + + adm1272_i2c_set16(i2cdev, ADM1272_MFR_ALERT1_CONFIG, 0x0123); + i2c_value = adm1272_i2c_get16(i2cdev, ADM1272_MFR_ALERT1_CONFIG); + g_assert_cmphex(i2c_value, ==, 0x0123); + + adm1272_i2c_set16(i2cdev, ADM1272_MFR_ALERT2_CONFIG, 0x9876); + i2c_value = adm1272_i2c_get16(i2cdev, ADM1272_MFR_ALERT2_CONFIG); + g_assert_cmphex(i2c_value, ==, 0x9876); + + adm1272_i2c_set16(i2cdev, ADM1272_MFR_DEVICE_CONFIG, 0x3456); + i2c_value = adm1272_i2c_get16(i2cdev, ADM1272_MFR_DEVICE_CONFIG); + g_assert_cmphex(i2c_value, ==, 0x3456); + + adm1272_i2c_set16(i2cdev, ADM1272_HYSTERESIS_LOW, 0xCABA); + i2c_value = adm1272_i2c_get16(i2cdev, ADM1272_HYSTERESIS_LOW); + g_assert_cmphex(i2c_value, ==, 0xCABA); + + adm1272_i2c_set16(i2cdev, ADM1272_HYSTERESIS_HIGH, 0x6789); + i2c_value = adm1272_i2c_get16(i2cdev, ADM1272_HYSTERESIS_HIGH); + g_assert_cmphex(i2c_value, ==, 0x6789); + + adm1272_i2c_set16(i2cdev, ADM1272_STRT_UP_IOUT_LIM, 0x9876); + i2c_value = adm1272_i2c_get16(i2cdev, ADM1272_STRT_UP_IOUT_LIM); + g_assert_cmphex(i2c_value, ==, 0x9876); + + adm1272_i2c_set16(i2cdev, PMBUS_OPERATION, 0xA); + i2c_value = i2c_get8(i2cdev, PMBUS_OPERATION); + g_assert_cmphex(i2c_value, ==, 0xA); +} + +/* test read-only registers */ +static void test_ro_regs(void *obj, void *data, QGuestAllocator *alloc) +{ + uint16_t i2c_init_value, i2c_value; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + i2c_init_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_VIN); + adm1272_i2c_set16(i2cdev, PMBUS_READ_VIN, 0xBEEF); + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_VIN); + g_assert_cmphex(i2c_init_value, ==, i2c_value); + + i2c_init_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_VOUT); + adm1272_i2c_set16(i2cdev, PMBUS_READ_VOUT, 0x1234); + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_VOUT); + g_assert_cmphex(i2c_init_value, ==, i2c_value); + + i2c_init_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_IOUT); + adm1272_i2c_set16(i2cdev, PMBUS_READ_IOUT, 0x6547); + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_IOUT); + g_assert_cmphex(i2c_init_value, ==, i2c_value); + + i2c_init_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_TEMPERATURE_1); + adm1272_i2c_set16(i2cdev, PMBUS_READ_TEMPERATURE_1, 0x1597); + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_TEMPERATURE_1); + g_assert_cmphex(i2c_init_value, ==, i2c_value); + + i2c_init_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_PIN); + adm1272_i2c_set16(i2cdev, PMBUS_READ_PIN, 0xDEAD); + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_READ_PIN); + g_assert_cmphex(i2c_init_value, ==, i2c_value); +} + +/* test voltage fault handling */ +static void test_voltage_faults(void *obj, void *data, QGuestAllocator *alloc) +{ + uint16_t i2c_value; + uint8_t i2c_byte; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + adm1272_i2c_set16(i2cdev, PMBUS_VOUT_OV_WARN_LIMIT, + adm1272_millivolts_to_direct(5000)); + qmp_adm1272_set(TEST_ID, "vout", 5100); + + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_STATUS_WORD); + i2c_byte = i2c_get8(i2cdev, PMBUS_STATUS_VOUT); + g_assert_true((i2c_value & PB_STATUS_VOUT) != 0); + g_assert_true((i2c_byte & PB_STATUS_VOUT_OV_WARN) != 0); + + qmp_adm1272_set(TEST_ID, "vout", 4500); + i2c_set8(i2cdev, PMBUS_CLEAR_FAULTS, 0); + i2c_byte = i2c_get8(i2cdev, PMBUS_STATUS_VOUT); + g_assert_true((i2c_byte & PB_STATUS_VOUT_OV_WARN) == 0); + + adm1272_i2c_set16(i2cdev, PMBUS_VOUT_UV_WARN_LIMIT, + adm1272_millivolts_to_direct(4600)); + i2c_value = adm1272_i2c_get16(i2cdev, PMBUS_STATUS_WORD); + i2c_byte = i2c_get8(i2cdev, PMBUS_STATUS_VOUT); + g_assert_true((i2c_value & PB_STATUS_VOUT) != 0); + g_assert_true((i2c_byte & PB_STATUS_VOUT_UV_WARN) != 0); + +} + +static void adm1272_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "id=" TEST_ID ",address=0x10" + }; + add_qi2c_address(&opts, &(QI2CAddress) { TEST_ADDR }); + + qos_node_create_driver("adm1272", i2c_device_create); + qos_node_consumes("adm1272", "i2c-bus", &opts); + + qos_add_test("test_defaults", "adm1272", test_defaults, NULL); + qos_add_test("test_tx_rx", "adm1272", test_tx_rx, NULL); + qos_add_test("test_rw_regs", "adm1272", test_rw_regs, NULL); + qos_add_test("test_ro_regs", "adm1272", test_ro_regs, NULL); + qos_add_test("test_ov_faults", "adm1272", test_voltage_faults, NULL); +} +libqos_init(adm1272_register_nodes); diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index c3a223a83d..17c87a6730 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -201,6 +201,7 @@ qtests_s390x = \ qos_test_ss = ss.source_set() qos_test_ss.add( 'ac97-test.c', + 'adm1272-test.c', 'ds1338-test.c', 'e1000-test.c', 'e1000e-test.c', From 7215456a4fe6d7416fbad829ad25ec994d3a1cd0 Mon Sep 17 00:00:00 2001 From: Titus Rwantare Date: Thu, 8 Jul 2021 10:25:55 -0700 Subject: [PATCH 060/272] hw/misc: add MAX34451 device The MAX34451 is a Maxim power-supply system manager that can monitor up to 16 voltage rails or currents. It also contains a temperature sensor and supports up to four external temperature sensors. This commit adds support for interfacing with it, and setting limits on the supported sensors. Reviewed-by: Joel Stanley Reviewed-by: Hao Wu Signed-off-by: Titus Rwantare Message-Id: <20210708172556.1868139-5-titusr@google.com> [Moved the device to the sensor directory] Signed-off-by: Corey Minyard --- hw/arm/Kconfig | 1 + hw/sensor/Kconfig | 4 + hw/sensor/max34451.c | 775 ++++++++++++++++++++++++++++++++++++++++++ hw/sensor/meson.build | 1 + 4 files changed, 781 insertions(+) create mode 100644 hw/sensor/max34451.c diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index ae43f0a661..fea84eb2ef 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -375,6 +375,7 @@ config NPCM7XX select ADM1272 select ARM_GIC select AT24C # EEPROM + select MAX34451 select PL310 # cache controller select PMBUS select SERIAL diff --git a/hw/sensor/Kconfig b/hw/sensor/Kconfig index a34ffcb024..a2b55a4fdb 100644 --- a/hw/sensor/Kconfig +++ b/hw/sensor/Kconfig @@ -13,3 +13,7 @@ config EMC141X config ADM1272 bool depends on I2C + +config MAX34451 + bool + depends on I2C diff --git a/hw/sensor/max34451.c b/hw/sensor/max34451.c new file mode 100644 index 0000000000..a91d8bd487 --- /dev/null +++ b/hw/sensor/max34451.c @@ -0,0 +1,775 @@ +/* + * Maxim MAX34451 PMBus 16-Channel V/I monitor and 12-Channel Sequencer/Marginer + * + * Copyright 2021 Google LLC + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/i2c/pmbus_device.h" +#include "hw/irq.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "qemu/log.h" +#include "qemu/module.h" + +#define TYPE_MAX34451 "max34451" +#define MAX34451(obj) OBJECT_CHECK(MAX34451State, (obj), TYPE_MAX34451) + +#define MAX34451_MFR_MODE 0xD1 +#define MAX34451_MFR_PSEN_CONFIG 0xD2 +#define MAX34451_MFR_VOUT_PEAK 0xD4 +#define MAX34451_MFR_IOUT_PEAK 0xD5 +#define MAX34451_MFR_TEMPERATURE_PEAK 0xD6 +#define MAX34451_MFR_VOUT_MIN 0xD7 +#define MAX34451_MFR_NV_LOG_CONFIG 0xD8 +#define MAX34451_MFR_FAULT_RESPONSE 0xD9 +#define MAX34451_MFR_FAULT_RETRY 0xDA +#define MAX34451_MFR_NV_FAULT_LOG 0xDC +#define MAX34451_MFR_TIME_COUNT 0xDD +#define MAX34451_MFR_MARGIN_CONFIG 0xDF +#define MAX34451_MFR_FW_SERIAL 0xE0 +#define MAX34451_MFR_IOUT_AVG 0xE2 +#define MAX34451_MFR_CHANNEL_CONFIG 0xE4 +#define MAX34451_MFR_TON_SEQ_MAX 0xE6 +#define MAX34451_MFR_PWM_CONFIG 0xE7 +#define MAX34451_MFR_SEQ_CONFIG 0xE8 +#define MAX34451_MFR_STORE_ALL 0xEE +#define MAX34451_MFR_RESTORE_ALL 0xEF +#define MAX34451_MFR_TEMP_SENSOR_CONFIG 0xF0 +#define MAX34451_MFR_STORE_SINGLE 0xFC +#define MAX34451_MFR_CRC 0xFE + +#define MAX34451_NUM_MARGINED_PSU 12 +#define MAX34451_NUM_PWR_DEVICES 16 +#define MAX34451_NUM_TEMP_DEVICES 5 +#define MAX34451_NUM_PAGES 21 + +#define DEFAULT_OP_ON 0x80 +#define DEFAULT_CAPABILITY 0x20 +#define DEFAULT_ON_OFF_CONFIG 0x1a +#define DEFAULT_VOUT_MODE 0x40 +#define DEFAULT_TEMPERATURE 2500 +#define DEFAULT_SCALE 0x7FFF +#define DEFAULT_OV_LIMIT 0x7FFF +#define DEFAULT_OC_LIMIT 0x7FFF +#define DEFAULT_OT_LIMIT 0x7FFF +#define DEFAULT_VMIN 0x7FFF +#define DEFAULT_TON_FAULT_LIMIT 0xFFFF +#define DEFAULT_CHANNEL_CONFIG 0x20 +#define DEFAULT_TEXT 0x3130313031303130 + +/** + * MAX34451State: + * @code: The command code received + * @page: Each page corresponds to a device monitored by the Max 34451 + * The page register determines the available commands depending on device + ___________________________________________________________________________ + | 0 | Power supply monitored by RS0, controlled by PSEN0, and | + | | margined with PWM0. | + |_______|___________________________________________________________________| + | 1 | Power supply monitored by RS1, controlled by PSEN1, and | + | | margined with PWM1. | + |_______|___________________________________________________________________| + | 2 | Power supply monitored by RS2, controlled by PSEN2, and | + | | margined with PWM2. | + |_______|___________________________________________________________________| + | 3 | Power supply monitored by RS3, controlled by PSEN3, and | + | | margined with PWM3. | + |_______|___________________________________________________________________| + | 4 | Power supply monitored by RS4, controlled by PSEN4, and | + | | margined with PWM4. | + |_______|___________________________________________________________________| + | 5 | Power supply monitored by RS5, controlled by PSEN5, and | + | | margined with PWM5. | + |_______|___________________________________________________________________| + | 6 | Power supply monitored by RS6, controlled by PSEN6, and | + | | margined with PWM6. | + |_______|___________________________________________________________________| + | 7 | Power supply monitored by RS7, controlled by PSEN7, and | + | | margined with PWM7. | + |_______|___________________________________________________________________| + | 8 | Power supply monitored by RS8, controlled by PSEN8, and | + | | optionally margined by OUT0 of external DS4424 at I2C address A0h.| + |_______|___________________________________________________________________| + | 9 | Power supply monitored by RS9, controlled by PSEN9, and | + | | optionally margined by OUT1 of external DS4424 at I2C address A0h.| + |_______|___________________________________________________________________| + | 10 | Power supply monitored by RS10, controlled by PSEN10, and | + | | optionally margined by OUT2 of external DS4424 at I2C address A0h.| + |_______|___________________________________________________________________| + | 11 | Power supply monitored by RS11, controlled by PSEN11, and | + | | optionally margined by OUT3 of external DS4424 at I2C address A0h.| + |_______|___________________________________________________________________| + | 12 | ADC channel 12 (monitors voltage or current) or GPI. | + |_______|___________________________________________________________________| + | 13 | ADC channel 13 (monitors voltage or current) or GPI. | + |_______|___________________________________________________________________| + | 14 | ADC channel 14 (monitors voltage or current) or GPI. | + |_______|___________________________________________________________________| + | 15 | ADC channel 15 (monitors voltage or current) or GPI. | + |_______|___________________________________________________________________| + | 16 | Internal temperature sensor. | + |_______|___________________________________________________________________| + | 17 | External DS75LV temperature sensor with I2C address 90h. | + |_______|___________________________________________________________________| + | 18 | External DS75LV temperature sensor with I2C address 92h. | + |_______|___________________________________________________________________| + | 19 | External DS75LV temperature sensor with I2C address 94h. | + |_______|___________________________________________________________________| + | 20 | External DS75LV temperature sensor with I2C address 96h. | + |_______|___________________________________________________________________| + | 21=E2=80=93254| Reserved. | + |_______|___________________________________________________________________| + | 255 | Applies to all pages. | + |_______|___________________________________________________________________| + * + * @operation: Turn on and off power supplies + * @on_off_config: Configure the power supply on and off transition behaviour + * @write_protect: protect against changes to the device's memory + * @vout_margin_high: the voltage when OPERATION is set to margin high + * @vout_margin_low: the voltage when OPERATION is set to margin low + * @vout_scale: scale ADC reading to actual device reading if different + * @iout_cal_gain: set ratio of the voltage at the ADC input to sensed current + */ +typedef struct MAX34451State { + PMBusDevice parent; + + uint16_t power_good_on[MAX34451_NUM_PWR_DEVICES]; + uint16_t power_good_off[MAX34451_NUM_PWR_DEVICES]; + uint16_t ton_delay[MAX34451_NUM_MARGINED_PSU]; + uint16_t ton_max_fault_limit[MAX34451_NUM_MARGINED_PSU]; + uint16_t toff_delay[MAX34451_NUM_MARGINED_PSU]; + uint8_t status_mfr_specific[MAX34451_NUM_PWR_DEVICES]; + /* Manufacturer specific function */ + uint64_t mfr_location; + uint64_t mfr_date; + uint64_t mfr_serial; + uint16_t mfr_mode; + uint32_t psen_config[MAX34451_NUM_MARGINED_PSU]; + uint16_t vout_peak[MAX34451_NUM_PWR_DEVICES]; + uint16_t iout_peak[MAX34451_NUM_PWR_DEVICES]; + uint16_t temperature_peak[MAX34451_NUM_TEMP_DEVICES]; + uint16_t vout_min[MAX34451_NUM_PWR_DEVICES]; + uint16_t nv_log_config; + uint32_t fault_response[MAX34451_NUM_PWR_DEVICES]; + uint16_t fault_retry; + uint32_t fault_log; + uint32_t time_count; + uint16_t margin_config[MAX34451_NUM_MARGINED_PSU]; + uint16_t fw_serial; + uint16_t iout_avg[MAX34451_NUM_PWR_DEVICES]; + uint16_t channel_config[MAX34451_NUM_PWR_DEVICES]; + uint16_t ton_seq_max[MAX34451_NUM_MARGINED_PSU]; + uint32_t pwm_config[MAX34451_NUM_MARGINED_PSU]; + uint32_t seq_config[MAX34451_NUM_MARGINED_PSU]; + uint16_t temp_sensor_config[MAX34451_NUM_TEMP_DEVICES]; + uint16_t store_single; + uint16_t crc; +} MAX34451State; + + +static void max34451_check_limits(MAX34451State *s) +{ + PMBusDevice *pmdev = PMBUS_DEVICE(s); + + pmbus_check_limits(pmdev); + + for (int i = 0; i < MAX34451_NUM_PWR_DEVICES; i++) { + if (pmdev->pages[i].read_vout == 0) { /* PSU disabled */ + continue; + } + + if (pmdev->pages[i].read_vout > s->vout_peak[i]) { + s->vout_peak[i] = pmdev->pages[i].read_vout; + } + + if (pmdev->pages[i].read_vout < s->vout_min[i]) { + s->vout_min[i] = pmdev->pages[i].read_vout; + } + + if (pmdev->pages[i].read_iout > s->iout_peak[i]) { + s->iout_peak[i] = pmdev->pages[i].read_iout; + } + } + + for (int i = 0; i < MAX34451_NUM_TEMP_DEVICES; i++) { + if (pmdev->pages[i + 16].read_temperature_1 > s->temperature_peak[i]) { + s->temperature_peak[i] = pmdev->pages[i + 16].read_temperature_1; + } + } +} + +static uint8_t max34451_read_byte(PMBusDevice *pmdev) +{ + MAX34451State *s = MAX34451(pmdev); + switch (pmdev->code) { + + case PMBUS_POWER_GOOD_ON: + if (pmdev->page < 16) { + pmbus_send16(pmdev, s->power_good_on[pmdev->page]); + } + break; + + case PMBUS_POWER_GOOD_OFF: + if (pmdev->page < 16) { + pmbus_send16(pmdev, s->power_good_off[pmdev->page]); + } + break; + + case PMBUS_TON_DELAY: + if (pmdev->page < 12) { + pmbus_send16(pmdev, s->ton_delay[pmdev->page]); + } + break; + + case PMBUS_TON_MAX_FAULT_LIMIT: + if (pmdev->page < 12) { + pmbus_send16(pmdev, s->ton_max_fault_limit[pmdev->page]); + } + break; + + case PMBUS_TOFF_DELAY: + if (pmdev->page < 12) { + pmbus_send16(pmdev, s->toff_delay[pmdev->page]); + } + break; + + case PMBUS_STATUS_MFR_SPECIFIC: + if (pmdev->page < 16) { + pmbus_send8(pmdev, s->status_mfr_specific[pmdev->page]); + } + break; + + case PMBUS_MFR_ID: + pmbus_send8(pmdev, 0x4d); /* Maxim */ + break; + + case PMBUS_MFR_MODEL: + pmbus_send8(pmdev, 0x59); + break; + + case PMBUS_MFR_LOCATION: + pmbus_send64(pmdev, s->mfr_location); + break; + + case PMBUS_MFR_DATE: + pmbus_send64(pmdev, s->mfr_date); + break; + + case PMBUS_MFR_SERIAL: + pmbus_send64(pmdev, s->mfr_serial); + break; + + case MAX34451_MFR_MODE: + pmbus_send16(pmdev, s->mfr_mode); + break; + + case MAX34451_MFR_PSEN_CONFIG: + if (pmdev->page < 12) { + pmbus_send32(pmdev, s->psen_config[pmdev->page]); + } + break; + + case MAX34451_MFR_VOUT_PEAK: + if (pmdev->page < 16) { + pmbus_send16(pmdev, s->vout_peak[pmdev->page]); + } + break; + + case MAX34451_MFR_IOUT_PEAK: + if (pmdev->page < 16) { + pmbus_send16(pmdev, s->iout_peak[pmdev->page]); + } + break; + + case MAX34451_MFR_TEMPERATURE_PEAK: + if (15 < pmdev->page && pmdev->page < 21) { + pmbus_send16(pmdev, s->temperature_peak[pmdev->page % 16]); + } else { + pmbus_send16(pmdev, s->temperature_peak[0]); + } + break; + + case MAX34451_MFR_VOUT_MIN: + if (pmdev->page < 16) { + pmbus_send16(pmdev, s->vout_min[pmdev->page]); + } + break; + + case MAX34451_MFR_NV_LOG_CONFIG: + pmbus_send16(pmdev, s->nv_log_config); + break; + + case MAX34451_MFR_FAULT_RESPONSE: + if (pmdev->page < 16) { + pmbus_send32(pmdev, s->fault_response[pmdev->page]); + } + break; + + case MAX34451_MFR_FAULT_RETRY: + pmbus_send32(pmdev, s->fault_retry); + break; + + case MAX34451_MFR_NV_FAULT_LOG: + pmbus_send32(pmdev, s->fault_log); + break; + + case MAX34451_MFR_TIME_COUNT: + pmbus_send32(pmdev, s->time_count); + break; + + case MAX34451_MFR_MARGIN_CONFIG: + if (pmdev->page < 12) { + pmbus_send16(pmdev, s->margin_config[pmdev->page]); + } + break; + + case MAX34451_MFR_FW_SERIAL: + if (pmdev->page == 255) { + pmbus_send16(pmdev, 1); /* Firmware revision */ + } + break; + + case MAX34451_MFR_IOUT_AVG: + if (pmdev->page < 16) { + pmbus_send16(pmdev, s->iout_avg[pmdev->page]); + } + break; + + case MAX34451_MFR_CHANNEL_CONFIG: + if (pmdev->page < 16) { + pmbus_send16(pmdev, s->channel_config[pmdev->page]); + } + break; + + case MAX34451_MFR_TON_SEQ_MAX: + if (pmdev->page < 12) { + pmbus_send16(pmdev, s->ton_seq_max[pmdev->page]); + } + break; + + case MAX34451_MFR_PWM_CONFIG: + if (pmdev->page < 12) { + pmbus_send32(pmdev, s->pwm_config[pmdev->page]); + } + break; + + case MAX34451_MFR_SEQ_CONFIG: + if (pmdev->page < 12) { + pmbus_send32(pmdev, s->seq_config[pmdev->page]); + } + break; + + case MAX34451_MFR_TEMP_SENSOR_CONFIG: + if (15 < pmdev->page && pmdev->page < 21) { + pmbus_send32(pmdev, s->temp_sensor_config[pmdev->page % 16]); + } + break; + + case MAX34451_MFR_STORE_SINGLE: + pmbus_send32(pmdev, s->store_single); + break; + + case MAX34451_MFR_CRC: + pmbus_send32(pmdev, s->crc); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: reading from unsupported register: 0x%02x\n", + __func__, pmdev->code); + break; + } + return 0xFF; +} + +static int max34451_write_data(PMBusDevice *pmdev, const uint8_t *buf, + uint8_t len) +{ + MAX34451State *s = MAX34451(pmdev); + + if (len == 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__); + return -1; + } + + pmdev->code = buf[0]; /* PMBus command code */ + + if (len == 1) { + return 0; + } + + /* Exclude command code from buffer */ + buf++; + len--; + uint8_t index = pmdev->page; + + switch (pmdev->code) { + case MAX34451_MFR_STORE_ALL: + case MAX34451_MFR_RESTORE_ALL: + case MAX34451_MFR_STORE_SINGLE: + /* + * TODO: hardware behaviour is to move the contents of volatile + * memory to non-volatile memory. + */ + break; + + case PMBUS_POWER_GOOD_ON: /* R/W word */ + if (pmdev->page < MAX34451_NUM_PWR_DEVICES) { + s->power_good_on[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case PMBUS_POWER_GOOD_OFF: /* R/W word */ + if (pmdev->page < MAX34451_NUM_PWR_DEVICES) { + s->power_good_off[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case PMBUS_TON_DELAY: /* R/W word */ + if (pmdev->page < 12) { + s->ton_delay[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case PMBUS_TON_MAX_FAULT_LIMIT: /* R/W word */ + if (pmdev->page < 12) { + s->ton_max_fault_limit[pmdev->page] + = pmbus_receive16(pmdev); + } + break; + + case PMBUS_TOFF_DELAY: /* R/W word */ + if (pmdev->page < 12) { + s->toff_delay[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case PMBUS_MFR_LOCATION: /* R/W 64 */ + s->mfr_location = pmbus_receive64(pmdev); + break; + + case PMBUS_MFR_DATE: /* R/W 64 */ + s->mfr_date = pmbus_receive64(pmdev); + break; + + case PMBUS_MFR_SERIAL: /* R/W 64 */ + s->mfr_serial = pmbus_receive64(pmdev); + break; + + case MAX34451_MFR_MODE: /* R/W word */ + s->mfr_mode = pmbus_receive16(pmdev); + break; + + case MAX34451_MFR_PSEN_CONFIG: /* R/W 32 */ + if (pmdev->page < 12) { + s->psen_config[pmdev->page] = pmbus_receive32(pmdev); + } + break; + + case MAX34451_MFR_VOUT_PEAK: /* R/W word */ + if (pmdev->page < 16) { + s->vout_peak[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case MAX34451_MFR_IOUT_PEAK: /* R/W word */ + if (pmdev->page < 16) { + s->iout_peak[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case MAX34451_MFR_TEMPERATURE_PEAK: /* R/W word */ + if (15 < pmdev->page && pmdev->page < 21) { + s->temperature_peak[pmdev->page % 16] + = pmbus_receive16(pmdev); + } + break; + + case MAX34451_MFR_VOUT_MIN: /* R/W word */ + if (pmdev->page < 16) { + s->vout_min[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case MAX34451_MFR_NV_LOG_CONFIG: /* R/W word */ + s->nv_log_config = pmbus_receive16(pmdev); + break; + + case MAX34451_MFR_FAULT_RESPONSE: /* R/W 32 */ + if (pmdev->page < 16) { + s->fault_response[pmdev->page] = pmbus_receive32(pmdev); + } + break; + + case MAX34451_MFR_FAULT_RETRY: /* R/W word */ + s->fault_retry = pmbus_receive16(pmdev); + break; + + case MAX34451_MFR_TIME_COUNT: /* R/W 32 */ + s->time_count = pmbus_receive32(pmdev); + break; + + case MAX34451_MFR_MARGIN_CONFIG: /* R/W word */ + if (pmdev->page < 12) { + s->margin_config[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case MAX34451_MFR_CHANNEL_CONFIG: /* R/W word */ + if (pmdev->page < 16) { + s->channel_config[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case MAX34451_MFR_TON_SEQ_MAX: /* R/W word */ + if (pmdev->page < 12) { + s->ton_seq_max[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case MAX34451_MFR_PWM_CONFIG: /* R/W 32 */ + if (pmdev->page < 12) { + s->pwm_config[pmdev->page] = pmbus_receive32(pmdev); + } + break; + + case MAX34451_MFR_SEQ_CONFIG: /* R/W 32 */ + if (pmdev->page < 12) { + s->seq_config[pmdev->page] = pmbus_receive32(pmdev); + } + break; + + case MAX34451_MFR_TEMP_SENSOR_CONFIG: /* R/W word */ + if (15 < pmdev->page && pmdev->page < 21) { + s->temp_sensor_config[pmdev->page % 16] + = pmbus_receive16(pmdev); + } + break; + + case MAX34451_MFR_CRC: /* R/W word */ + s->crc = pmbus_receive16(pmdev); + break; + + case MAX34451_MFR_NV_FAULT_LOG: + case MAX34451_MFR_FW_SERIAL: + case MAX34451_MFR_IOUT_AVG: + /* Read only commands */ + pmdev->pages[index].status_word |= PMBUS_STATUS_CML; + pmdev->pages[index].status_cml |= PB_CML_FAULT_INVALID_DATA; + qemu_log_mask(LOG_GUEST_ERROR, + "%s: writing to read-only register 0x%02x\n", + __func__, pmdev->code); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: writing to unsupported register: 0x%02x\n", + __func__, pmdev->code); + break; + } + + return 0; +} + +static void max34451_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + visit_type_uint16(v, name, (uint16_t *)opaque, errp); +} + +static void max34451_set(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + MAX34451State *s = MAX34451(obj); + uint16_t *internal = opaque; + uint16_t value; + if (!visit_type_uint16(v, name, &value, errp)) { + return; + } + + *internal = value; + max34451_check_limits(s); +} + +/* used to init uint16_t arrays */ +static inline void *memset_word(void *s, uint16_t c, size_t n) +{ + size_t i; + uint16_t *p = s; + + for (i = 0; i < n; i++) { + p[i] = c; + } + + return s; +} + +static void max34451_exit_reset(Object *obj) +{ + PMBusDevice *pmdev = PMBUS_DEVICE(obj); + MAX34451State *s = MAX34451(obj); + pmdev->capability = DEFAULT_CAPABILITY; + + for (int i = 0; i < MAX34451_NUM_PAGES; i++) { + pmdev->pages[i].operation = DEFAULT_OP_ON; + pmdev->pages[i].on_off_config = DEFAULT_ON_OFF_CONFIG; + pmdev->pages[i].revision = 0x11; + pmdev->pages[i].vout_mode = DEFAULT_VOUT_MODE; + } + + for (int i = 0; i < MAX34451_NUM_PWR_DEVICES; i++) { + pmdev->pages[i].vout_scale_monitor = DEFAULT_SCALE; + pmdev->pages[i].vout_ov_fault_limit = DEFAULT_OV_LIMIT; + pmdev->pages[i].vout_ov_warn_limit = DEFAULT_OV_LIMIT; + pmdev->pages[i].iout_oc_warn_limit = DEFAULT_OC_LIMIT; + pmdev->pages[i].iout_oc_fault_limit = DEFAULT_OC_LIMIT; + } + + for (int i = 0; i < MAX34451_NUM_MARGINED_PSU; i++) { + pmdev->pages[i].ton_max_fault_limit = DEFAULT_TON_FAULT_LIMIT; + } + + for (int i = 16; i < MAX34451_NUM_TEMP_DEVICES + 16; i++) { + pmdev->pages[i].read_temperature_1 = DEFAULT_TEMPERATURE; + pmdev->pages[i].ot_warn_limit = DEFAULT_OT_LIMIT; + pmdev->pages[i].ot_fault_limit = DEFAULT_OT_LIMIT; + } + + memset_word(s->ton_max_fault_limit, DEFAULT_TON_FAULT_LIMIT, + MAX34451_NUM_MARGINED_PSU); + memset_word(s->channel_config, DEFAULT_CHANNEL_CONFIG, + MAX34451_NUM_PWR_DEVICES); + memset_word(s->vout_min, DEFAULT_VMIN, MAX34451_NUM_PWR_DEVICES); + + s->mfr_location = DEFAULT_TEXT; + s->mfr_date = DEFAULT_TEXT; + s->mfr_serial = DEFAULT_TEXT; +} + +static const VMStateDescription vmstate_max34451 = { + .name = TYPE_MAX34451, + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]){ + VMSTATE_PMBUS_DEVICE(parent, MAX34451State), + VMSTATE_UINT16_ARRAY(power_good_on, MAX34451State, + MAX34451_NUM_PWR_DEVICES), + VMSTATE_UINT16_ARRAY(power_good_off, MAX34451State, + MAX34451_NUM_PWR_DEVICES), + VMSTATE_UINT16_ARRAY(ton_delay, MAX34451State, + MAX34451_NUM_MARGINED_PSU), + VMSTATE_UINT16_ARRAY(ton_max_fault_limit, MAX34451State, + MAX34451_NUM_MARGINED_PSU), + VMSTATE_UINT16_ARRAY(toff_delay, MAX34451State, + MAX34451_NUM_MARGINED_PSU), + VMSTATE_UINT8_ARRAY(status_mfr_specific, MAX34451State, + MAX34451_NUM_PWR_DEVICES), + VMSTATE_UINT64(mfr_location, MAX34451State), + VMSTATE_UINT64(mfr_date, MAX34451State), + VMSTATE_UINT64(mfr_serial, MAX34451State), + VMSTATE_UINT16(mfr_mode, MAX34451State), + VMSTATE_UINT32_ARRAY(psen_config, MAX34451State, + MAX34451_NUM_MARGINED_PSU), + VMSTATE_UINT16_ARRAY(vout_peak, MAX34451State, + MAX34451_NUM_PWR_DEVICES), + VMSTATE_UINT16_ARRAY(iout_peak, MAX34451State, + MAX34451_NUM_PWR_DEVICES), + VMSTATE_UINT16_ARRAY(temperature_peak, MAX34451State, + MAX34451_NUM_TEMP_DEVICES), + VMSTATE_UINT16_ARRAY(vout_min, MAX34451State, MAX34451_NUM_PWR_DEVICES), + VMSTATE_UINT16(nv_log_config, MAX34451State), + VMSTATE_UINT32_ARRAY(fault_response, MAX34451State, + MAX34451_NUM_PWR_DEVICES), + VMSTATE_UINT16(fault_retry, MAX34451State), + VMSTATE_UINT32(fault_log, MAX34451State), + VMSTATE_UINT32(time_count, MAX34451State), + VMSTATE_UINT16_ARRAY(margin_config, MAX34451State, + MAX34451_NUM_MARGINED_PSU), + VMSTATE_UINT16(fw_serial, MAX34451State), + VMSTATE_UINT16_ARRAY(iout_avg, MAX34451State, MAX34451_NUM_PWR_DEVICES), + VMSTATE_UINT16_ARRAY(channel_config, MAX34451State, + MAX34451_NUM_PWR_DEVICES), + VMSTATE_UINT16_ARRAY(ton_seq_max, MAX34451State, + MAX34451_NUM_MARGINED_PSU), + VMSTATE_UINT32_ARRAY(pwm_config, MAX34451State, + MAX34451_NUM_MARGINED_PSU), + VMSTATE_UINT32_ARRAY(seq_config, MAX34451State, + MAX34451_NUM_MARGINED_PSU), + VMSTATE_UINT16_ARRAY(temp_sensor_config, MAX34451State, + MAX34451_NUM_TEMP_DEVICES), + VMSTATE_UINT16(store_single, MAX34451State), + VMSTATE_UINT16(crc, MAX34451State), + VMSTATE_END_OF_LIST() + } +}; + +static void max34451_init(Object *obj) +{ + PMBusDevice *pmdev = PMBUS_DEVICE(obj); + uint64_t psu_flags = PB_HAS_VOUT | PB_HAS_IOUT | PB_HAS_VOUT_MODE | + PB_HAS_IOUT_GAIN; + + for (int i = 0; i < MAX34451_NUM_PWR_DEVICES; i++) { + pmbus_page_config(pmdev, i, psu_flags); + } + + for (int i = 0; i < MAX34451_NUM_MARGINED_PSU; i++) { + pmbus_page_config(pmdev, i, psu_flags | PB_HAS_VOUT_MARGIN); + } + + for (int i = 16; i < MAX34451_NUM_TEMP_DEVICES + 16; i++) { + pmbus_page_config(pmdev, i, PB_HAS_TEMPERATURE | PB_HAS_VOUT_MODE); + } + + /* get and set the voltage in millivolts, max is 32767 mV */ + for (int i = 0; i < MAX34451_NUM_PWR_DEVICES; i++) { + object_property_add(obj, "vout[*]", "uint16", + max34451_get, + max34451_set, NULL, &pmdev->pages[i].read_vout); + } + + /* + * get and set the temperature of the internal temperature sensor in + * centidegrees Celcius i.e.: 2500 -> 25.00 C, max is 327.67 C + */ + for (int i = 0; i < MAX34451_NUM_TEMP_DEVICES; i++) { + object_property_add(obj, "temperature[*]", "uint16", + max34451_get, + max34451_set, + NULL, + &pmdev->pages[i + 16].read_temperature_1); + } + +} + +static void max34451_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + PMBusDeviceClass *k = PMBUS_DEVICE_CLASS(klass); + dc->desc = "Maxim MAX34451 16-Channel V/I monitor"; + dc->vmsd = &vmstate_max34451; + k->write_data = max34451_write_data; + k->receive_byte = max34451_read_byte; + k->device_num_pages = MAX34451_NUM_PAGES; + rc->phases.exit = max34451_exit_reset; +} + +static const TypeInfo max34451_info = { + .name = TYPE_MAX34451, + .parent = TYPE_PMBUS_DEVICE, + .instance_size = sizeof(MAX34451State), + .instance_init = max34451_init, + .class_init = max34451_class_init, +}; + +static void max34451_register_types(void) +{ + type_register_static(&max34451_info); +} + +type_init(max34451_register_types) diff --git a/hw/sensor/meson.build b/hw/sensor/meson.build index 8b319b5826..034e3e0207 100644 --- a/hw/sensor/meson.build +++ b/hw/sensor/meson.build @@ -2,3 +2,4 @@ softmmu_ss.add(when: 'CONFIG_TMP105', if_true: files('tmp105.c')) softmmu_ss.add(when: 'CONFIG_TMP421', if_true: files('tmp421.c')) softmmu_ss.add(when: 'CONFIG_EMC141X', if_true: files('emc141x.c')) softmmu_ss.add(when: 'CONFIG_ADM1272', if_true: files('adm1272.c')) +softmmu_ss.add(when: 'CONFIG_MAX34451', if_true: files('max34451.c')) From 7649086f455fe44bd076828749a93ab2a5bb0806 Mon Sep 17 00:00:00 2001 From: Titus Rwantare Date: Thu, 8 Jul 2021 10:25:56 -0700 Subject: [PATCH 061/272] tests/qtest: add tests for MAX34451 device model Signed-off-by: Titus Rwantare Reviewed-by: Joel Stanley Message-Id: <20210708172556.1868139-6-titusr@google.com> Signed-off-by: Corey Minyard --- tests/qtest/max34451-test.c | 336 ++++++++++++++++++++++++++++++++++++ tests/qtest/meson.build | 1 + 2 files changed, 337 insertions(+) create mode 100644 tests/qtest/max34451-test.c diff --git a/tests/qtest/max34451-test.c b/tests/qtest/max34451-test.c new file mode 100644 index 0000000000..0c98d0764c --- /dev/null +++ b/tests/qtest/max34451-test.c @@ -0,0 +1,336 @@ +/* + * QTests for the MAX34451 device + * + * Copyright 2021 Google LLC + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/i2c/pmbus_device.h" +#include "libqtest-single.h" +#include "libqos/qgraph.h" +#include "libqos/i2c.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qnum.h" +#include "qemu/bitops.h" + +#define TEST_ID "max34451-test" +#define TEST_ADDR (0x4e) + +#define MAX34451_MFR_VOUT_PEAK 0xD4 +#define MAX34451_MFR_IOUT_PEAK 0xD5 +#define MAX34451_MFR_TEMPERATURE_PEAK 0xD6 +#define MAX34451_MFR_VOUT_MIN 0xD7 + +#define DEFAULT_VOUT 0 +#define DEFAULT_UV_LIMIT 0 +#define DEFAULT_TEMPERATURE 2500 +#define DEFAULT_SCALE 0x7FFF +#define DEFAULT_OV_LIMIT 0x7FFF +#define DEFAULT_OC_LIMIT 0x7FFF +#define DEFAULT_OT_LIMIT 0x7FFF +#define DEFAULT_VMIN 0x7FFF +#define DEFAULT_TON_FAULT_LIMIT 0xFFFF +#define DEFAULT_CHANNEL_CONFIG 0x20 +#define DEFAULT_TEXT 0x20 + +#define MAX34451_NUM_PWR_DEVICES 16 +#define MAX34451_NUM_TEMP_DEVICES 5 + + +static uint16_t qmp_max34451_get(const char *id, const char *property) +{ + QDict *response; + uint16_t ret; + response = qmp("{ 'execute': 'qom-get', 'arguments': { 'path': %s, " + "'property': %s } }", id, property); + g_assert(qdict_haskey(response, "return")); + ret = qnum_get_uint(qobject_to(QNum, qdict_get(response, "return"))); + qobject_unref(response); + return ret; +} + +static void qmp_max34451_set(const char *id, + const char *property, + uint16_t value) +{ + QDict *response; + + response = qmp("{ 'execute': 'qom-set', 'arguments': { 'path': %s, " + "'property': %s, 'value': %u } }", + id, property, value); + g_assert(qdict_haskey(response, "return")); + qobject_unref(response); +} + +/* PMBus commands are little endian vs i2c_set16 in i2c.h which is big endian */ +static uint16_t max34451_i2c_get16(QI2CDevice *i2cdev, uint8_t reg) +{ + uint8_t resp[2]; + i2c_read_block(i2cdev, reg, resp, sizeof(resp)); + return (resp[1] << 8) | resp[0]; +} + +/* PMBus commands are little endian vs i2c_set16 in i2c.h which is big endian */ +static void max34451_i2c_set16(QI2CDevice *i2cdev, uint8_t reg, uint16_t value) +{ + uint8_t data[2]; + + data[0] = value & 255; + data[1] = value >> 8; + i2c_write_block(i2cdev, reg, data, sizeof(data)); +} + +/* Test default values */ +static void test_defaults(void *obj, void *data, QGuestAllocator *alloc) +{ + uint16_t value, i2c_value; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + char *path; + + /* Default temperatures and temperature fault limits */ + for (int i = 0; i < MAX34451_NUM_TEMP_DEVICES; i++) { + path = g_strdup_printf("temperature[%d]", i); + value = qmp_max34451_get(TEST_ID, path); + g_assert_cmpuint(value, ==, DEFAULT_TEMPERATURE); + g_free(path); + + /* Temperature sensors start on page 16 */ + i2c_set8(i2cdev, PMBUS_PAGE, i + 16); + i2c_value = max34451_i2c_get16(i2cdev, PMBUS_READ_TEMPERATURE_1); + g_assert_cmpuint(i2c_value, ==, DEFAULT_TEMPERATURE); + + i2c_value = max34451_i2c_get16(i2cdev, PMBUS_OT_FAULT_LIMIT); + g_assert_cmpuint(i2c_value, ==, DEFAULT_OT_LIMIT); + + i2c_value = max34451_i2c_get16(i2cdev, PMBUS_OT_WARN_LIMIT); + g_assert_cmpuint(i2c_value, ==, DEFAULT_OT_LIMIT); + } + + /* Default voltages and fault limits */ + for (int i = 0; i < MAX34451_NUM_PWR_DEVICES; i++) { + path = g_strdup_printf("vout[%d]", i); + value = qmp_max34451_get(TEST_ID, path); + g_assert_cmpuint(value, ==, DEFAULT_VOUT); + g_free(path); + + i2c_set8(i2cdev, PMBUS_PAGE, i); + i2c_value = max34451_i2c_get16(i2cdev, PMBUS_READ_VOUT); + g_assert_cmpuint(i2c_value, ==, DEFAULT_VOUT); + + i2c_value = max34451_i2c_get16(i2cdev, PMBUS_VOUT_OV_FAULT_LIMIT); + g_assert_cmpuint(i2c_value, ==, DEFAULT_OV_LIMIT); + + i2c_value = max34451_i2c_get16(i2cdev, PMBUS_VOUT_OV_WARN_LIMIT); + g_assert_cmpuint(i2c_value, ==, DEFAULT_OV_LIMIT); + + i2c_value = max34451_i2c_get16(i2cdev, PMBUS_VOUT_UV_WARN_LIMIT); + g_assert_cmpuint(i2c_value, ==, DEFAULT_UV_LIMIT); + + i2c_value = max34451_i2c_get16(i2cdev, PMBUS_VOUT_UV_FAULT_LIMIT); + g_assert_cmpuint(i2c_value, ==, DEFAULT_UV_LIMIT); + + i2c_value = max34451_i2c_get16(i2cdev, MAX34451_MFR_VOUT_MIN); + g_assert_cmpuint(i2c_value, ==, DEFAULT_VMIN); + } + + i2c_value = i2c_get8(i2cdev, PMBUS_VOUT_MODE); + g_assert_cmphex(i2c_value, ==, 0x40); /* DIRECT mode */ + + i2c_value = i2c_get8(i2cdev, PMBUS_REVISION); + g_assert_cmphex(i2c_value, ==, 0x11); /* Rev 1.1 */ +} + +/* Test setting temperature */ +static void test_temperature(void *obj, void *data, QGuestAllocator *alloc) +{ + uint16_t value, i2c_value; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + char *path; + + for (int i = 0; i < MAX34451_NUM_TEMP_DEVICES; i++) { + path = g_strdup_printf("temperature[%d]", i); + qmp_max34451_set(TEST_ID, path, 0xBE00 + i); + value = qmp_max34451_get(TEST_ID, path); + g_assert_cmphex(value, ==, 0xBE00 + i); + g_free(path); + } + + /* compare qmp read with i2c read separately */ + for (int i = 0; i < MAX34451_NUM_TEMP_DEVICES; i++) { + /* temperature[0] is on page 16 */ + i2c_set8(i2cdev, PMBUS_PAGE, i + 16); + i2c_value = max34451_i2c_get16(i2cdev, PMBUS_READ_TEMPERATURE_1); + g_assert_cmphex(i2c_value, ==, 0xBE00 + i); + + i2c_value = max34451_i2c_get16(i2cdev, MAX34451_MFR_TEMPERATURE_PEAK); + g_assert_cmphex(i2c_value, ==, 0xBE00 + i); + } +} + +/* Test setting voltage */ +static void test_voltage(void *obj, void *data, QGuestAllocator *alloc) +{ + uint16_t value, i2c_value; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + char *path; + + for (int i = 0; i < MAX34451_NUM_PWR_DEVICES; i++) { + path = g_strdup_printf("vout[%d]", i); + qmp_max34451_set(TEST_ID, path, 3000 + i); + value = qmp_max34451_get(TEST_ID, path); + g_assert_cmpuint(value, ==, 3000 + i); + g_free(path); + } + + /* compare qmp read with i2c read separately */ + for (int i = 0; i < MAX34451_NUM_PWR_DEVICES; i++) { + i2c_set8(i2cdev, PMBUS_PAGE, i); + i2c_value = max34451_i2c_get16(i2cdev, PMBUS_READ_VOUT); + g_assert_cmpuint(i2c_value, ==, 3000 + i); + + i2c_value = max34451_i2c_get16(i2cdev, MAX34451_MFR_VOUT_PEAK); + g_assert_cmpuint(i2c_value, ==, 3000 + i); + + i2c_value = max34451_i2c_get16(i2cdev, MAX34451_MFR_VOUT_MIN); + g_assert_cmpuint(i2c_value, ==, 3000 + i); + } +} + +/* Test setting some read/write registers */ +static void test_rw_regs(void *obj, void *data, QGuestAllocator *alloc) +{ + uint16_t i2c_value; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + i2c_set8(i2cdev, PMBUS_PAGE, 11); + i2c_value = i2c_get8(i2cdev, PMBUS_PAGE); + g_assert_cmpuint(i2c_value, ==, 11); + + i2c_set8(i2cdev, PMBUS_OPERATION, 1); + i2c_value = i2c_get8(i2cdev, PMBUS_OPERATION); + g_assert_cmpuint(i2c_value, ==, 1); + + max34451_i2c_set16(i2cdev, PMBUS_VOUT_MARGIN_HIGH, 5000); + i2c_value = max34451_i2c_get16(i2cdev, PMBUS_VOUT_MARGIN_HIGH); + g_assert_cmpuint(i2c_value, ==, 5000); + + max34451_i2c_set16(i2cdev, PMBUS_VOUT_MARGIN_LOW, 4000); + i2c_value = max34451_i2c_get16(i2cdev, PMBUS_VOUT_MARGIN_LOW); + g_assert_cmpuint(i2c_value, ==, 4000); + + max34451_i2c_set16(i2cdev, PMBUS_VOUT_OV_FAULT_LIMIT, 5500); + i2c_value = max34451_i2c_get16(i2cdev, PMBUS_VOUT_OV_FAULT_LIMIT); + g_assert_cmpuint(i2c_value, ==, 5500); + + max34451_i2c_set16(i2cdev, PMBUS_VOUT_OV_WARN_LIMIT, 5600); + i2c_value = max34451_i2c_get16(i2cdev, PMBUS_VOUT_OV_WARN_LIMIT); + g_assert_cmpuint(i2c_value, ==, 5600); + + max34451_i2c_set16(i2cdev, PMBUS_VOUT_UV_FAULT_LIMIT, 5700); + i2c_value = max34451_i2c_get16(i2cdev, PMBUS_VOUT_UV_FAULT_LIMIT); + g_assert_cmpuint(i2c_value, ==, 5700); + + max34451_i2c_set16(i2cdev, PMBUS_VOUT_UV_WARN_LIMIT, 5800); + i2c_value = max34451_i2c_get16(i2cdev, PMBUS_VOUT_UV_WARN_LIMIT); + g_assert_cmpuint(i2c_value, ==, 5800); + + max34451_i2c_set16(i2cdev, PMBUS_POWER_GOOD_ON, 5900); + i2c_value = max34451_i2c_get16(i2cdev, PMBUS_POWER_GOOD_ON); + g_assert_cmpuint(i2c_value, ==, 5900); + + max34451_i2c_set16(i2cdev, PMBUS_POWER_GOOD_OFF, 6100); + i2c_value = max34451_i2c_get16(i2cdev, PMBUS_POWER_GOOD_OFF); + g_assert_cmpuint(i2c_value, ==, 6100); +} + +/* Test that Read only registers can't be written */ +static void test_ro_regs(void *obj, void *data, QGuestAllocator *alloc) +{ + uint16_t i2c_value, i2c_init_value; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + i2c_set8(i2cdev, PMBUS_PAGE, 1); /* move to page 1 */ + i2c_init_value = i2c_get8(i2cdev, PMBUS_CAPABILITY); + i2c_set8(i2cdev, PMBUS_CAPABILITY, 0xF9); + i2c_value = i2c_get8(i2cdev, PMBUS_CAPABILITY); + g_assert_cmpuint(i2c_init_value, ==, i2c_value); + + i2c_init_value = max34451_i2c_get16(i2cdev, PMBUS_READ_VOUT); + max34451_i2c_set16(i2cdev, PMBUS_READ_VOUT, 0xDEAD); + i2c_value = max34451_i2c_get16(i2cdev, PMBUS_READ_VOUT); + g_assert_cmpuint(i2c_init_value, ==, i2c_value); + g_assert_cmphex(i2c_value, !=, 0xDEAD); + + i2c_set8(i2cdev, PMBUS_PAGE, 16); /* move to page 16 */ + i2c_init_value = max34451_i2c_get16(i2cdev, PMBUS_READ_TEMPERATURE_1); + max34451_i2c_set16(i2cdev, PMBUS_READ_TEMPERATURE_1, 0xABBA); + i2c_value = max34451_i2c_get16(i2cdev, PMBUS_READ_TEMPERATURE_1); + g_assert_cmpuint(i2c_init_value, ==, i2c_value); + g_assert_cmphex(i2c_value, !=, 0xABBA); +} + +/* test over voltage faults */ +static void test_ov_faults(void *obj, void *data, QGuestAllocator *alloc) +{ + uint16_t i2c_value; + uint8_t i2c_byte; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + char *path; + /* Test ov fault reporting */ + for (int i = 0; i < MAX34451_NUM_PWR_DEVICES; i++) { + path = g_strdup_printf("vout[%d]", i); + i2c_set8(i2cdev, PMBUS_PAGE, i); + max34451_i2c_set16(i2cdev, PMBUS_VOUT_OV_FAULT_LIMIT, 5000); + qmp_max34451_set(TEST_ID, path, 5100); + g_free(path); + + i2c_value = max34451_i2c_get16(i2cdev, PMBUS_STATUS_WORD); + i2c_byte = i2c_get8(i2cdev, PMBUS_STATUS_VOUT); + g_assert_true((i2c_value & PB_STATUS_VOUT) != 0); + g_assert_true((i2c_byte & PB_STATUS_VOUT_OV_FAULT) != 0); + } +} + +/* test over temperature faults */ +static void test_ot_faults(void *obj, void *data, QGuestAllocator *alloc) +{ + uint16_t i2c_value; + uint8_t i2c_byte; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + char *path; + + for (int i = 0; i < MAX34451_NUM_TEMP_DEVICES; i++) { + path = g_strdup_printf("temperature[%d]", i); + i2c_set8(i2cdev, PMBUS_PAGE, i + 16); + max34451_i2c_set16(i2cdev, PMBUS_OT_FAULT_LIMIT, 6000); + qmp_max34451_set(TEST_ID, path, 6100); + g_free(path); + + i2c_value = max34451_i2c_get16(i2cdev, PMBUS_STATUS_WORD); + i2c_byte = i2c_get8(i2cdev, PMBUS_STATUS_TEMPERATURE); + g_assert_true((i2c_value & PB_STATUS_TEMPERATURE) != 0); + g_assert_true((i2c_byte & PB_STATUS_OT_FAULT) != 0); + } +} + +static void max34451_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "id=" TEST_ID ",address=0x4e" + }; + add_qi2c_address(&opts, &(QI2CAddress) { TEST_ADDR }); + + qos_node_create_driver("max34451", i2c_device_create); + qos_node_consumes("max34451", "i2c-bus", &opts); + + qos_add_test("test_defaults", "max34451", test_defaults, NULL); + qos_add_test("test_temperature", "max34451", test_temperature, NULL); + qos_add_test("test_voltage", "max34451", test_voltage, NULL); + qos_add_test("test_rw_regs", "max34451", test_rw_regs, NULL); + qos_add_test("test_ro_regs", "max34451", test_ro_regs, NULL); + qos_add_test("test_ov_faults", "max34451", test_ov_faults, NULL); + qos_add_test("test_ot_faults", "max34451", test_ot_faults, NULL); +} +libqos_init(max34451_register_nodes); diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 17c87a6730..05eede560d 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -208,6 +208,7 @@ qos_test_ss.add( 'eepro100-test.c', 'es1370-test.c', 'ipoctal232-test.c', + 'max34451-test.c', 'megasas-test.c', 'ne2000-test.c', 'tulip-test.c', From 8947d7fc4e77d36fae44411b1b63c513863f89a7 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 13 Apr 2021 11:55:19 +0200 Subject: [PATCH 062/272] memory: Introduce RamDiscardManager for RAM memory regions We have some special RAM memory regions (managed by virtio-mem), whereby the guest agreed to only use selected memory ranges. "unused" parts are discarded so they won't consume memory - to logically unplug these memory ranges. Before the VM is allowed to use such logically unplugged memory again, coordination with the hypervisor is required. This results in "sparse" mmaps/RAMBlocks/memory regions, whereby only coordinated parts are valid to be used/accessed by the VM. In most cases, we don't care about that - e.g., in KVM, we simply have a single KVM memory slot. However, in case of vfio, registering the whole region with the kernel results in all pages getting pinned, and therefore an unexpected high memory consumption - discarding of RAM in that context is broken. Let's introduce a way to coordinate discarding/populating memory within a RAM memory region with such special consumers of RAM memory regions: they can register as listeners and get updates on memory getting discarded and populated. Using this machinery, vfio will be able to map only the currently populated parts, resulting in discarded parts not getting pinned and not consuming memory. A RamDiscardManager has to be set for a memory region before it is getting mapped, and cannot change while the memory region is mapped. Note: At some point, we might want to let RAMBlock users (esp. vfio used for nvme://) consume this interface as well. We'll need RAMBlock notifier calls when a RAMBlock is getting mapped/unmapped (via the corresponding memory region), so we can properly register a listener there as well. Reviewed-by: Pankaj Gupta Acked-by: Michael S. Tsirkin Cc: Paolo Bonzini Cc: "Michael S. Tsirkin" Cc: Alex Williamson Cc: Dr. David Alan Gilbert Cc: Igor Mammedov Cc: Pankaj Gupta Cc: Peter Xu Cc: Auger Eric Cc: Wei Yang Cc: teawater Cc: Marek Kedzierski Signed-off-by: David Hildenbrand Message-Id: <20210413095531.25603-2-david@redhat.com> Signed-off-by: Eduardo Habkost --- include/exec/memory.h | 286 ++++++++++++++++++++++++++++++++++++++---- softmmu/memory.c | 71 +++++++++++ 2 files changed, 335 insertions(+), 22 deletions(-) diff --git a/include/exec/memory.h b/include/exec/memory.h index b116f7c64e..6574e46b93 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -42,6 +42,12 @@ typedef struct IOMMUMemoryRegionClass IOMMUMemoryRegionClass; DECLARE_OBJ_CHECKERS(IOMMUMemoryRegion, IOMMUMemoryRegionClass, IOMMU_MEMORY_REGION, TYPE_IOMMU_MEMORY_REGION) +#define TYPE_RAM_DISCARD_MANAGER "qemu:ram-discard-manager" +typedef struct RamDiscardManagerClass RamDiscardManagerClass; +typedef struct RamDiscardManager RamDiscardManager; +DECLARE_OBJ_CHECKERS(RamDiscardManager, RamDiscardManagerClass, + RAM_DISCARD_MANAGER, TYPE_RAM_DISCARD_MANAGER); + #ifdef CONFIG_FUZZ void fuzz_dma_read_cb(size_t addr, size_t len, @@ -65,6 +71,28 @@ struct ReservedRegion { unsigned type; }; +/** + * struct MemoryRegionSection: describes a fragment of a #MemoryRegion + * + * @mr: the region, or %NULL if empty + * @fv: the flat view of the address space the region is mapped in + * @offset_within_region: the beginning of the section, relative to @mr's start + * @size: the size of the section; will not exceed @mr's boundaries + * @offset_within_address_space: the address of the first byte of the section + * relative to the region's address space + * @readonly: writes to this section are ignored + * @nonvolatile: this section is non-volatile + */ +struct MemoryRegionSection { + Int128 size; + MemoryRegion *mr; + FlatView *fv; + hwaddr offset_within_region; + hwaddr offset_within_address_space; + bool readonly; + bool nonvolatile; +}; + typedef struct IOMMUTLBEntry IOMMUTLBEntry; /* See address_space_translate: bit 0 is read, bit 1 is write. */ @@ -448,6 +476,206 @@ struct IOMMUMemoryRegionClass { Error **errp); }; +typedef struct RamDiscardListener RamDiscardListener; +typedef int (*NotifyRamPopulate)(RamDiscardListener *rdl, + MemoryRegionSection *section); +typedef void (*NotifyRamDiscard)(RamDiscardListener *rdl, + MemoryRegionSection *section); + +struct RamDiscardListener { + /* + * @notify_populate: + * + * Notification that previously discarded memory is about to get populated. + * Listeners are able to object. If any listener objects, already + * successfully notified listeners are notified about a discard again. + * + * @rdl: the #RamDiscardListener getting notified + * @section: the #MemoryRegionSection to get populated. The section + * is aligned within the memory region to the minimum granularity + * unless it would exceed the registered section. + * + * Returns 0 on success. If the notification is rejected by the listener, + * an error is returned. + */ + NotifyRamPopulate notify_populate; + + /* + * @notify_discard: + * + * Notification that previously populated memory was discarded successfully + * and listeners should drop all references to such memory and prevent + * new population (e.g., unmap). + * + * @rdl: the #RamDiscardListener getting notified + * @section: the #MemoryRegionSection to get populated. The section + * is aligned within the memory region to the minimum granularity + * unless it would exceed the registered section. + */ + NotifyRamDiscard notify_discard; + + /* + * @double_discard_supported: + * + * The listener suppors getting @notify_discard notifications that span + * already discarded parts. + */ + bool double_discard_supported; + + MemoryRegionSection *section; + QLIST_ENTRY(RamDiscardListener) next; +}; + +static inline void ram_discard_listener_init(RamDiscardListener *rdl, + NotifyRamPopulate populate_fn, + NotifyRamDiscard discard_fn, + bool double_discard_supported) +{ + rdl->notify_populate = populate_fn; + rdl->notify_discard = discard_fn; + rdl->double_discard_supported = double_discard_supported; +} + +typedef int (*ReplayRamPopulate)(MemoryRegionSection *section, void *opaque); + +/* + * RamDiscardManagerClass: + * + * A #RamDiscardManager coordinates which parts of specific RAM #MemoryRegion + * regions are currently populated to be used/accessed by the VM, notifying + * after parts were discarded (freeing up memory) and before parts will be + * populated (consuming memory), to be used/acessed by the VM. + * + * A #RamDiscardManager can only be set for a RAM #MemoryRegion while the + * #MemoryRegion isn't mapped yet; it cannot change while the #MemoryRegion is + * mapped. + * + * The #RamDiscardManager is intended to be used by technologies that are + * incompatible with discarding of RAM (e.g., VFIO, which may pin all + * memory inside a #MemoryRegion), and require proper coordination to only + * map the currently populated parts, to hinder parts that are expected to + * remain discarded from silently getting populated and consuming memory. + * Technologies that support discarding of RAM don't have to bother and can + * simply map the whole #MemoryRegion. + * + * An example #RamDiscardManager is virtio-mem, which logically (un)plugs + * memory within an assigned RAM #MemoryRegion, coordinated with the VM. + * Logically unplugging memory consists of discarding RAM. The VM agreed to not + * access unplugged (discarded) memory - especially via DMA. virtio-mem will + * properly coordinate with listeners before memory is plugged (populated), + * and after memory is unplugged (discarded). + * + * Listeners are called in multiples of the minimum granularity (unless it + * would exceed the registered range) and changes are aligned to the minimum + * granularity within the #MemoryRegion. Listeners have to prepare for memory + * becomming discarded in a different granularity than it was populated and the + * other way around. + */ +struct RamDiscardManagerClass { + /* private */ + InterfaceClass parent_class; + + /* public */ + + /** + * @get_min_granularity: + * + * Get the minimum granularity in which listeners will get notified + * about changes within the #MemoryRegion via the #RamDiscardManager. + * + * @rdm: the #RamDiscardManager + * @mr: the #MemoryRegion + * + * Returns the minimum granularity. + */ + uint64_t (*get_min_granularity)(const RamDiscardManager *rdm, + const MemoryRegion *mr); + + /** + * @is_populated: + * + * Check whether the given #MemoryRegionSection is completely populated + * (i.e., no parts are currently discarded) via the #RamDiscardManager. + * There are no alignment requirements. + * + * @rdm: the #RamDiscardManager + * @section: the #MemoryRegionSection + * + * Returns whether the given range is completely populated. + */ + bool (*is_populated)(const RamDiscardManager *rdm, + const MemoryRegionSection *section); + + /** + * @replay_populated: + * + * Call the #ReplayRamPopulate 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 + * @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); + + /** + * @register_listener: + * + * Register a #RamDiscardListener for the given #MemoryRegionSection and + * immediately notify the #RamDiscardListener about all populated parts + * within the #MemoryRegionSection via the #RamDiscardManager. + * + * In case any notification fails, no further notifications are triggered + * and an error is logged. + * + * @rdm: the #RamDiscardManager + * @rdl: the #RamDiscardListener + * @section: the #MemoryRegionSection + */ + void (*register_listener)(RamDiscardManager *rdm, + RamDiscardListener *rdl, + MemoryRegionSection *section); + + /** + * @unregister_listener: + * + * Unregister a previously registered #RamDiscardListener via the + * #RamDiscardManager after notifying the #RamDiscardListener about all + * populated parts becoming unpopulated within the registered + * #MemoryRegionSection. + * + * @rdm: the #RamDiscardManager + * @rdl: the #RamDiscardListener + */ + void (*unregister_listener)(RamDiscardManager *rdm, + RamDiscardListener *rdl); +}; + +uint64_t ram_discard_manager_get_min_granularity(const RamDiscardManager *rdm, + const MemoryRegion *mr); + +bool ram_discard_manager_is_populated(const RamDiscardManager *rdm, + const MemoryRegionSection *section); + +int ram_discard_manager_replay_populated(const RamDiscardManager *rdm, + MemoryRegionSection *section, + ReplayRamPopulate replay_fn, + void *opaque); + +void ram_discard_manager_register_listener(RamDiscardManager *rdm, + RamDiscardListener *rdl, + MemoryRegionSection *section); + +void ram_discard_manager_unregister_listener(RamDiscardManager *rdm, + RamDiscardListener *rdl); + typedef struct CoalescedMemoryRange CoalescedMemoryRange; typedef struct MemoryRegionIoeventfd MemoryRegionIoeventfd; @@ -494,6 +722,7 @@ struct MemoryRegion { const char *name; unsigned ioeventfd_nb; MemoryRegionIoeventfd *ioeventfds; + RamDiscardManager *rdm; /* Only for RAM */ }; struct IOMMUMemoryRegion { @@ -825,28 +1054,6 @@ typedef bool (*flatview_cb)(Int128 start, */ void flatview_for_each_range(FlatView *fv, flatview_cb cb, void *opaque); -/** - * struct MemoryRegionSection: describes a fragment of a #MemoryRegion - * - * @mr: the region, or %NULL if empty - * @fv: the flat view of the address space the region is mapped in - * @offset_within_region: the beginning of the section, relative to @mr's start - * @size: the size of the section; will not exceed @mr's boundaries - * @offset_within_address_space: the address of the first byte of the section - * relative to the region's address space - * @readonly: writes to this section are ignored - * @nonvolatile: this section is non-volatile - */ -struct MemoryRegionSection { - Int128 size; - MemoryRegion *mr; - FlatView *fv; - hwaddr offset_within_region; - hwaddr offset_within_address_space; - bool readonly; - bool nonvolatile; -}; - static inline bool MemoryRegionSection_eq(MemoryRegionSection *a, MemoryRegionSection *b) { @@ -2023,6 +2230,41 @@ bool memory_region_present(MemoryRegion *container, hwaddr addr); */ bool memory_region_is_mapped(MemoryRegion *mr); +/** + * memory_region_get_ram_discard_manager: get the #RamDiscardManager for a + * #MemoryRegion + * + * The #RamDiscardManager cannot change while a memory region is mapped. + * + * @mr: the #MemoryRegion + */ +RamDiscardManager *memory_region_get_ram_discard_manager(MemoryRegion *mr); + +/** + * memory_region_has_ram_discard_manager: check whether a #MemoryRegion has a + * #RamDiscardManager assigned + * + * @mr: the #MemoryRegion + */ +static inline bool memory_region_has_ram_discard_manager(MemoryRegion *mr) +{ + return !!memory_region_get_ram_discard_manager(mr); +} + +/** + * memory_region_set_ram_discard_manager: set the #RamDiscardManager for a + * #MemoryRegion + * + * 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. + * + * @mr: the #MemoryRegion + * @rdm: #RamDiscardManager to set + */ +void memory_region_set_ram_discard_manager(MemoryRegion *mr, + RamDiscardManager *rdm); + /** * memory_region_find: translate an address/size relative to a * MemoryRegion into a #MemoryRegionSection. diff --git a/softmmu/memory.c b/softmmu/memory.c index f0161515e9..d20a9dec44 100644 --- a/softmmu/memory.c +++ b/softmmu/memory.c @@ -2027,6 +2027,70 @@ int memory_region_iommu_num_indexes(IOMMUMemoryRegion *iommu_mr) return imrc->num_indexes(iommu_mr); } +RamDiscardManager *memory_region_get_ram_discard_manager(MemoryRegion *mr) +{ + if (!memory_region_is_mapped(mr) || !memory_region_is_ram(mr)) { + return NULL; + } + return mr->rdm; +} + +void memory_region_set_ram_discard_manager(MemoryRegion *mr, + RamDiscardManager *rdm) +{ + g_assert(memory_region_is_ram(mr) && !memory_region_is_mapped(mr)); + g_assert(!rdm || !mr->rdm); + mr->rdm = rdm; +} + +uint64_t ram_discard_manager_get_min_granularity(const RamDiscardManager *rdm, + const MemoryRegion *mr) +{ + RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_GET_CLASS(rdm); + + g_assert(rdmc->get_min_granularity); + return rdmc->get_min_granularity(rdm, mr); +} + +bool ram_discard_manager_is_populated(const RamDiscardManager *rdm, + const MemoryRegionSection *section) +{ + RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_GET_CLASS(rdm); + + g_assert(rdmc->is_populated); + return rdmc->is_populated(rdm, section); +} + +int ram_discard_manager_replay_populated(const RamDiscardManager *rdm, + MemoryRegionSection *section, + ReplayRamPopulate replay_fn, + void *opaque) +{ + RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_GET_CLASS(rdm); + + g_assert(rdmc->replay_populated); + return rdmc->replay_populated(rdm, section, replay_fn, opaque); +} + +void ram_discard_manager_register_listener(RamDiscardManager *rdm, + RamDiscardListener *rdl, + MemoryRegionSection *section) +{ + RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_GET_CLASS(rdm); + + g_assert(rdmc->register_listener); + rdmc->register_listener(rdm, rdl, section); +} + +void ram_discard_manager_unregister_listener(RamDiscardManager *rdm, + RamDiscardListener *rdl) +{ + RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_GET_CLASS(rdm); + + g_assert(rdmc->unregister_listener); + rdmc->unregister_listener(rdm, rdl); +} + void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client) { uint8_t mask = 1 << client; @@ -3320,10 +3384,17 @@ static const TypeInfo iommu_memory_region_info = { .abstract = true, }; +static const TypeInfo ram_discard_manager_info = { + .parent = TYPE_INTERFACE, + .name = TYPE_RAM_DISCARD_MANAGER, + .class_size = sizeof(RamDiscardManagerClass), +}; + static void memory_register_types(void) { type_register_static(&memory_region_info); type_register_static(&iommu_memory_region_info); + type_register_static(&ram_discard_manager_info); } type_init(memory_register_types) From 228438384e64407949671e0b8b07258afb206ac2 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 13 Apr 2021 11:55:20 +0200 Subject: [PATCH 063/272] memory: Helpers to copy/free a MemoryRegionSection In case one wants to create a permanent copy of a MemoryRegionSections, one needs access to flatview_ref()/flatview_unref(). Instead of exposing these, let's just add helpers to copy/free a MemoryRegionSection and properly adjust references. Cc: Paolo Bonzini Cc: "Michael S. Tsirkin" Cc: Alex Williamson Cc: Dr. David Alan Gilbert Cc: Igor Mammedov Cc: Pankaj Gupta Cc: Peter Xu Cc: Auger Eric Cc: Wei Yang Cc: teawater Cc: Marek Kedzierski Signed-off-by: David Hildenbrand Message-Id: <20210413095531.25603-3-david@redhat.com> Signed-off-by: Eduardo Habkost --- include/exec/memory.h | 20 ++++++++++++++++++++ softmmu/memory.c | 27 +++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/include/exec/memory.h b/include/exec/memory.h index 6574e46b93..27a8833173 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -1066,6 +1066,26 @@ static inline bool MemoryRegionSection_eq(MemoryRegionSection *a, a->nonvolatile == b->nonvolatile; } +/** + * memory_region_section_new_copy: Copy a memory region section + * + * Allocate memory for a new copy, copy the memory region section, and + * properly take a reference on all relevant members. + * + * @s: the #MemoryRegionSection to copy + */ +MemoryRegionSection *memory_region_section_new_copy(MemoryRegionSection *s); + +/** + * memory_region_section_new_copy: Free a copied memory region section + * + * Free a copy of a memory section created via memory_region_section_new_copy(). + * properly dropping references on all relevant members. + * + * @s: the #MemoryRegionSection to copy + */ +void memory_region_section_free_copy(MemoryRegionSection *s); + /** * memory_region_init: Initialize a memory region * diff --git a/softmmu/memory.c b/softmmu/memory.c index d20a9dec44..cea2f622c9 100644 --- a/softmmu/memory.c +++ b/softmmu/memory.c @@ -2701,6 +2701,33 @@ MemoryRegionSection memory_region_find(MemoryRegion *mr, return ret; } +MemoryRegionSection *memory_region_section_new_copy(MemoryRegionSection *s) +{ + MemoryRegionSection *tmp = g_new(MemoryRegionSection, 1); + + *tmp = *s; + if (tmp->mr) { + memory_region_ref(tmp->mr); + } + if (tmp->fv) { + bool ret = flatview_ref(tmp->fv); + + g_assert(ret); + } + return tmp; +} + +void memory_region_section_free_copy(MemoryRegionSection *s) +{ + if (s->fv) { + flatview_unref(s->fv); + } + if (s->mr) { + memory_region_unref(s->mr); + } + g_free(s); +} + bool memory_region_present(MemoryRegion *container, hwaddr addr) { MemoryRegion *mr; From 7a9d5d0282c7f64b3e728c99edbacf9806fdba2c Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 13 Apr 2021 11:55:21 +0200 Subject: [PATCH 064/272] virtio-mem: Factor out traversing unplugged ranges Let's factor out the core logic, no need to replicate. Reviewed-by: Pankaj Gupta Acked-by: Michael S. Tsirkin Reviewed-by: Michael S. Tsirkin Cc: Paolo Bonzini Cc: "Michael S. Tsirkin" Cc: Alex Williamson Cc: Dr. David Alan Gilbert Cc: Igor Mammedov Cc: Pankaj Gupta Cc: Peter Xu Cc: Auger Eric Cc: Wei Yang Cc: teawater Cc: Marek Kedzierski Signed-off-by: David Hildenbrand Message-Id: <20210413095531.25603-4-david@redhat.com> Signed-off-by: Eduardo Habkost --- hw/virtio/virtio-mem.c | 86 ++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 37 deletions(-) diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index 75aa7d6f1b..3942fd7549 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -145,6 +145,33 @@ static bool virtio_mem_is_busy(void) return migration_in_incoming_postcopy() || !migration_is_idle(); } +typedef int (*virtio_mem_range_cb)(const VirtIOMEM *vmem, void *arg, + uint64_t offset, uint64_t size); + +static int virtio_mem_for_each_unplugged_range(const VirtIOMEM *vmem, void *arg, + virtio_mem_range_cb cb) +{ + unsigned long first_zero_bit, last_zero_bit; + uint64_t offset, size; + int ret = 0; + + first_zero_bit = find_first_zero_bit(vmem->bitmap, vmem->bitmap_size); + while (first_zero_bit < vmem->bitmap_size) { + offset = first_zero_bit * vmem->block_size; + last_zero_bit = find_next_bit(vmem->bitmap, vmem->bitmap_size, + first_zero_bit + 1) - 1; + size = (last_zero_bit - first_zero_bit + 1) * vmem->block_size; + + ret = cb(vmem, arg, offset, size); + if (ret) { + break; + } + first_zero_bit = find_next_zero_bit(vmem->bitmap, vmem->bitmap_size, + last_zero_bit + 2); + } + return ret; +} + static bool virtio_mem_test_bitmap(VirtIOMEM *vmem, uint64_t start_gpa, uint64_t size, bool plugged) { @@ -594,33 +621,27 @@ static void virtio_mem_device_unrealize(DeviceState *dev) ram_block_discard_require(false); } -static int virtio_mem_restore_unplugged(VirtIOMEM *vmem) +static int virtio_mem_discard_range_cb(const VirtIOMEM *vmem, void *arg, + uint64_t offset, uint64_t size) { RAMBlock *rb = vmem->memdev->mr.ram_block; - unsigned long first_zero_bit, last_zero_bit; - uint64_t offset, length; int ret; - /* Find consecutive unplugged blocks and discard the consecutive range. */ - first_zero_bit = find_first_zero_bit(vmem->bitmap, vmem->bitmap_size); - while (first_zero_bit < vmem->bitmap_size) { - offset = first_zero_bit * vmem->block_size; - last_zero_bit = find_next_bit(vmem->bitmap, vmem->bitmap_size, - first_zero_bit + 1) - 1; - length = (last_zero_bit - first_zero_bit + 1) * vmem->block_size; - - ret = ram_block_discard_range(rb, offset, length); - if (ret) { - error_report("Unexpected error discarding RAM: %s", - strerror(-ret)); - return -EINVAL; - } - first_zero_bit = find_next_zero_bit(vmem->bitmap, vmem->bitmap_size, - last_zero_bit + 2); + ret = ram_block_discard_range(rb, offset, size); + if (ret) { + error_report("Unexpected error discarding RAM: %s", strerror(-ret)); + return -EINVAL; } return 0; } +static int virtio_mem_restore_unplugged(VirtIOMEM *vmem) +{ + /* Make sure all memory is really discarded after migration. */ + return virtio_mem_for_each_unplugged_range(vmem, NULL, + virtio_mem_discard_range_cb); +} + static int virtio_mem_post_load(void *opaque, int version_id) { if (migration_in_incoming_postcopy()) { @@ -872,28 +893,19 @@ static void virtio_mem_set_block_size(Object *obj, Visitor *v, const char *name, vmem->block_size = value; } -static void virtio_mem_precopy_exclude_unplugged(VirtIOMEM *vmem) +static int virtio_mem_precopy_exclude_range_cb(const VirtIOMEM *vmem, void *arg, + uint64_t offset, uint64_t size) { void * const host = qemu_ram_get_host_addr(vmem->memdev->mr.ram_block); - unsigned long first_zero_bit, last_zero_bit; - uint64_t offset, length; - /* - * Find consecutive unplugged blocks and exclude them from migration. - * - * Note: Blocks cannot get (un)plugged during precopy, no locking needed. - */ - first_zero_bit = find_first_zero_bit(vmem->bitmap, vmem->bitmap_size); - while (first_zero_bit < vmem->bitmap_size) { - offset = first_zero_bit * vmem->block_size; - last_zero_bit = find_next_bit(vmem->bitmap, vmem->bitmap_size, - first_zero_bit + 1) - 1; - length = (last_zero_bit - first_zero_bit + 1) * vmem->block_size; + qemu_guest_free_page_hint(host + offset, size); + return 0; +} - qemu_guest_free_page_hint(host + offset, length); - first_zero_bit = find_next_zero_bit(vmem->bitmap, vmem->bitmap_size, - last_zero_bit + 2); - } +static void virtio_mem_precopy_exclude_unplugged(VirtIOMEM *vmem) +{ + virtio_mem_for_each_unplugged_range(vmem, NULL, + virtio_mem_precopy_exclude_range_cb); } static int virtio_mem_precopy_notify(NotifierWithReturn *n, void *data) From 3aca6380fdf566e518640de0d90ea6fa74b41825 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 13 Apr 2021 11:55:22 +0200 Subject: [PATCH 065/272] virtio-mem: Don't report errors when ram_block_discard_range() fails Any errors are unexpected and ram_block_discard_range() already properly prints errors. Let's stop manually reporting errors. Cc: Paolo Bonzini Cc: "Michael S. Tsirkin" Cc: Alex Williamson Cc: Dr. David Alan Gilbert Cc: Igor Mammedov Cc: Pankaj Gupta Cc: Peter Xu Cc: Auger Eric Cc: Wei Yang Cc: teawater Cc: Marek Kedzierski Signed-off-by: David Hildenbrand Message-Id: <20210413095531.25603-5-david@redhat.com> Signed-off-by: Eduardo Habkost --- hw/virtio/virtio-mem.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index 3942fd7549..a92a067b28 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -246,17 +246,14 @@ static int virtio_mem_set_block_state(VirtIOMEM *vmem, uint64_t start_gpa, uint64_t size, bool plug) { const uint64_t offset = start_gpa - vmem->addr; - int ret; + RAMBlock *rb = vmem->memdev->mr.ram_block; if (virtio_mem_is_busy()) { return -EBUSY; } if (!plug) { - ret = ram_block_discard_range(vmem->memdev->mr.ram_block, offset, size); - if (ret) { - error_report("Unexpected error discarding RAM: %s", - strerror(-ret)); + if (ram_block_discard_range(rb, offset, size)) { return -EBUSY; } } @@ -345,15 +342,12 @@ static void virtio_mem_resize_usable_region(VirtIOMEM *vmem, static int virtio_mem_unplug_all(VirtIOMEM *vmem) { RAMBlock *rb = vmem->memdev->mr.ram_block; - int ret; if (virtio_mem_is_busy()) { return -EBUSY; } - ret = ram_block_discard_range(rb, 0, qemu_ram_get_used_length(rb)); - if (ret) { - error_report("Unexpected error discarding RAM: %s", strerror(-ret)); + if (ram_block_discard_range(rb, 0, qemu_ram_get_used_length(rb))) { return -EBUSY; } bitmap_clear(vmem->bitmap, 0, vmem->bitmap_size); @@ -625,14 +619,8 @@ static int virtio_mem_discard_range_cb(const VirtIOMEM *vmem, void *arg, uint64_t offset, uint64_t size) { RAMBlock *rb = vmem->memdev->mr.ram_block; - int ret; - ret = ram_block_discard_range(rb, offset, size); - if (ret) { - error_report("Unexpected error discarding RAM: %s", strerror(-ret)); - return -EINVAL; - } - return 0; + return ram_block_discard_range(rb, offset, size) ? -EINVAL : 0; } static int virtio_mem_restore_unplugged(VirtIOMEM *vmem) From 2044969f0b27fa67f2b69bc710eaef45998cb6fb Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 13 Apr 2021 11:55:23 +0200 Subject: [PATCH 066/272] virtio-mem: Implement RamDiscardManager interface Let's properly notify when (un)plugging blocks, after discarding memory and before allowing the guest to consume memory. Handle errors from notifiers gracefully (e.g., no remaining VFIO mappings) when plugging, rolling back the change and telling the guest that the VM is busy. One special case to take care of is replaying all notifications after restoring the vmstate. The device starts out with all memory discarded, so after loading the vmstate, we have to notify about all plugged blocks. Acked-by: Michael S. Tsirkin Reviewed-by: Michael S. Tsirkin Cc: Paolo Bonzini Cc: "Michael S. Tsirkin" Cc: Alex Williamson Cc: Dr. David Alan Gilbert Cc: Igor Mammedov Cc: Pankaj Gupta Cc: Peter Xu Cc: Auger Eric Cc: Wei Yang Cc: teawater Cc: Marek Kedzierski Signed-off-by: David Hildenbrand Message-Id: <20210413095531.25603-6-david@redhat.com> Signed-off-by: Eduardo Habkost --- hw/virtio/virtio-mem.c | 288 ++++++++++++++++++++++++++++++++- include/hw/virtio/virtio-mem.h | 3 + 2 files changed, 288 insertions(+), 3 deletions(-) diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index a92a067b28..f60cb8a3fc 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -172,7 +172,146 @@ static int virtio_mem_for_each_unplugged_range(const VirtIOMEM *vmem, void *arg, return ret; } -static bool virtio_mem_test_bitmap(VirtIOMEM *vmem, uint64_t start_gpa, +/* + * Adjust the memory section to cover the intersection with the given range. + * + * Returns false if the intersection is empty, otherwise returns true. + */ +static bool virito_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, + MemoryRegionSection *s, + void *arg, + virtio_mem_section_cb cb) +{ + unsigned long first_bit, last_bit; + uint64_t offset, size; + int ret = 0; + + first_bit = s->offset_within_region / vmem->bitmap_size; + first_bit = find_next_bit(vmem->bitmap, vmem->bitmap_size, first_bit); + while (first_bit < vmem->bitmap_size) { + MemoryRegionSection tmp = *s; + + offset = first_bit * vmem->block_size; + last_bit = find_next_zero_bit(vmem->bitmap, vmem->bitmap_size, + first_bit + 1) - 1; + size = (last_bit - first_bit + 1) * vmem->block_size; + + if (!virito_mem_intersect_memory_section(&tmp, offset, size)) { + break; + } + ret = cb(&tmp, arg); + if (ret) { + break; + } + first_bit = find_next_bit(vmem->bitmap, vmem->bitmap_size, + last_bit + 2); + } + return ret; +} + +static int virtio_mem_notify_populate_cb(MemoryRegionSection *s, void *arg) +{ + RamDiscardListener *rdl = arg; + + return rdl->notify_populate(rdl, s); +} + +static int virtio_mem_notify_discard_cb(MemoryRegionSection *s, void *arg) +{ + RamDiscardListener *rdl = arg; + + rdl->notify_discard(rdl, s); + return 0; +} + +static void virtio_mem_notify_unplug(VirtIOMEM *vmem, uint64_t offset, + uint64_t size) +{ + RamDiscardListener *rdl; + + QLIST_FOREACH(rdl, &vmem->rdl_list, next) { + MemoryRegionSection tmp = *rdl->section; + + if (!virito_mem_intersect_memory_section(&tmp, offset, size)) { + continue; + } + rdl->notify_discard(rdl, &tmp); + } +} + +static int virtio_mem_notify_plug(VirtIOMEM *vmem, uint64_t offset, + uint64_t size) +{ + RamDiscardListener *rdl, *rdl2; + int ret = 0; + + QLIST_FOREACH(rdl, &vmem->rdl_list, next) { + MemoryRegionSection tmp = *rdl->section; + + if (!virito_mem_intersect_memory_section(&tmp, offset, size)) { + continue; + } + ret = rdl->notify_populate(rdl, &tmp); + if (ret) { + break; + } + } + + if (ret) { + /* Notify all already-notified listeners. */ + QLIST_FOREACH(rdl2, &vmem->rdl_list, next) { + MemoryRegionSection tmp = *rdl->section; + + if (rdl2 == rdl) { + break; + } + if (!virito_mem_intersect_memory_section(&tmp, offset, size)) { + continue; + } + rdl2->notify_discard(rdl2, &tmp); + } + } + return ret; +} + +static void virtio_mem_notify_unplug_all(VirtIOMEM *vmem) +{ + RamDiscardListener *rdl; + + if (!vmem->size) { + return; + } + + QLIST_FOREACH(rdl, &vmem->rdl_list, next) { + if (rdl->double_discard_supported) { + rdl->notify_discard(rdl, rdl->section); + } else { + virtio_mem_for_each_plugged_section(vmem, rdl->section, rdl, + virtio_mem_notify_discard_cb); + } + } +} + +static bool virtio_mem_test_bitmap(const VirtIOMEM *vmem, uint64_t start_gpa, uint64_t size, bool plugged) { const unsigned long first_bit = (start_gpa - vmem->addr) / vmem->block_size; @@ -225,7 +364,8 @@ static void virtio_mem_send_response_simple(VirtIOMEM *vmem, virtio_mem_send_response(vmem, elem, &resp); } -static bool virtio_mem_valid_range(VirtIOMEM *vmem, uint64_t gpa, uint64_t size) +static bool virtio_mem_valid_range(const VirtIOMEM *vmem, uint64_t gpa, + uint64_t size) { if (!QEMU_IS_ALIGNED(gpa, vmem->block_size)) { return false; @@ -256,6 +396,11 @@ static int virtio_mem_set_block_state(VirtIOMEM *vmem, uint64_t start_gpa, if (ram_block_discard_range(rb, offset, size)) { return -EBUSY; } + virtio_mem_notify_unplug(vmem, offset, size); + } else if (virtio_mem_notify_plug(vmem, offset, size)) { + /* Could be a mapping attempt resulted in memory getting populated. */ + ram_block_discard_range(vmem->memdev->mr.ram_block, offset, size); + return -EBUSY; } virtio_mem_set_bitmap(vmem, start_gpa, size, plug); return 0; @@ -350,6 +495,8 @@ static int virtio_mem_unplug_all(VirtIOMEM *vmem) if (ram_block_discard_range(rb, 0, qemu_ram_get_used_length(rb))) { return -EBUSY; } + virtio_mem_notify_unplug_all(vmem); + bitmap_clear(vmem->bitmap, 0, vmem->bitmap_size); if (vmem->size) { vmem->size = 0; @@ -598,6 +745,13 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp) vmstate_register_ram(&vmem->memdev->mr, DEVICE(vmem)); qemu_register_reset(virtio_mem_system_reset, vmem); precopy_add_notifier(&vmem->precopy_notifier); + + /* + * 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) @@ -605,6 +759,11 @@ 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); precopy_remove_notifier(&vmem->precopy_notifier); qemu_unregister_reset(virtio_mem_system_reset, vmem); vmstate_unregister_ram(&vmem->memdev->mr, DEVICE(vmem)); @@ -632,11 +791,27 @@ static int virtio_mem_restore_unplugged(VirtIOMEM *vmem) static int virtio_mem_post_load(void *opaque, int version_id) { + VirtIOMEM *vmem = VIRTIO_MEM(opaque); + RamDiscardListener *rdl; + int ret; + + /* + * We started out with all memory discarded and our memory region is mapped + * into an address space. Replay, now that we updated the bitmap. + */ + QLIST_FOREACH(rdl, &vmem->rdl_list, next) { + ret = virtio_mem_for_each_plugged_section(vmem, rdl->section, rdl, + virtio_mem_notify_populate_cb); + if (ret) { + return ret; + } + } + if (migration_in_incoming_postcopy()) { return 0; } - return virtio_mem_restore_unplugged(VIRTIO_MEM(opaque)); + return virtio_mem_restore_unplugged(vmem); } typedef struct VirtIOMEMMigSanityChecks { @@ -918,6 +1093,7 @@ static void virtio_mem_instance_init(Object *obj) notifier_list_init(&vmem->size_change_notifiers); vmem->precopy_notifier.notify = virtio_mem_precopy_notify; + QLIST_INIT(&vmem->rdl_list); object_property_add(obj, VIRTIO_MEM_SIZE_PROP, "size", virtio_mem_get_size, NULL, NULL, NULL); @@ -937,11 +1113,107 @@ static Property virtio_mem_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static uint64_t virtio_mem_rdm_get_min_granularity(const RamDiscardManager *rdm, + const MemoryRegion *mr) +{ + const VirtIOMEM *vmem = VIRTIO_MEM(rdm); + + g_assert(mr == &vmem->memdev->mr); + return vmem->block_size; +} + +static bool virtio_mem_rdm_is_populated(const RamDiscardManager *rdm, + const MemoryRegionSection *s) +{ + const VirtIOMEM *vmem = VIRTIO_MEM(rdm); + uint64_t start_gpa = vmem->addr + s->offset_within_region; + uint64_t end_gpa = start_gpa + int128_get64(s->size); + + g_assert(s->mr == &vmem->memdev->mr); + + start_gpa = QEMU_ALIGN_DOWN(start_gpa, vmem->block_size); + end_gpa = QEMU_ALIGN_UP(end_gpa, vmem->block_size); + + if (!virtio_mem_valid_range(vmem, start_gpa, end_gpa - start_gpa)) { + return false; + } + + return virtio_mem_test_bitmap(vmem, start_gpa, end_gpa - start_gpa, true); +} + +struct VirtIOMEMReplayData { + void *fn; + void *opaque; +}; + +static int virtio_mem_rdm_replay_populated_cb(MemoryRegionSection *s, void *arg) +{ + struct VirtIOMEMReplayData *data = arg; + + return ((ReplayRamPopulate)data->fn)(s, data->opaque); +} + +static int virtio_mem_rdm_replay_populated(const RamDiscardManager *rdm, + MemoryRegionSection *s, + ReplayRamPopulate replay_fn, + void *opaque) +{ + const VirtIOMEM *vmem = VIRTIO_MEM(rdm); + struct VirtIOMEMReplayData data = { + .fn = replay_fn, + .opaque = opaque, + }; + + g_assert(s->mr == &vmem->memdev->mr); + return virtio_mem_for_each_plugged_section(vmem, s, &data, + virtio_mem_rdm_replay_populated_cb); +} + +static void virtio_mem_rdm_register_listener(RamDiscardManager *rdm, + RamDiscardListener *rdl, + MemoryRegionSection *s) +{ + VirtIOMEM *vmem = VIRTIO_MEM(rdm); + int ret; + + g_assert(s->mr == &vmem->memdev->mr); + rdl->section = memory_region_section_new_copy(s); + + QLIST_INSERT_HEAD(&vmem->rdl_list, rdl, next); + ret = virtio_mem_for_each_plugged_section(vmem, rdl->section, rdl, + virtio_mem_notify_populate_cb); + if (ret) { + error_report("%s: Replaying plugged ranges failed: %s", __func__, + strerror(-ret)); + } +} + +static void virtio_mem_rdm_unregister_listener(RamDiscardManager *rdm, + RamDiscardListener *rdl) +{ + VirtIOMEM *vmem = VIRTIO_MEM(rdm); + + g_assert(rdl->section->mr == &vmem->memdev->mr); + if (vmem->size) { + if (rdl->double_discard_supported) { + rdl->notify_discard(rdl, rdl->section); + } else { + virtio_mem_for_each_plugged_section(vmem, rdl->section, rdl, + virtio_mem_notify_discard_cb); + } + } + + memory_region_section_free_copy(rdl->section); + rdl->section = NULL; + QLIST_REMOVE(rdl, next); +} + static void virtio_mem_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); VirtIOMEMClass *vmc = VIRTIO_MEM_CLASS(klass); + RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_CLASS(klass); device_class_set_props(dc, virtio_mem_properties); dc->vmsd = &vmstate_virtio_mem; @@ -957,6 +1229,12 @@ static void virtio_mem_class_init(ObjectClass *klass, void *data) vmc->get_memory_region = virtio_mem_get_memory_region; vmc->add_size_change_notifier = virtio_mem_add_size_change_notifier; vmc->remove_size_change_notifier = virtio_mem_remove_size_change_notifier; + + rdmc->get_min_granularity = virtio_mem_rdm_get_min_granularity; + rdmc->is_populated = virtio_mem_rdm_is_populated; + rdmc->replay_populated = virtio_mem_rdm_replay_populated; + rdmc->register_listener = virtio_mem_rdm_register_listener; + rdmc->unregister_listener = virtio_mem_rdm_unregister_listener; } static const TypeInfo virtio_mem_info = { @@ -966,6 +1244,10 @@ static const TypeInfo virtio_mem_info = { .instance_init = virtio_mem_instance_init, .class_init = virtio_mem_class_init, .class_size = sizeof(VirtIOMEMClass), + .interfaces = (InterfaceInfo[]) { + { TYPE_RAM_DISCARD_MANAGER }, + { } + }, }; static void virtio_register_types(void) diff --git a/include/hw/virtio/virtio-mem.h b/include/hw/virtio/virtio-mem.h index 4eeb82d5dd..9a6e348fa2 100644 --- a/include/hw/virtio/virtio-mem.h +++ b/include/hw/virtio/virtio-mem.h @@ -67,6 +67,9 @@ struct VirtIOMEM { /* don't migrate unplugged memory */ NotifierWithReturn precopy_notifier; + + /* listeners to notify on plug/unplug activity. */ + QLIST_HEAD(, RamDiscardListener) rdl_list; }; struct VirtIOMEMClass { From 5e3b981c330c58c4e97ab85e40c3bd2ee54b2fa7 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 13 Apr 2021 11:55:24 +0200 Subject: [PATCH 067/272] vfio: Support for RamDiscardManager in the !vIOMMU case Implement support for RamDiscardManager, to prepare for virtio-mem support. Instead of mapping the whole memory section, we only map "populated" parts and update the mapping when notified about discarding/population of memory via the RamDiscardListener. Similarly, when syncing the dirty bitmaps, sync only the actually mapped (populated) parts by replaying via the notifier. Using virtio-mem with vfio is still blocked via ram_block_discard_disable()/ram_block_discard_require() after this patch. Reviewed-by: Alex Williamson Acked-by: Alex Williamson Acked-by: Michael S. Tsirkin Cc: Paolo Bonzini Cc: "Michael S. Tsirkin" Cc: Alex Williamson Cc: Dr. David Alan Gilbert Cc: Igor Mammedov Cc: Pankaj Gupta Cc: Peter Xu Cc: Auger Eric Cc: Wei Yang Cc: teawater Cc: Marek Kedzierski Signed-off-by: David Hildenbrand Message-Id: <20210413095531.25603-7-david@redhat.com> Signed-off-by: Eduardo Habkost --- hw/vfio/common.c | 164 ++++++++++++++++++++++++++++++++++ include/hw/vfio/vfio-common.h | 11 +++ 2 files changed, 175 insertions(+) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index ae5654fcdb..5af7755227 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -649,6 +649,110 @@ out: rcu_read_unlock(); } +static void vfio_ram_discard_notify_discard(RamDiscardListener *rdl, + MemoryRegionSection *section) +{ + VFIORamDiscardListener *vrdl = container_of(rdl, VFIORamDiscardListener, + listener); + const hwaddr size = int128_get64(section->size); + const hwaddr iova = section->offset_within_address_space; + int ret; + + /* Unmap with a single call. */ + ret = vfio_dma_unmap(vrdl->container, iova, size , NULL); + if (ret) { + error_report("%s: vfio_dma_unmap() failed: %s", __func__, + strerror(-ret)); + } +} + +static int vfio_ram_discard_notify_populate(RamDiscardListener *rdl, + MemoryRegionSection *section) +{ + VFIORamDiscardListener *vrdl = container_of(rdl, VFIORamDiscardListener, + listener); + const hwaddr end = section->offset_within_region + + int128_get64(section->size); + hwaddr start, next, iova; + void *vaddr; + int ret; + + /* + * Map in (aligned within memory region) minimum granularity, so we can + * unmap in minimum granularity later. + */ + for (start = section->offset_within_region; start < end; start = next) { + next = ROUND_UP(start + 1, vrdl->granularity); + next = MIN(next, end); + + iova = start - section->offset_within_region + + section->offset_within_address_space; + vaddr = memory_region_get_ram_ptr(section->mr) + start; + + ret = vfio_dma_map(vrdl->container, iova, next - start, + vaddr, section->readonly); + if (ret) { + /* Rollback */ + vfio_ram_discard_notify_discard(rdl, section); + return ret; + } + } + return 0; +} + +static void vfio_register_ram_discard_listener(VFIOContainer *container, + MemoryRegionSection *section) +{ + RamDiscardManager *rdm = memory_region_get_ram_discard_manager(section->mr); + VFIORamDiscardListener *vrdl; + + /* Ignore some corner cases not relevant in practice. */ + g_assert(QEMU_IS_ALIGNED(section->offset_within_region, TARGET_PAGE_SIZE)); + g_assert(QEMU_IS_ALIGNED(section->offset_within_address_space, + TARGET_PAGE_SIZE)); + g_assert(QEMU_IS_ALIGNED(int128_get64(section->size), TARGET_PAGE_SIZE)); + + vrdl = g_new0(VFIORamDiscardListener, 1); + vrdl->container = container; + vrdl->mr = section->mr; + vrdl->offset_within_address_space = section->offset_within_address_space; + vrdl->size = int128_get64(section->size); + vrdl->granularity = ram_discard_manager_get_min_granularity(rdm, + section->mr); + + g_assert(vrdl->granularity && is_power_of_2(vrdl->granularity)); + g_assert(vrdl->granularity >= 1 << ctz64(container->pgsizes)); + + ram_discard_listener_init(&vrdl->listener, + vfio_ram_discard_notify_populate, + vfio_ram_discard_notify_discard, true); + ram_discard_manager_register_listener(rdm, &vrdl->listener, section); + QLIST_INSERT_HEAD(&container->vrdl_list, vrdl, next); +} + +static void vfio_unregister_ram_discard_listener(VFIOContainer *container, + MemoryRegionSection *section) +{ + RamDiscardManager *rdm = memory_region_get_ram_discard_manager(section->mr); + VFIORamDiscardListener *vrdl = NULL; + + QLIST_FOREACH(vrdl, &container->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 unregister missing RAM discard listener"); + } + + ram_discard_manager_unregister_listener(rdm, &vrdl->listener); + QLIST_REMOVE(vrdl, next); + g_free(vrdl); +} + static void vfio_listener_region_add(MemoryListener *listener, MemoryRegionSection *section) { @@ -810,6 +914,16 @@ static void vfio_listener_region_add(MemoryListener *listener, /* Here we assume that memory_region_is_ram(section->mr)==true */ + /* + * For RAM memory regions with a RamDiscardManager, we only want to map the + * actually populated parts - and update the mapping whenever we're notified + * about changes. + */ + if (memory_region_has_ram_discard_manager(section->mr)) { + vfio_register_ram_discard_listener(container, section); + return; + } + vaddr = memory_region_get_ram_ptr(section->mr) + section->offset_within_region + (iova - section->offset_within_address_space); @@ -947,6 +1061,10 @@ static void vfio_listener_region_del(MemoryListener *listener, pgmask = (1ULL << ctz64(hostwin->iova_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(container, section); + /* Unregistering will trigger an unmap. */ + try_unmap = false; } if (try_unmap) { @@ -1108,6 +1226,49 @@ static void vfio_iommu_map_dirty_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) rcu_read_unlock(); } +static int vfio_ram_discard_get_dirty_bitmap(MemoryRegionSection *section, + void *opaque) +{ + const hwaddr size = int128_get64(section->size); + const hwaddr iova = section->offset_within_address_space; + const ram_addr_t ram_addr = memory_region_get_ram_addr(section->mr) + + section->offset_within_region; + VFIORamDiscardListener *vrdl = opaque; + + /* + * Sync the whole mapped region (spanning multiple individual mappings) + * in one go. + */ + return vfio_get_dirty_bitmap(vrdl->container, iova, size, ram_addr); +} + +static int vfio_sync_ram_discard_listener_dirty_bitmap(VFIOContainer *container, + MemoryRegionSection *section) +{ + RamDiscardManager *rdm = memory_region_get_ram_discard_manager(section->mr); + VFIORamDiscardListener *vrdl = NULL; + + QLIST_FOREACH(vrdl, &container->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"); + } + + /* + * We only want/can synchronize the bitmap for actually mapped parts - + * which correspond to populated parts. Replay all populated parts. + */ + return ram_discard_manager_replay_populated(rdm, section, + vfio_ram_discard_get_dirty_bitmap, + &vrdl); +} + static int vfio_sync_dirty_bitmap(VFIOContainer *container, MemoryRegionSection *section) { @@ -1139,6 +1300,8 @@ static int vfio_sync_dirty_bitmap(VFIOContainer *container, } } return 0; + } else if (memory_region_has_ram_discard_manager(section->mr)) { + return vfio_sync_ram_discard_listener_dirty_bitmap(container, section); } ram_addr = memory_region_get_ram_addr(section->mr) + @@ -1770,6 +1933,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, container->dirty_pages_supported = false; QLIST_INIT(&container->giommu_list); QLIST_INIT(&container->hostwin_list); + QLIST_INIT(&container->vrdl_list); ret = vfio_init_container(container, group->fd, errp); if (ret) { diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 6141162d7a..681432213d 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -91,6 +91,7 @@ typedef struct VFIOContainer { QLIST_HEAD(, VFIOGuestIOMMU) giommu_list; QLIST_HEAD(, VFIOHostDMAWindow) hostwin_list; QLIST_HEAD(, VFIOGroup) group_list; + QLIST_HEAD(, VFIORamDiscardListener) vrdl_list; QLIST_ENTRY(VFIOContainer) next; } VFIOContainer; @@ -102,6 +103,16 @@ typedef struct VFIOGuestIOMMU { QLIST_ENTRY(VFIOGuestIOMMU) giommu_next; } VFIOGuestIOMMU; +typedef struct VFIORamDiscardListener { + VFIOContainer *container; + MemoryRegion *mr; + hwaddr offset_within_address_space; + hwaddr size; + uint64_t granularity; + RamDiscardListener listener; + QLIST_ENTRY(VFIORamDiscardListener) next; +} VFIORamDiscardListener; + typedef struct VFIOHostDMAWindow { hwaddr min_iova; hwaddr max_iova; From 3eed155caf0a9a6db1e140c01bd8f0300ac475ce Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 13 Apr 2021 11:55:25 +0200 Subject: [PATCH 068/272] vfio: Query and store the maximum number of possible DMA mappings Let's query the maximum number of possible DMA mappings by querying the available mappings when creating the container (before any mappings are created). We'll use this informaton soon to perform some sanity checks and warn the user. Reviewed-by: Alex Williamson Acked-by: Alex Williamson Acked-by: Michael S. Tsirkin Cc: Paolo Bonzini Cc: "Michael S. Tsirkin" Cc: Alex Williamson Cc: Dr. David Alan Gilbert Cc: Igor Mammedov Cc: Pankaj Gupta Cc: Peter Xu Cc: Auger Eric Cc: Wei Yang Cc: teawater Cc: Marek Kedzierski Signed-off-by: David Hildenbrand Message-Id: <20210413095531.25603-8-david@redhat.com> Signed-off-by: Eduardo Habkost --- hw/vfio/common.c | 4 ++++ include/hw/vfio/vfio-common.h | 1 + 2 files changed, 5 insertions(+) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 5af7755227..79628d60ae 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1931,6 +1931,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, container->fd = fd; container->error = NULL; container->dirty_pages_supported = false; + container->dma_max_mappings = 0; QLIST_INIT(&container->giommu_list); QLIST_INIT(&container->hostwin_list); QLIST_INIT(&container->vrdl_list); @@ -1962,7 +1963,10 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, vfio_host_win_add(container, 0, (hwaddr)-1, info->iova_pgsizes); container->pgsizes = info->iova_pgsizes; + /* The default in the kernel ("dma_entry_limit") is 65535. */ + container->dma_max_mappings = 65535; if (!ret) { + vfio_get_info_dma_avail(info, &container->dma_max_mappings); vfio_get_iommu_info_migration(container, info); } g_free(info); diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 681432213d..8af11b0a76 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -88,6 +88,7 @@ typedef struct VFIOContainer { uint64_t dirty_pgsizes; uint64_t max_dirty_bitmap_size; unsigned long pgsizes; + unsigned int dma_max_mappings; QLIST_HEAD(, VFIOGuestIOMMU) giommu_list; QLIST_HEAD(, VFIOHostDMAWindow) hostwin_list; QLIST_HEAD(, VFIOGroup) group_list; From a74317f636eb3352210fff5c58896ddc1e5aabdf Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 13 Apr 2021 11:55:26 +0200 Subject: [PATCH 069/272] vfio: Sanity check maximum number of DMA mappings with RamDiscardManager Although RamDiscardManager can handle running into the maximum number of DMA mappings by propagating errors when creating a DMA mapping, we want to sanity check and warn the user early that there is a theoretical setup issue and that virtio-mem might not be able to provide as much memory towards a VM as desired. As suggested by Alex, let's use the number of KVM memory slots to guess how many other mappings we might see over time. Acked-by: Alex Williamson Reviewed-by: Alex Williamson Acked-by: Michael S. Tsirkin Cc: Paolo Bonzini Cc: "Michael S. Tsirkin" Cc: Alex Williamson Cc: Dr. David Alan Gilbert Cc: Igor Mammedov Cc: Pankaj Gupta Cc: Peter Xu Cc: Auger Eric Cc: Wei Yang Cc: teawater Cc: Marek Kedzierski Signed-off-by: David Hildenbrand Message-Id: <20210413095531.25603-9-david@redhat.com> Signed-off-by: Eduardo Habkost --- hw/vfio/common.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 79628d60ae..f8a2fe8441 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -728,6 +728,49 @@ static void vfio_register_ram_discard_listener(VFIOContainer *container, vfio_ram_discard_notify_discard, true); ram_discard_manager_register_listener(rdm, &vrdl->listener, section); QLIST_INSERT_HEAD(&container->vrdl_list, vrdl, next); + + /* + * Sanity-check if we have a theoretically problematic setup where we could + * exceed the maximum number of possible DMA mappings over time. We assume + * that each mapped section in the same address space as a RamDiscardManager + * section consumes exactly one DMA mapping, with the exception of + * RamDiscardManager sections; i.e., we don't expect to have gIOMMU sections + * in the same address space as RamDiscardManager sections. + * + * We assume that each section in the address space consumes one memslot. + * We take the number of KVM memory slots as a best guess for the maximum + * number of sections in the address space we could have over time, + * also consuming DMA mappings. + */ + if (container->dma_max_mappings) { + unsigned int vrdl_count = 0, vrdl_mappings = 0, max_memslots = 512; + +#ifdef CONFIG_KVM + if (kvm_enabled()) { + max_memslots = kvm_get_max_memslots(); + } +#endif + + QLIST_FOREACH(vrdl, &container->vrdl_list, next) { + hwaddr start, end; + + start = QEMU_ALIGN_DOWN(vrdl->offset_within_address_space, + vrdl->granularity); + end = ROUND_UP(vrdl->offset_within_address_space + vrdl->size, + vrdl->granularity); + vrdl_mappings += (end - start) / vrdl->granularity; + vrdl_count++; + } + + if (vrdl_mappings + max_memslots - vrdl_count > + container->dma_max_mappings) { + warn_report("%s: possibly running out of DMA mappings. E.g., try" + " increasing the 'block-size' of virtio-mem devies." + " Maximum possible DMA mappings: %d, Maximum possible" + " memslots: %d", __func__, container->dma_max_mappings, + max_memslots); + } + } } static void vfio_unregister_ram_discard_listener(VFIOContainer *container, From 0fd7616e0f1171b8149bb71f59e23ab048a8df83 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 13 Apr 2021 11:55:27 +0200 Subject: [PATCH 070/272] vfio: Support for RamDiscardManager in the vIOMMU case vIOMMU support works already with RamDiscardManager as long as guests only map populated memory. Both, populated and discarded memory is mapped into &address_space_memory, where vfio_get_xlat_addr() will find that memory, to create the vfio mapping. Sane guests will never map discarded memory (e.g., unplugged memory blocks in virtio-mem) into an IOMMU - or keep it mapped into an IOMMU while memory is getting discarded. However, there are two cases where a malicious guests could trigger pinning of more memory than intended. One case is easy to handle: the guest trying to map discarded memory into an IOMMU. The other case is harder to handle: the guest keeping memory mapped in the IOMMU while it is getting discarded. We would have to walk over all mappings when discarding memory and identify if any mapping would be a violation. Let's keep it simple for now and print a warning, indicating that setting RLIMIT_MEMLOCK can mitigate such attacks. We have to take care of incoming migration: at the point the IOMMUs get restored and start creating mappings in vfio, RamDiscardManager implementations might not be back up and running yet: let's add runstate priorities to enforce the order when restoring. Acked-by: Alex Williamson Reviewed-by: Alex Williamson Acked-by: Michael S. Tsirkin Cc: Paolo Bonzini Cc: "Michael S. Tsirkin" Cc: Alex Williamson Cc: Dr. David Alan Gilbert Cc: Igor Mammedov Cc: Pankaj Gupta Cc: Peter Xu Cc: Auger Eric Cc: Wei Yang Cc: teawater Cc: Marek Kedzierski Signed-off-by: David Hildenbrand Message-Id: <20210413095531.25603-10-david@redhat.com> Signed-off-by: Eduardo Habkost --- hw/vfio/common.c | 39 +++++++++++++++++++++++++++++++++++++ hw/virtio/virtio-mem.c | 1 + include/migration/vmstate.h | 1 + 3 files changed, 41 insertions(+) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index f8a2fe8441..8a9bbf2791 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -36,6 +36,7 @@ #include "qemu/range.h" #include "sysemu/kvm.h" #include "sysemu/reset.h" +#include "sysemu/runstate.h" #include "trace.h" #include "qapi/error.h" #include "migration/migration.h" @@ -569,6 +570,44 @@ static bool vfio_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, error_report("iommu map to non memory area %"HWADDR_PRIx"", xlat); return false; + } else if (memory_region_has_ram_discard_manager(mr)) { + RamDiscardManager *rdm = memory_region_get_ram_discard_manager(mr); + MemoryRegionSection tmp = { + .mr = mr, + .offset_within_region = xlat, + .size = int128_make64(len), + }; + + /* + * Malicious VMs can map memory into the IOMMU, which is expected + * to remain discarded. vfio will pin all pages, populating memory. + * Disallow that. vmstate priorities make sure any RamDiscardManager + * were already restored before IOMMUs are restored. + */ + if (!ram_discard_manager_is_populated(rdm, &tmp)) { + error_report("iommu map to discarded memory (e.g., unplugged via" + " virtio-mem): %"HWADDR_PRIx"", + iotlb->translated_addr); + return false; + } + + /* + * Malicious VMs might trigger discarding of IOMMU-mapped memory. The + * pages will remain pinned inside vfio until unmapped, resulting in a + * higher memory consumption than expected. If memory would get + * populated again later, there would be an inconsistency between pages + * pinned by vfio and pages seen by QEMU. This is the case until + * unmapped from the IOMMU (e.g., during device reset). + * + * With malicious guests, we really only care about pinning more memory + * than expected. RLIMIT_MEMLOCK set for the user/process can never be + * exceeded and can be used to mitigate this problem. + */ + warn_report_once("Using vfio with vIOMMUs and coordinated discarding of" + " RAM (e.g., virtio-mem) works, however, malicious" + " guests can trigger pinning of more memory than" + " intended via an IOMMU. It's possible to mitigate " + " by setting/adjusting RLIMIT_MEMLOCK."); } /* diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index f60cb8a3fc..368ae1db90 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -886,6 +886,7 @@ static const VMStateDescription vmstate_virtio_mem_device = { .name = "virtio-mem-device", .minimum_version_id = 1, .version_id = 1, + .priority = MIG_PRI_VIRTIO_MEM, .post_load = virtio_mem_post_load, .fields = (VMStateField[]) { VMSTATE_WITH_TMP(VirtIOMEM, VirtIOMEMMigSanityChecks, diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 8df7b69f38..017c03675c 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -153,6 +153,7 @@ typedef enum { MIG_PRI_DEFAULT = 0, MIG_PRI_IOMMU, /* Must happen before PCI devices */ MIG_PRI_PCI_BUS, /* Must happen before IOMMU */ + MIG_PRI_VIRTIO_MEM, /* Must happen before IOMMU */ MIG_PRI_GICV3_ITS, /* Must happen before PCI devices */ MIG_PRI_GICV3, /* Must happen before the ITS */ MIG_PRI_MAX, From 98da491dff558df95768c5f81243fc49c6360a91 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 13 Apr 2021 11:55:28 +0200 Subject: [PATCH 071/272] softmmu/physmem: Don't use atomic operations in ram_block_discard_(disable|require) We have users in migration context that don't hold the BQL (when finishing migration). To prepare for further changes, use a dedicated mutex instead of atomic operations. Keep using qatomic_read ("READ_ONCE") for the functions that only extract the current state (e.g., used by virtio-balloon), locking isn't necessary. While at it, split up the counter into two variables to make it easier to understand. Suggested-by: Peter Xu Reviewed-by: Peter Xu Reviewed-by: Pankaj Gupta Acked-by: Michael S. Tsirkin Cc: Paolo Bonzini Cc: "Michael S. Tsirkin" Cc: Alex Williamson Cc: Dr. David Alan Gilbert Cc: Igor Mammedov Cc: Pankaj Gupta Cc: Peter Xu Cc: Auger Eric Cc: Wei Yang Cc: teawater Cc: Marek Kedzierski Signed-off-by: David Hildenbrand Message-Id: <20210413095531.25603-11-david@redhat.com> Signed-off-by: Eduardo Habkost --- softmmu/physmem.c | 70 ++++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/softmmu/physmem.c b/softmmu/physmem.c index 9b171c9dbe..f1275b61f8 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -3684,56 +3684,64 @@ void mtree_print_dispatch(AddressSpaceDispatch *d, MemoryRegion *root) } } -/* - * If positive, discarding RAM is disabled. If negative, discarding RAM is - * required to work and cannot be disabled. - */ -static int ram_block_discard_disabled; +static unsigned int ram_block_discard_required_cnt; +static unsigned int ram_block_discard_disabled_cnt; +static QemuMutex ram_block_discard_disable_mutex; + +static void ram_block_discard_disable_mutex_lock(void) +{ + static gsize initialized; + + if (g_once_init_enter(&initialized)) { + qemu_mutex_init(&ram_block_discard_disable_mutex); + g_once_init_leave(&initialized, 1); + } + qemu_mutex_lock(&ram_block_discard_disable_mutex); +} + +static void ram_block_discard_disable_mutex_unlock(void) +{ + qemu_mutex_unlock(&ram_block_discard_disable_mutex); +} int ram_block_discard_disable(bool state) { - int old; + int ret = 0; + ram_block_discard_disable_mutex_lock(); if (!state) { - qatomic_dec(&ram_block_discard_disabled); - return 0; + ram_block_discard_disabled_cnt--; + } else if (!ram_block_discard_required_cnt) { + ram_block_discard_disabled_cnt++; + } else { + ret = -EBUSY; } - - do { - old = qatomic_read(&ram_block_discard_disabled); - if (old < 0) { - return -EBUSY; - } - } while (qatomic_cmpxchg(&ram_block_discard_disabled, - old, old + 1) != old); - return 0; + ram_block_discard_disable_mutex_unlock(); + return ret; } int ram_block_discard_require(bool state) { - int old; + int ret = 0; + ram_block_discard_disable_mutex_lock(); if (!state) { - qatomic_inc(&ram_block_discard_disabled); - return 0; + ram_block_discard_required_cnt--; + } else if (!ram_block_discard_disabled_cnt) { + ram_block_discard_required_cnt++; + } else { + ret = -EBUSY; } - - do { - old = qatomic_read(&ram_block_discard_disabled); - if (old > 0) { - return -EBUSY; - } - } while (qatomic_cmpxchg(&ram_block_discard_disabled, - old, old - 1) != old); - return 0; + ram_block_discard_disable_mutex_unlock(); + return ret; } bool ram_block_discard_is_disabled(void) { - return qatomic_read(&ram_block_discard_disabled) > 0; + return qatomic_read(&ram_block_discard_disabled_cnt); } bool ram_block_discard_is_required(void) { - return qatomic_read(&ram_block_discard_disabled) < 0; + return qatomic_read(&ram_block_discard_required_cnt); } From 7e6d32ebf79079a88e24da3359e2427ebed5f1be Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 13 Apr 2021 11:55:29 +0200 Subject: [PATCH 072/272] softmmu/physmem: Extend ram_block_discard_(require|disable) by two discard types We want to separate the two cases whereby we discard ram - uncoordinated: e.g., virito-balloon - coordinated: e.g., virtio-mem coordinated via the RamDiscardManager Reviewed-by: Pankaj Gupta Acked-by: Michael S. Tsirkin Cc: Paolo Bonzini Cc: "Michael S. Tsirkin" Cc: Alex Williamson Cc: Dr. David Alan Gilbert Cc: Igor Mammedov Cc: Pankaj Gupta Cc: Peter Xu Cc: Auger Eric Cc: Wei Yang Cc: teawater Cc: Marek Kedzierski Signed-off-by: David Hildenbrand Message-Id: <20210413095531.25603-12-david@redhat.com> Signed-off-by: Eduardo Habkost --- include/exec/memory.h | 18 ++++++++++++-- softmmu/physmem.c | 58 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/include/exec/memory.h b/include/exec/memory.h index 27a8833173..c3d417d317 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -2893,6 +2893,12 @@ static inline MemOp devend_memop(enum device_endian end) */ int ram_block_discard_disable(bool state); +/* + * See ram_block_discard_disable(): only disable uncoordinated discards, + * keeping coordinated discards (via the RamDiscardManager) enabled. + */ +int ram_block_uncoordinated_discard_disable(bool state); + /* * Inhibit technologies that disable discarding of pages in RAM blocks. * @@ -2902,12 +2908,20 @@ int ram_block_discard_disable(bool state); int ram_block_discard_require(bool state); /* - * Test if discarding of memory in ram blocks is disabled. + * See ram_block_discard_require(): only inhibit technologies that disable + * uncoordinated discarding of pages in RAM blocks, allowing co-existance with + * technologies that only inhibit uncoordinated discards (via the + * RamDiscardManager). + */ +int ram_block_coordinated_discard_require(bool state); + +/* + * Test if any discarding of memory in ram blocks is disabled. */ bool ram_block_discard_is_disabled(void); /* - * Test if discarding of memory in ram blocks is required to work reliably. + * Test if any discarding of memory in ram blocks is required to work reliably. */ bool ram_block_discard_is_required(void); diff --git a/softmmu/physmem.c b/softmmu/physmem.c index f1275b61f8..3c1912a1a0 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -3684,8 +3684,14 @@ void mtree_print_dispatch(AddressSpaceDispatch *d, MemoryRegion *root) } } +/* Require any discards to work. */ static unsigned int ram_block_discard_required_cnt; +/* Require only coordinated discards to work. */ +static unsigned int ram_block_coordinated_discard_required_cnt; +/* Disable any discards. */ static unsigned int ram_block_discard_disabled_cnt; +/* Disable only uncoordinated discards. */ +static unsigned int ram_block_uncoordinated_discard_disabled_cnt; static QemuMutex ram_block_discard_disable_mutex; static void ram_block_discard_disable_mutex_lock(void) @@ -3711,10 +3717,27 @@ int ram_block_discard_disable(bool state) ram_block_discard_disable_mutex_lock(); if (!state) { ram_block_discard_disabled_cnt--; - } else if (!ram_block_discard_required_cnt) { - ram_block_discard_disabled_cnt++; - } else { + } else if (ram_block_discard_required_cnt || + ram_block_coordinated_discard_required_cnt) { ret = -EBUSY; + } else { + ram_block_discard_disabled_cnt++; + } + ram_block_discard_disable_mutex_unlock(); + return ret; +} + +int ram_block_uncoordinated_discard_disable(bool state) +{ + int ret = 0; + + ram_block_discard_disable_mutex_lock(); + if (!state) { + ram_block_uncoordinated_discard_disabled_cnt--; + } else if (ram_block_discard_required_cnt) { + ret = -EBUSY; + } else { + ram_block_uncoordinated_discard_disabled_cnt++; } ram_block_discard_disable_mutex_unlock(); return ret; @@ -3727,10 +3750,27 @@ int ram_block_discard_require(bool state) ram_block_discard_disable_mutex_lock(); if (!state) { ram_block_discard_required_cnt--; - } else if (!ram_block_discard_disabled_cnt) { - ram_block_discard_required_cnt++; - } else { + } else if (ram_block_discard_disabled_cnt || + ram_block_uncoordinated_discard_disabled_cnt) { ret = -EBUSY; + } else { + ram_block_discard_required_cnt++; + } + ram_block_discard_disable_mutex_unlock(); + return ret; +} + +int ram_block_coordinated_discard_require(bool state) +{ + int ret = 0; + + ram_block_discard_disable_mutex_lock(); + if (!state) { + ram_block_coordinated_discard_required_cnt--; + } else if (ram_block_discard_disabled_cnt) { + ret = -EBUSY; + } else { + ram_block_coordinated_discard_required_cnt++; } ram_block_discard_disable_mutex_unlock(); return ret; @@ -3738,10 +3778,12 @@ int ram_block_discard_require(bool state) bool ram_block_discard_is_disabled(void) { - return qatomic_read(&ram_block_discard_disabled_cnt); + return qatomic_read(&ram_block_discard_disabled_cnt) || + qatomic_read(&ram_block_uncoordinated_discard_disabled_cnt); } bool ram_block_discard_is_required(void) { - return qatomic_read(&ram_block_discard_required_cnt); + return qatomic_read(&ram_block_discard_required_cnt) || + qatomic_read(&ram_block_coordinated_discard_required_cnt); } From bc072ed403e6f08c1911db4687511adcb3ecf587 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 13 Apr 2021 11:55:30 +0200 Subject: [PATCH 073/272] virtio-mem: Require only coordinated discards We implement the RamDiscardManager interface and only require coordinated discarding of RAM to work. Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Pankaj Gupta Acked-by: Michael S. Tsirkin Reviewed-by: Michael S. Tsirkin Cc: Paolo Bonzini Cc: "Michael S. Tsirkin" Cc: Alex Williamson Cc: Dr. David Alan Gilbert Cc: Igor Mammedov Cc: Pankaj Gupta Cc: Peter Xu Cc: Auger Eric Cc: Wei Yang Cc: teawater Cc: Marek Kedzierski Signed-off-by: David Hildenbrand Message-Id: <20210413095531.25603-13-david@redhat.com> Signed-off-by: Eduardo Habkost --- hw/virtio/virtio-mem.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index 368ae1db90..df91e454b2 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -719,7 +719,7 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp) return; } - if (ram_block_discard_require(true)) { + if (ram_block_coordinated_discard_require(true)) { error_setg(errp, "Discarding RAM is disabled"); return; } @@ -727,7 +727,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"); - ram_block_discard_require(false); + ram_block_coordinated_discard_require(false); return; } @@ -771,7 +771,7 @@ static void virtio_mem_device_unrealize(DeviceState *dev) virtio_del_queue(vdev, 0); virtio_cleanup(vdev); g_free(vmem->bitmap); - ram_block_discard_require(false); + ram_block_coordinated_discard_require(false); } static int virtio_mem_discard_range_cb(const VirtIOMEM *vmem, void *arg, From 53d1b5fcfb40c47da4c060dc913df0e9f62894bd Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 13 Apr 2021 11:55:31 +0200 Subject: [PATCH 074/272] vfio: Disable only uncoordinated discards for VFIO_TYPE1 iommus We support coordinated discarding of RAM using the RamDiscardManager for the VFIO_TYPE1 iommus. Let's unlock support for coordinated discards, keeping uncoordinated discards (e.g., via virtio-balloon) disabled if possible. This unlocks virtio-mem + vfio on x86-64. Note that vfio used via "nvme://" by the block layer has to be implemented/unlocked separately. For now, virtio-mem only supports x86-64; we don't restrict RamDiscardManager to x86-64, though: arm64 and s390x are supposed to work as well, and we'll test once unlocking virtio-mem support. The spapr IOMMUs will need special care, to be tackled later, e.g.., once supporting virtio-mem. Note: The block size of a virtio-mem device has to be set to sane sizes, depending on the maximum hotplug size - to not run out of vfio mappings. The default virtio-mem block size is usually in the range of a couple of MBs. The maximum number of mapping is 64k, shared with other users. Assume you want to hotplug 256GB using virtio-mem - the block size would have to be set to at least 8 MiB (resulting in 32768 separate mappings). Acked-by: Alex Williamson Reviewed-by: Alex Williamson Acked-by: Michael S. Tsirkin Cc: Paolo Bonzini Cc: "Michael S. Tsirkin" Cc: Alex Williamson Cc: Dr. David Alan Gilbert Cc: Igor Mammedov Cc: Pankaj Gupta Cc: Peter Xu Cc: Auger Eric Cc: Wei Yang Cc: teawater Cc: Marek Kedzierski Signed-off-by: David Hildenbrand Message-Id: <20210413095531.25603-14-david@redhat.com> Signed-off-by: Eduardo Habkost --- hw/vfio/common.c | 65 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 12 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 8a9bbf2791..3f0d111360 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -135,6 +135,29 @@ static const char *index_to_str(VFIODevice *vbasedev, int index) } } +static int vfio_ram_block_discard_disable(VFIOContainer *container, bool state) +{ + switch (container->iommu_type) { + case VFIO_TYPE1v2_IOMMU: + case VFIO_TYPE1_IOMMU: + /* + * We support coordinated discarding of RAM via the RamDiscardManager. + */ + return ram_block_uncoordinated_discard_disable(state); + default: + /* + * VFIO_SPAPR_TCE_IOMMU most probably works just fine with + * RamDiscardManager, however, it is completely untested. + * + * VFIO_SPAPR_TCE_v2_IOMMU with "DMA memory preregistering" does + * completely the opposite of managing mapping/pinning dynamically as + * required by RamDiscardManager. We would have to special-case sections + * with a RamDiscardManager. + */ + return ram_block_discard_disable(state); + } +} + int vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex, int action, int fd, Error **errp) { @@ -1977,15 +2000,25 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, * new memory, it will not yet set ram_block_discard_set_required() and * therefore, neither stops us here or deals with the sudden memory * consumption of inflated memory. + * + * We do support discarding of memory coordinated via the RamDiscardManager + * with some IOMMU types. vfio_ram_block_discard_disable() handles the + * details once we know which type of IOMMU we are using. */ - ret = ram_block_discard_disable(true); - if (ret) { - error_setg_errno(errp, -ret, "Cannot set discarding of RAM broken"); - return ret; - } QLIST_FOREACH(container, &space->containers, next) { if (!ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &container->fd)) { + ret = vfio_ram_block_discard_disable(container, true); + if (ret) { + error_setg_errno(errp, -ret, + "Cannot set discarding of RAM broken"); + if (ioctl(group->fd, VFIO_GROUP_UNSET_CONTAINER, + &container->fd)) { + error_report("vfio: error disconnecting group %d from" + " container", group->groupid); + } + return ret; + } group->container = container; QLIST_INSERT_HEAD(&container->group_list, group, container_next); vfio_kvm_device_add_group(group); @@ -2023,6 +2056,12 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, goto free_container_exit; } + ret = vfio_ram_block_discard_disable(container, true); + if (ret) { + error_setg_errno(errp, -ret, "Cannot set discarding of RAM broken"); + goto free_container_exit; + } + switch (container->iommu_type) { case VFIO_TYPE1v2_IOMMU: case VFIO_TYPE1_IOMMU: @@ -2070,7 +2109,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, if (ret) { error_setg_errno(errp, errno, "failed to enable container"); ret = -errno; - goto free_container_exit; + goto enable_discards_exit; } } else { container->prereg_listener = vfio_prereg_listener; @@ -2082,7 +2121,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, ret = -1; error_propagate_prepend(errp, container->error, "RAM memory listener initialization failed: "); - goto free_container_exit; + goto enable_discards_exit; } } @@ -2095,7 +2134,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, if (v2) { memory_listener_unregister(&container->prereg_listener); } - goto free_container_exit; + goto enable_discards_exit; } if (v2) { @@ -2110,7 +2149,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, if (ret) { error_setg_errno(errp, -ret, "failed to remove existing window"); - goto free_container_exit; + goto enable_discards_exit; } } else { /* The default table uses 4K pages */ @@ -2151,6 +2190,9 @@ listener_release_exit: vfio_kvm_device_del_group(group); vfio_listener_release(container); +enable_discards_exit: + vfio_ram_block_discard_disable(container, false); + free_container_exit: g_free(container); @@ -2158,7 +2200,6 @@ close_fd_exit: close(fd); put_space_exit: - ram_block_discard_disable(false); vfio_put_address_space(space); return ret; @@ -2280,7 +2321,7 @@ void vfio_put_group(VFIOGroup *group) } if (!group->ram_block_discard_allowed) { - ram_block_discard_disable(false); + vfio_ram_block_discard_disable(group->container, false); } vfio_kvm_device_del_group(group); vfio_disconnect_container(group); @@ -2334,7 +2375,7 @@ int vfio_get_device(VFIOGroup *group, const char *name, if (!group->ram_block_discard_allowed) { group->ram_block_discard_allowed = true; - ram_block_discard_disable(false); + vfio_ram_block_discard_disable(group->container, false); } } From c11dc15d3aabb9dab04d9d2767e1b227d2b9085d Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 22 Jun 2021 16:09:25 +0200 Subject: [PATCH 075/272] target/ppc: Introduce ppc_interrupts_little_endian() PowerPC CPUs use big endian by default but starting with POWER7, server grade CPUs use the ILE bit of the LPCR special purpose register to decide on the endianness to use when handling interrupts. This gives a clue to QEMU on the endianness the guest kernel is running, which is needed when generating an ELF dump of the guest or when delivering an FWNMI machine check interrupt. Commit 382d2db62bcb ("target-ppc: Introduce callback for interrupt endianness") added a class method to PowerPCCPUClass to modelize this : default implementation returns a fixed "big endian" value, while POWER7 and newer do the LPCR_ILE check. This is suboptimal as it forces to implement the method for every new CPU family, and it is very unlikely that this will ever be different than what we have today. We basically only have three cases to consider: a) CPU doesn't have an LPCR => big endian b) CPU has an LPCR but doesn't support the ILE bit => big endian c) CPU has an LPCR and supports the ILE bit => little or big endian Instead of class methods, introduce an inline helper that checks the ILE bit in the LPCR_MASK to decide on the outcome. The new helper words little endian instead of big endian. This allows to drop a ! operator in ppc_cpu_do_fwnmi_machine_check(). Signed-off-by: Greg Kurz Message-Id: <20210622140926.677618-2-groug@kaod.org> Reviewed-by: Fabiano Rosas Signed-off-by: David Gibson --- target/ppc/arch_dump.c | 8 +++----- target/ppc/cpu.h | 15 +++++++++++++++ target/ppc/excp_helper.c | 3 +-- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/target/ppc/arch_dump.c b/target/ppc/arch_dump.c index 9210e61ef4..bb392f6d88 100644 --- a/target/ppc/arch_dump.c +++ b/target/ppc/arch_dump.c @@ -227,22 +227,20 @@ int cpu_get_dump_info(ArchDumpInfo *info, const struct GuestPhysBlockList *guest_phys_blocks) { PowerPCCPU *cpu; - PowerPCCPUClass *pcc; if (first_cpu == NULL) { return -1; } cpu = POWERPC_CPU(first_cpu); - pcc = POWERPC_CPU_GET_CLASS(cpu); info->d_machine = PPC_ELF_MACHINE; info->d_class = ELFCLASS; - if ((*pcc->interrupts_big_endian)(cpu)) { - info->d_endian = ELFDATA2MSB; - } else { + if (ppc_interrupts_little_endian(cpu)) { info->d_endian = ELFDATA2LSB; + } else { + info->d_endian = ELFDATA2MSB; } /* 64KB is the max page size for pseries kernel */ if (strncmp(object_get_typename(qdev_get_machine()), diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index b4de0db7ff..93d308ac8f 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -2643,6 +2643,21 @@ static inline bool ppc_has_spr(PowerPCCPU *cpu, int spr) return cpu->env.spr_cb[spr].name != NULL; } +static inline bool ppc_interrupts_little_endian(PowerPCCPU *cpu) +{ + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + + /* + * Only models that have an LPCR and know about LPCR_ILE can do little + * endian. + */ + if (pcc->lpcr_mask & LPCR_ILE) { + return !!(cpu->env.spr[SPR_LPCR] & LPCR_ILE); + } + + return false; +} + void dump_mmu(CPUPPCState *env); void ppc_maybe_bswap_register(CPUPPCState *env, uint8_t *mem_buf, int len); diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index fd147e2a37..a79a0ed465 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -1099,7 +1099,6 @@ void ppc_cpu_do_fwnmi_machine_check(CPUState *cs, target_ulong vector) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); target_ulong msr = 0; /* @@ -1108,7 +1107,7 @@ void ppc_cpu_do_fwnmi_machine_check(CPUState *cs, target_ulong vector) */ msr = (1ULL << MSR_ME); msr |= env->msr & (1ULL << MSR_SF); - if (!(*pcc->interrupts_big_endian)(cpu)) { + if (ppc_interrupts_little_endian(cpu)) { msr |= (1ULL << MSR_LE); } From 642f6f59cda39a1f67276f4a5f74876975b6ee34 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 22 Jun 2021 16:09:26 +0200 Subject: [PATCH 076/272] target/ppc: Drop PowerPCCPUClass::interrupts_big_endian() This isn't used anymore. Signed-off-by: Greg Kurz Message-Id: <20210622140926.677618-3-groug@kaod.org> Reviewed-by: Fabiano Rosas Signed-off-by: David Gibson --- target/ppc/cpu-qom.h | 1 - target/ppc/cpu_init.c | 17 ----------------- 2 files changed, 18 deletions(-) diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index 06b6571bc9..7b424e3cb0 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -199,7 +199,6 @@ struct PowerPCCPUClass { void (*init_proc)(CPUPPCState *env); int (*check_pow)(CPUPPCState *env); int (*handle_mmu_fault)(PowerPCCPU *cpu, vaddr eaddr, int rwx, int mmu_idx); - bool (*interrupts_big_endian)(PowerPCCPU *cpu); }; #ifndef CONFIG_USER_ONLY diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index d0411e7302..1a22aef874 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -2666,18 +2666,6 @@ static int check_pow_hid0_74xx(CPUPPCState *env) return 0; } -static bool ppc_cpu_interrupts_big_endian_always(PowerPCCPU *cpu) -{ - return true; -} - -#ifdef TARGET_PPC64 -static bool ppc_cpu_interrupts_big_endian_lpcr(PowerPCCPU *cpu) -{ - return !(cpu->env.spr[SPR_LPCR] & LPCR_ILE); -} -#endif - /*****************************************************************************/ /* PowerPC implementations definitions */ @@ -7740,7 +7728,6 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) POWERPC_FLAG_VSX; pcc->l1_dcache_size = 0x8000; pcc->l1_icache_size = 0x8000; - pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr; } static void init_proc_POWER8(CPUPPCState *env) @@ -7918,7 +7905,6 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) POWERPC_FLAG_VSX | POWERPC_FLAG_TM; pcc->l1_dcache_size = 0x8000; pcc->l1_icache_size = 0x8000; - pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr; } #ifdef CONFIG_SOFTMMU @@ -8136,7 +8122,6 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) POWERPC_FLAG_VSX | POWERPC_FLAG_TM | POWERPC_FLAG_SCV; pcc->l1_dcache_size = 0x8000; pcc->l1_icache_size = 0x8000; - pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr; } #ifdef CONFIG_SOFTMMU @@ -8347,7 +8332,6 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) POWERPC_FLAG_VSX | POWERPC_FLAG_TM | POWERPC_FLAG_SCV; pcc->l1_dcache_size = 0x8000; pcc->l1_icache_size = 0x8000; - pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr; } #if !defined(CONFIG_USER_ONLY) @@ -9094,7 +9078,6 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_unrealize(dc, ppc_cpu_unrealize, &pcc->parent_unrealize); pcc->pvr_match = ppc_pvr_match_default; - pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_always; device_class_set_props(dc, ppc_cpu_properties); device_class_set_parent_reset(dc, ppc_cpu_reset, &pcc->parent_reset); From 7381c5d11fe9a03ad3bf2e5700e96acc5cafe218 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Tue, 22 Jun 2021 17:03:36 +1000 Subject: [PATCH 077/272] spapr: tune rtas-size QEMU reserves space for RTAS via /rtas/rtas-size which tells the client how much space the RTAS requires to work which includes the RTAS binary blob implementing RTAS runtime. Because pseries supports FWNMI which requires plenty of space, QEMU reserves more than 2KB which is enough for the RTAS blob as it is just 20 bytes (under QEMU). Since FWNMI reset delivery was added, RTAS_SIZE macro is not used anymore. This replaces RTAS_SIZE with RTAS_MIN_SIZE and uses it in the /rtas/rtas-size calculation to account for the RTAS blob. Fixes: 0e236d347790 ("ppc/spapr: Implement FWNMI System Reset delivery") Signed-off-by: Alexey Kardashevskiy Message-Id: <20210622070336.1463250-1-aik@ozlabs.ru> Reviewed-by: Greg Kurz Signed-off-by: David Gibson --- hw/ppc/spapr.c | 8 ++++++-- include/hw/ppc/spapr.h | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 4dd90b75cc..9e19c57032 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -919,9 +919,13 @@ static void spapr_dt_rtas(SpaprMachineState *spapr, void *fdt) * * The extra 8 bytes is required because Linux's FWNMI error log check * is off-by-one. + * + * RTAS_MIN_SIZE is required for the RTAS blob itself. */ - _FDT(fdt_setprop_cell(fdt, rtas, "rtas-size", RTAS_ERROR_LOG_MAX + - ms->smp.max_cpus * sizeof(uint64_t)*2 + sizeof(uint64_t))); + _FDT(fdt_setprop_cell(fdt, rtas, "rtas-size", RTAS_MIN_SIZE + + RTAS_ERROR_LOG_MAX + + ms->smp.max_cpus * sizeof(uint64_t) * 2 + + sizeof(uint64_t))); _FDT(fdt_setprop_cell(fdt, rtas, "rtas-error-log-max", RTAS_ERROR_LOG_MAX)); _FDT(fdt_setprop_cell(fdt, rtas, "rtas-event-scan-rate", diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index f05219f75e..5697327e4c 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -770,7 +770,7 @@ void spapr_load_rtas(SpaprMachineState *spapr, void *fdt, hwaddr addr); #define SPAPR_IS_PCI_LIOBN(liobn) (!!((liobn) & 0x80000000)) #define SPAPR_PCI_DMA_WINDOW_NUM(liobn) ((liobn) & 0xff) -#define RTAS_SIZE 2048 +#define RTAS_MIN_SIZE 20 /* hv_rtas_size in SLOF */ #define RTAS_ERROR_LOG_MAX 2048 /* Offset from rtas-base where error log is placed */ From db20cc2c563bfa259f7574a064190cf6456861f6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 21 Jun 2021 09:51:06 -0300 Subject: [PATCH 078/272] target/ppc: Remove PowerPCCPUClass.handle_mmu_fault Instead, use a switch on env->mmu_model. This avoids some replicated information in cpu setup. Signed-off-by: Richard Henderson Message-Id: <20210621125115.67717-2-bruno.larsen@eldorado.org.br> Reviewed-by: Greg Kurz Signed-off-by: David Gibson --- target/ppc/cpu-qom.h | 1 - target/ppc/cpu_init.c | 45 ----------------------------------------- target/ppc/mmu_helper.c | 24 ++++++++++++++++++---- 3 files changed, 20 insertions(+), 50 deletions(-) diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index 7b424e3cb0..5800fa324e 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -198,7 +198,6 @@ struct PowerPCCPUClass { int n_host_threads; void (*init_proc)(CPUPPCState *env); int (*check_pow)(CPUPPCState *env); - int (*handle_mmu_fault)(PowerPCCPU *cpu, vaddr eaddr, int rwx, int mmu_idx); }; #ifndef CONFIG_USER_ONLY diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 1a22aef874..6f8ce010ba 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -4566,9 +4566,6 @@ POWERPC_FAMILY(601)(ObjectClass *oc, void *data) (1ull << MSR_IR) | (1ull << MSR_DR); pcc->mmu_model = POWERPC_MMU_601; -#if defined(CONFIG_SOFTMMU) - pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; -#endif pcc->excp_model = POWERPC_EXCP_601; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_601; @@ -4611,9 +4608,6 @@ POWERPC_FAMILY(601v)(ObjectClass *oc, void *data) (1ull << MSR_IR) | (1ull << MSR_DR); pcc->mmu_model = POWERPC_MMU_601; -#if defined(CONFIG_SOFTMMU) - pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; -#endif pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_601; pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK | POWERPC_FLAG_HID0_LE; @@ -4877,9 +4871,6 @@ POWERPC_FAMILY(604)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_32B; -#if defined(CONFIG_SOFTMMU) - pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; -#endif pcc->excp_model = POWERPC_EXCP_604; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_604; @@ -4961,9 +4952,6 @@ POWERPC_FAMILY(604E)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_32B; -#if defined(CONFIG_SOFTMMU) - pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; -#endif pcc->excp_model = POWERPC_EXCP_604; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_604; @@ -5032,9 +5020,6 @@ POWERPC_FAMILY(740)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_32B; -#if defined(CONFIG_SOFTMMU) - pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; -#endif pcc->excp_model = POWERPC_EXCP_7x0; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_750; @@ -5112,9 +5097,6 @@ POWERPC_FAMILY(750)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_32B; -#if defined(CONFIG_SOFTMMU) - pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; -#endif pcc->excp_model = POWERPC_EXCP_7x0; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_750; @@ -5315,9 +5297,6 @@ POWERPC_FAMILY(750cl)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_32B; -#if defined(CONFIG_SOFTMMU) - pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; -#endif pcc->excp_model = POWERPC_EXCP_7x0; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_750; @@ -5398,9 +5377,6 @@ POWERPC_FAMILY(750cx)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_32B; -#if defined(CONFIG_SOFTMMU) - pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; -#endif pcc->excp_model = POWERPC_EXCP_7x0; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_750; @@ -5486,9 +5462,6 @@ POWERPC_FAMILY(750fx)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_32B; -#if defined(CONFIG_SOFTMMU) - pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; -#endif pcc->excp_model = POWERPC_EXCP_7x0; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_750; @@ -5574,9 +5547,6 @@ POWERPC_FAMILY(750gx)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_32B; -#if defined(CONFIG_SOFTMMU) - pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; -#endif pcc->excp_model = POWERPC_EXCP_7x0; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_750; @@ -5816,9 +5786,6 @@ POWERPC_FAMILY(7400)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_32B; -#if defined(CONFIG_SOFTMMU) - pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; -#endif pcc->excp_model = POWERPC_EXCP_74xx; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_7400; @@ -5902,9 +5869,6 @@ POWERPC_FAMILY(7410)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_32B; -#if defined(CONFIG_SOFTMMU) - pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; -#endif pcc->excp_model = POWERPC_EXCP_74xx; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_7400; @@ -6731,9 +6695,6 @@ POWERPC_FAMILY(e600)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_32B; -#if defined(CONFIG_SOFTMMU) - pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; -#endif pcc->excp_model = POWERPC_EXCP_74xx; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_7400; @@ -7493,7 +7454,6 @@ POWERPC_FAMILY(970)(ObjectClass *oc, void *data) (1ull << MSR_RI); pcc->mmu_model = POWERPC_MMU_64B; #if defined(CONFIG_SOFTMMU) - pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; pcc->hash64_opts = &ppc_hash64_opts_basic; #endif pcc->excp_model = POWERPC_EXCP_970; @@ -7571,7 +7531,6 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data) LPCR_RMI | LPCR_HDICE; pcc->mmu_model = POWERPC_MMU_2_03; #if defined(CONFIG_SOFTMMU) - pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; pcc->hash64_opts = &ppc_hash64_opts_basic; pcc->lrg_decr_bits = 32; #endif @@ -7715,7 +7674,6 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) pcc->lpcr_pm = LPCR_P7_PECE0 | LPCR_P7_PECE1 | LPCR_P7_PECE2; pcc->mmu_model = POWERPC_MMU_2_06; #if defined(CONFIG_SOFTMMU) - pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; pcc->hash64_opts = &ppc_hash64_opts_POWER7; pcc->lrg_decr_bits = 32; #endif @@ -7891,7 +7849,6 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) LPCR_P8_PECE3 | LPCR_P8_PECE4; pcc->mmu_model = POWERPC_MMU_2_07; #if defined(CONFIG_SOFTMMU) - pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; pcc->hash64_opts = &ppc_hash64_opts_POWER7; pcc->lrg_decr_bits = 32; pcc->n_host_threads = 8; @@ -8106,7 +8063,6 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) pcc->lpcr_pm = LPCR_PDEE | LPCR_HDEE | LPCR_EEE | LPCR_DEE | LPCR_OEE; pcc->mmu_model = POWERPC_MMU_3_00; #if defined(CONFIG_SOFTMMU) - pcc->handle_mmu_fault = ppc64_v3_handle_mmu_fault; /* segment page size remain the same */ pcc->hash64_opts = &ppc_hash64_opts_POWER7; pcc->radix_page_info = &POWER9_radix_page_info; @@ -8317,7 +8273,6 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) pcc->lpcr_pm = LPCR_PDEE | LPCR_HDEE | LPCR_EEE | LPCR_DEE | LPCR_OEE; pcc->mmu_model = POWERPC_MMU_3_00; #if defined(CONFIG_SOFTMMU) - pcc->handle_mmu_fault = ppc64_v3_handle_mmu_fault; /* segment page size remain the same */ pcc->hash64_opts = &ppc_hash64_opts_POWER7; pcc->radix_page_info = &POWER10_radix_page_info; diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index 1ecb36e85a..c4b1c93e47 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -2947,14 +2947,30 @@ bool ppc_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, bool probe, uintptr_t retaddr) { PowerPCCPU *cpu = POWERPC_CPU(cs); - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); CPUPPCState *env = &cpu->env; int ret; - if (pcc->handle_mmu_fault) { - ret = pcc->handle_mmu_fault(cpu, addr, access_type, mmu_idx); - } else { + switch (env->mmu_model) { +#if defined(TARGET_PPC64) + case POWERPC_MMU_64B: + case POWERPC_MMU_2_03: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_07: + ret = ppc_hash64_handle_mmu_fault(cpu, addr, access_type, mmu_idx); + break; + case POWERPC_MMU_3_00: + ret = ppc64_v3_handle_mmu_fault(cpu, addr, access_type, mmu_idx); + break; +#endif + + case POWERPC_MMU_32B: + case POWERPC_MMU_601: + ret = ppc_hash32_handle_mmu_fault(cpu, addr, access_type, mmu_idx); + break; + + default: ret = cpu_ppc_handle_mmu_fault(env, addr, access_type, mmu_idx); + break; } if (unlikely(ret != 0)) { if (probe) { From 1b4d1cb31a886418635e288f89b2da24fd091c55 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 21 Jun 2021 09:51:07 -0300 Subject: [PATCH 079/272] target/ppc: Use MMUAccessType with *_handle_mmu_fault These changes were waiting until we didn't need to match the function type of PowerPCCPUClass.handle_mmu_fault. Signed-off-by: Richard Henderson Message-Id: <20210621125115.67717-3-bruno.larsen@eldorado.org.br> Reviewed-by: Greg Kurz Signed-off-by: David Gibson --- target/ppc/mmu-hash32.c | 7 ++----- target/ppc/mmu-hash32.h | 4 ++-- target/ppc/mmu-hash64.c | 6 +----- target/ppc/mmu-hash64.h | 4 ++-- target/ppc/mmu-radix64.c | 7 ++----- target/ppc/mmu-radix64.h | 4 ++-- 6 files changed, 11 insertions(+), 21 deletions(-) diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index 9f0a497657..8f19b43e47 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -415,8 +415,8 @@ static hwaddr ppc_hash32_pte_raddr(target_ulong sr, ppc_hash_pte32_t pte, return (rpn & ~mask) | (eaddr & mask); } -int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx, - int mmu_idx) +int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, + MMUAccessType access_type, int mmu_idx) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; @@ -425,11 +425,8 @@ int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx, ppc_hash_pte32_t pte; int prot; int need_prot; - MMUAccessType access_type; hwaddr raddr; - assert((rwx == 0) || (rwx == 1) || (rwx == 2)); - access_type = rwx; need_prot = prot_for_access_type(access_type); /* 1. Handle real mode accesses */ diff --git a/target/ppc/mmu-hash32.h b/target/ppc/mmu-hash32.h index 898021f0d8..30e35718a7 100644 --- a/target/ppc/mmu-hash32.h +++ b/target/ppc/mmu-hash32.h @@ -5,8 +5,8 @@ hwaddr get_pteg_offset32(PowerPCCPU *cpu, hwaddr hash); hwaddr ppc_hash32_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr); -int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, vaddr address, int rw, - int mmu_idx); +int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, vaddr address, + MMUAccessType access_type, int mmu_idx); /* * Segment register definitions diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index 708dffc31b..2febd369b1 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -874,7 +874,7 @@ static int build_vrma_slbe(PowerPCCPU *cpu, ppc_slb_t *slb) } int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, - int rwx, int mmu_idx) + MMUAccessType access_type, int mmu_idx) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; @@ -884,13 +884,9 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, hwaddr ptex; ppc_hash_pte64_t pte; int exec_prot, pp_prot, amr_prot, prot; - MMUAccessType access_type; int need_prot; hwaddr raddr; - assert((rwx == 0) || (rwx == 1) || (rwx == 2)); - access_type = rwx; - /* * Note on LPCR usage: 970 uses HID4, but our special variant of * store_spr copies relevant fields into env->spr[SPR_LPCR]. diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h index 4b8b8e7950..3e8a8eec1f 100644 --- a/target/ppc/mmu-hash64.h +++ b/target/ppc/mmu-hash64.h @@ -8,8 +8,8 @@ void dump_slb(PowerPCCPU *cpu); int ppc_store_slb(PowerPCCPU *cpu, target_ulong slot, target_ulong esid, target_ulong vsid); hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr); -int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr address, int rw, - int mmu_idx); +int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr address, + MMUAccessType access_type, int mmu_idx); void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu, target_ulong pte_index, target_ulong pte0, target_ulong pte1); diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index b6d191c1d8..1c707d387d 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -555,19 +555,16 @@ static int ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, return 0; } -int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx, - int mmu_idx) +int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, + MMUAccessType access_type, int mmu_idx) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; int page_size, prot; bool relocation; - MMUAccessType access_type; hwaddr raddr; assert(!(msr_hv && cpu->vhyp)); - assert((rwx == 0) || (rwx == 1) || (rwx == 2)); - access_type = rwx; relocation = (access_type == MMU_INST_FETCH ? msr_ir : msr_dr); /* HV or virtual hypervisor Real Mode Access */ diff --git a/target/ppc/mmu-radix64.h b/target/ppc/mmu-radix64.h index f28c5794d0..94bd72cb38 100644 --- a/target/ppc/mmu-radix64.h +++ b/target/ppc/mmu-radix64.h @@ -44,8 +44,8 @@ #ifdef TARGET_PPC64 -int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx, - int mmu_idx); +int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, + MMUAccessType access_type, int mmu_idx); hwaddr ppc_radix64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr); static inline int ppc_radix64_get_prot_eaa(uint64_t pte) From 42a611240e110c126dab318d52d9ca760b9ff01c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 21 Jun 2021 09:51:08 -0300 Subject: [PATCH 080/272] target/ppc: Push real-mode handling into ppc_radix64_xlate This removes some incomplete duplication between ppc_radix64_handle_mmu_fault and ppc_radix64_get_phys_page_debug. The former was correct wrt SPR_HRMOR and the latter was not. Signed-off-by: Richard Henderson Message-Id: <20210621125115.67717-4-bruno.larsen@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/mmu-radix64.c | 77 ++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 43 deletions(-) diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index 1c707d387d..dd5ae69052 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -465,7 +465,6 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, */ static int ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, - bool relocation, hwaddr *raddr, int *psizep, int *protp, bool guest_visible) { @@ -474,6 +473,37 @@ static int ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, ppc_v3_pate_t pate; int psize, prot; hwaddr g_raddr; + bool relocation; + + assert(!(msr_hv && cpu->vhyp)); + + relocation = (access_type == MMU_INST_FETCH ? msr_ir : msr_dr); + + /* HV or virtual hypervisor Real Mode Access */ + if (!relocation && (msr_hv || cpu->vhyp)) { + /* In real mode top 4 effective addr bits (mostly) ignored */ + *raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL; + + /* In HV mode, add HRMOR if top EA bit is clear */ + if (msr_hv || !env->has_hv_mode) { + if (!(eaddr >> 63)) { + *raddr |= env->spr[SPR_HRMOR]; + } + } + *protp = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + *psizep = TARGET_PAGE_BITS; + return 0; + } + + /* + * Check UPRT (we avoid the check in real mode to deal with + * transitional states during kexec. + */ + if (guest_visible && !ppc64_use_proc_tbl(cpu)) { + qemu_log_mask(LOG_GUEST_ERROR, + "LPCR:UPRT not set in radix mode ! LPCR=" + TARGET_FMT_lx "\n", env->spr[SPR_LPCR]); + } /* Virtual Mode Access - get the fully qualified address */ if (!ppc_radix64_get_fully_qualified_addr(&cpu->env, eaddr, &lpid, &pid)) { @@ -559,43 +589,11 @@ int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, int mmu_idx) { CPUState *cs = CPU(cpu); - CPUPPCState *env = &cpu->env; int page_size, prot; - bool relocation; hwaddr raddr; - assert(!(msr_hv && cpu->vhyp)); - - relocation = (access_type == MMU_INST_FETCH ? msr_ir : msr_dr); - /* HV or virtual hypervisor Real Mode Access */ - if (!relocation && (msr_hv || cpu->vhyp)) { - /* In real mode top 4 effective addr bits (mostly) ignored */ - raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL; - - /* In HV mode, add HRMOR if top EA bit is clear */ - if (msr_hv || !env->has_hv_mode) { - if (!(eaddr >> 63)) { - raddr |= env->spr[SPR_HRMOR]; - } - } - tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, - PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx, - TARGET_PAGE_SIZE); - return 0; - } - - /* - * Check UPRT (we avoid the check in real mode to deal with - * transitional states during kexec. - */ - if (!ppc64_use_proc_tbl(cpu)) { - qemu_log_mask(LOG_GUEST_ERROR, - "LPCR:UPRT not set in radix mode ! LPCR=" - TARGET_FMT_lx "\n", env->spr[SPR_LPCR]); - } - /* Translate eaddr to raddr (where raddr is addr qemu needs for access) */ - if (ppc_radix64_xlate(cpu, eaddr, access_type, relocation, &raddr, + if (ppc_radix64_xlate(cpu, eaddr, access_type, &raddr, &page_size, &prot, true)) { return 1; } @@ -607,18 +605,11 @@ int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, hwaddr ppc_radix64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong eaddr) { - CPUPPCState *env = &cpu->env; int psize, prot; hwaddr raddr; - /* Handle Real Mode */ - if ((msr_dr == 0) && (msr_hv || cpu->vhyp)) { - /* In real mode top 4 effective addr bits (mostly) ignored */ - return eaddr & 0x0FFFFFFFFFFFFFFFULL; - } - - if (ppc_radix64_xlate(cpu, eaddr, 0, msr_dr, &raddr, &psize, - &prot, false)) { + if (ppc_radix64_xlate(cpu, eaddr, MMU_DATA_LOAD, &raddr, + &psize, &prot, false)) { return -1; } From 077a370499bb100237e291da9a06e6adbcd89335 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 21 Jun 2021 09:51:09 -0300 Subject: [PATCH 081/272] target/ppc: Use bool success for ppc_radix64_xlate Instead of returning non-zero for failure, return true for success. Signed-off-by: Richard Henderson Message-Id: <20210621125115.67717-5-bruno.larsen@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/mmu-radix64.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index dd5ae69052..2d5f0850c9 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -463,10 +463,10 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, * | = On | Process Scoped | Scoped | * +-------------+----------------+---------------+ */ -static int ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, - MMUAccessType access_type, - hwaddr *raddr, int *psizep, int *protp, - bool guest_visible) +static bool ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, + MMUAccessType access_type, + hwaddr *raddr, int *psizep, int *protp, + bool guest_visible) { CPUPPCState *env = &cpu->env; uint64_t lpid, pid; @@ -492,7 +492,7 @@ static int ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, } *protp = PAGE_READ | PAGE_WRITE | PAGE_EXEC; *psizep = TARGET_PAGE_BITS; - return 0; + return true; } /* @@ -510,7 +510,7 @@ static int ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, if (guest_visible) { ppc_radix64_raise_segi(cpu, access_type, eaddr); } - return 1; + return false; } /* Get Process Table */ @@ -523,13 +523,13 @@ static int ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, if (guest_visible) { ppc_radix64_raise_si(cpu, access_type, eaddr, DSISR_NOPTE); } - return 1; + return false; } if (!validate_pate(cpu, lpid, &pate)) { if (guest_visible) { ppc_radix64_raise_si(cpu, access_type, eaddr, DSISR_R_BADCONFIG); } - return 1; + return false; } } @@ -549,7 +549,7 @@ static int ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, pate, &g_raddr, &prot, &psize, guest_visible); if (ret) { - return ret; + return false; } *psizep = MIN(*psizep, psize); *protp &= prot; @@ -573,7 +573,7 @@ static int ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, &prot, &psize, false, guest_visible); if (ret) { - return ret; + return false; } *psizep = MIN(*psizep, psize); *protp &= prot; @@ -582,7 +582,7 @@ static int ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, } } - return 0; + return true; } int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, @@ -593,8 +593,8 @@ int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, hwaddr raddr; /* Translate eaddr to raddr (where raddr is addr qemu needs for access) */ - if (ppc_radix64_xlate(cpu, eaddr, access_type, &raddr, - &page_size, &prot, true)) { + if (!ppc_radix64_xlate(cpu, eaddr, access_type, &raddr, + &page_size, &prot, true)) { return 1; } @@ -608,8 +608,8 @@ hwaddr ppc_radix64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong eaddr) int psize, prot; hwaddr raddr; - if (ppc_radix64_xlate(cpu, eaddr, MMU_DATA_LOAD, &raddr, - &psize, &prot, false)) { + if (!ppc_radix64_xlate(cpu, eaddr, MMU_DATA_LOAD, &raddr, + &psize, &prot, false)) { return -1; } From 1a8c647bbd72f70c2cd8d369b3aa3e71a57ac3d8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 21 Jun 2021 09:51:10 -0300 Subject: [PATCH 082/272] target/ppc: Split out ppc_hash64_xlate Mirror the interface of ppc_radix64_xlate, putting all of the logic for hash64 translation into a single function. Signed-off-by: Richard Henderson Message-Id: <20210621125115.67717-6-bruno.larsen@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/mmu-hash64.c | 125 +++++++++++++++++++--------------------- 1 file changed, 59 insertions(+), 66 deletions(-) diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index 2febd369b1..c6b167b4dc 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -873,8 +873,10 @@ static int build_vrma_slbe(PowerPCCPU *cpu, ppc_slb_t *slb) return -1; } -int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, - MMUAccessType access_type, int mmu_idx) +static bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, + MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, + bool guest_visible) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; @@ -918,9 +920,11 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, slb = &vrma_slbe; if (build_vrma_slbe(cpu, slb) != 0) { /* Invalid VRMA setup, machine check */ - cs->exception_index = POWERPC_EXCP_MCHECK; - env->error_code = 0; - return 1; + if (guest_visible) { + cs->exception_index = POWERPC_EXCP_MCHECK; + env->error_code = 0; + } + return false; } goto skip_slb_search; @@ -929,6 +933,9 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, /* Emulated old-style RMO mode, bounds check against RMLS */ if (raddr >= limit) { + if (!guest_visible) { + return false; + } switch (access_type) { case MMU_INST_FETCH: ppc_hash64_set_isi(cs, SRR1_PROTFAULT); @@ -943,15 +950,16 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, default: g_assert_not_reached(); } - return 1; + return false; } raddr |= env->spr[SPR_RMOR]; } - tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, - PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx, - TARGET_PAGE_SIZE); - return 0; + + *raddrp = raddr; + *protp = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + *psizep = TARGET_PAGE_BITS; + return true; } /* 2. Translation is on, so look up the SLB */ @@ -964,6 +972,9 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, exit(1); } /* Segment still not found, generate the appropriate interrupt */ + if (!guest_visible) { + return false; + } switch (access_type) { case MMU_INST_FETCH: cs->exception_index = POWERPC_EXCP_ISEG; @@ -978,20 +989,25 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, default: g_assert_not_reached(); } - return 1; + return false; } -skip_slb_search: + skip_slb_search: /* 3. Check for segment level no-execute violation */ if (access_type == MMU_INST_FETCH && (slb->vsid & SLB_VSID_N)) { - ppc_hash64_set_isi(cs, SRR1_NOEXEC_GUARD); - return 1; + if (guest_visible) { + ppc_hash64_set_isi(cs, SRR1_NOEXEC_GUARD); + } + return false; } /* 4. Locate the PTE in the hash table */ ptex = ppc_hash64_htab_lookup(cpu, slb, eaddr, &pte, &apshift); if (ptex == -1) { + if (!guest_visible) { + return false; + } switch (access_type) { case MMU_INST_FETCH: ppc_hash64_set_isi(cs, SRR1_NOPTE); @@ -1005,7 +1021,7 @@ skip_slb_search: default: g_assert_not_reached(); } - return 1; + return false; } qemu_log_mask(CPU_LOG_MMU, "found PTE at index %08" HWADDR_PRIx "\n", ptex); @@ -1021,6 +1037,9 @@ skip_slb_search: if (need_prot & ~prot) { /* Access right violation */ qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); + if (!guest_visible) { + return false; + } if (access_type == MMU_INST_FETCH) { int srr1 = 0; if (PAGE_EXEC & ~exec_prot) { @@ -1045,7 +1064,7 @@ skip_slb_search: } ppc_hash64_set_dsi(cs, eaddr, dsisr); } - return 1; + return false; } qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); @@ -1069,66 +1088,40 @@ skip_slb_search: /* 7. Determine the real address from the PTE */ - raddr = deposit64(pte.pte1 & HPTE64_R_RPN, 0, apshift, eaddr); + *raddrp = deposit64(pte.pte1 & HPTE64_R_RPN, 0, apshift, eaddr); + *protp = prot; + *psizep = apshift; + return true; +} + +int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, + MMUAccessType access_type, int mmu_idx) +{ + CPUState *cs = CPU(cpu); + int page_size, prot; + hwaddr raddr; + + if (!ppc_hash64_xlate(cpu, eaddr, access_type, &raddr, + &page_size, &prot, true)) { + return 1; + } tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, - prot, mmu_idx, 1ULL << apshift); - + prot, mmu_idx, 1UL << page_size); return 0; } -hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr) +hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong eaddr) { - CPUPPCState *env = &cpu->env; - ppc_slb_t vrma_slbe; - ppc_slb_t *slb; - hwaddr ptex, raddr; - ppc_hash_pte64_t pte; - unsigned apshift; + int psize, prot; + hwaddr raddr; - /* Handle real mode */ - if (msr_dr == 0) { - /* In real mode the top 4 effective address bits are ignored */ - raddr = addr & 0x0FFFFFFFFFFFFFFFULL; - - if (cpu->vhyp) { - /* - * In virtual hypervisor mode, there's nothing to do: - * EA == GPA == qemu guest address - */ - return raddr; - } else if ((msr_hv || !env->has_hv_mode) && !(addr >> 63)) { - /* In HV mode, add HRMOR if top EA bit is clear */ - return raddr | env->spr[SPR_HRMOR]; - } else if (ppc_hash64_use_vrma(env)) { - /* Emulated VRMA mode */ - slb = &vrma_slbe; - if (build_vrma_slbe(cpu, slb) != 0) { - return -1; - } - } else { - target_ulong limit = rmls_limit(cpu); - - /* Emulated old-style RMO mode, bounds check against RMLS */ - if (raddr >= limit) { - return -1; - } - return raddr | env->spr[SPR_RMOR]; - } - } else { - slb = slb_lookup(cpu, addr); - if (!slb) { - return -1; - } - } - - ptex = ppc_hash64_htab_lookup(cpu, slb, addr, &pte, &apshift); - if (ptex == -1) { + if (!ppc_hash64_xlate(cpu, eaddr, MMU_DATA_LOAD, &raddr, + &psize, &prot, false)) { return -1; } - return deposit64(pte.pte1 & HPTE64_R_RPN, 0, apshift, addr) - & TARGET_PAGE_MASK; + return raddr & TARGET_PAGE_MASK; } void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu, target_ulong ptex, From 6c3c873c63830eb89a5776486af0f32858f62938 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 21 Jun 2021 09:51:11 -0300 Subject: [PATCH 083/272] target/ppc: Split out ppc_hash32_xlate Mirror the interface of ppc_radix64_xlate, putting all of the logic for hash32 translation into a single entry point. Signed-off-by: Richard Henderson Message-Id: <20210621125115.67717-7-bruno.larsen@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/mmu-hash32.c | 224 ++++++++++++++++++++-------------------- 1 file changed, 113 insertions(+), 111 deletions(-) diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index 8f19b43e47..ad22372c07 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -218,10 +218,11 @@ static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea, return -1; } -static int ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, - target_ulong eaddr, - MMUAccessType access_type, - hwaddr *raddr, int *prot) +static bool ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, + target_ulong eaddr, + MMUAccessType access_type, + hwaddr *raddr, int *prot, + bool guest_visible) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; @@ -238,17 +239,23 @@ static int ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, */ *raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF); *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - return 0; + return true; } if (access_type == MMU_INST_FETCH) { /* No code fetch is allowed in direct-store areas */ - cs->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0x10000000; - return 1; + if (guest_visible) { + cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x10000000; + } + return false; } - switch (env->access_type) { + /* + * From ppc_cpu_get_phys_page_debug, env->access_type is not set. + * Assume ACCESS_INT for that case. + */ + switch (guest_visible ? env->access_type : ACCESS_INT) { case ACCESS_INT: /* Integer load/store : only access allowed */ break; @@ -257,7 +264,7 @@ static int ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, cs->exception_index = POWERPC_EXCP_ALIGN; env->error_code = POWERPC_EXCP_ALIGN_FP; env->spr[SPR_DAR] = eaddr; - return 1; + return false; case ACCESS_RES: /* lwarx, ldarx or srwcx. */ env->error_code = 0; @@ -267,7 +274,7 @@ static int ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, } else { env->spr[SPR_DSISR] = 0x04000000; } - return 1; + return false; case ACCESS_CACHE: /* * dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi @@ -276,7 +283,7 @@ static int ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, * no-op, it's quite easy :-) */ *raddr = eaddr; - return 0; + return true; case ACCESS_EXT: /* eciwx or ecowx */ cs->exception_index = POWERPC_EXCP_DSI; @@ -287,16 +294,18 @@ static int ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, } else { env->spr[SPR_DSISR] = 0x04100000; } - return 1; + return false; default: - cpu_abort(cs, "ERROR: instruction should not need " - "address translation\n"); + cpu_abort(cs, "ERROR: insn should not need address translation\n"); } - if ((access_type == MMU_DATA_STORE || key != 1) && - (access_type == MMU_DATA_LOAD || key != 0)) { + + *prot = key ? PAGE_READ | PAGE_WRITE : PAGE_READ; + if (*prot & prot_for_access_type(access_type)) { *raddr = eaddr; - return 0; - } else { + return true; + } + + if (guest_visible) { cs->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; env->spr[SPR_DAR] = eaddr; @@ -305,8 +314,8 @@ static int ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, } else { env->spr[SPR_DSISR] = 0x08000000; } - return 1; } + return false; } hwaddr get_pteg_offset32(PowerPCCPU *cpu, hwaddr hash) @@ -415,8 +424,10 @@ static hwaddr ppc_hash32_pte_raddr(target_ulong sr, ppc_hash_pte32_t pte, return (rpn & ~mask) | (eaddr & mask); } -int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, - MMUAccessType access_type, int mmu_idx) +static bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, + MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, + bool guest_visible) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; @@ -427,43 +438,43 @@ int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int need_prot; hwaddr raddr; - need_prot = prot_for_access_type(access_type); + /* There are no hash32 large pages. */ + *psizep = TARGET_PAGE_BITS; /* 1. Handle real mode accesses */ if (access_type == MMU_INST_FETCH ? !msr_ir : !msr_dr) { /* Translation is off */ - raddr = eaddr; - tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, - PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx, - TARGET_PAGE_SIZE); - return 0; + *raddrp = eaddr; + *protp = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return true; } + need_prot = prot_for_access_type(access_type); + /* 2. Check Block Address Translation entries (BATs) */ if (env->nb_BATs != 0) { - raddr = ppc_hash32_bat_lookup(cpu, eaddr, access_type, &prot); + raddr = ppc_hash32_bat_lookup(cpu, eaddr, access_type, protp); if (raddr != -1) { - if (need_prot & ~prot) { - if (access_type == MMU_INST_FETCH) { - cs->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0x08000000; - } else { - cs->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - env->spr[SPR_DAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_DSISR] = 0x0a000000; + if (need_prot & ~*protp) { + if (guest_visible) { + if (access_type == MMU_INST_FETCH) { + cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x08000000; } else { - env->spr[SPR_DSISR] = 0x08000000; + cs->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_DSISR] = 0x0a000000; + } else { + env->spr[SPR_DSISR] = 0x08000000; + } } } - return 1; + return false; } - - tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, - raddr & TARGET_PAGE_MASK, prot, mmu_idx, - TARGET_PAGE_SIZE); - return 0; + *raddrp = raddr; + return true; } } @@ -472,42 +483,38 @@ int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, /* 4. Handle direct store segments */ if (sr & SR32_T) { - if (ppc_hash32_direct_store(cpu, sr, eaddr, access_type, - &raddr, &prot) == 0) { - tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, - raddr & TARGET_PAGE_MASK, prot, mmu_idx, - TARGET_PAGE_SIZE); - return 0; - } else { - return 1; - } + return ppc_hash32_direct_store(cpu, sr, eaddr, access_type, + raddrp, protp, guest_visible); } /* 5. Check for segment level no-execute violation */ if (access_type == MMU_INST_FETCH && (sr & SR32_NX)) { - cs->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0x10000000; - return 1; + if (guest_visible) { + cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x10000000; + } + return false; } /* 6. Locate the PTE in the hash table */ pte_offset = ppc_hash32_htab_lookup(cpu, sr, eaddr, &pte); if (pte_offset == -1) { - if (access_type == MMU_INST_FETCH) { - cs->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0x40000000; - } else { - cs->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - env->spr[SPR_DAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_DSISR] = 0x42000000; + if (guest_visible) { + if (access_type == MMU_INST_FETCH) { + cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x40000000; } else { - env->spr[SPR_DSISR] = 0x40000000; + cs->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_DSISR] = 0x42000000; + } else { + env->spr[SPR_DSISR] = 0x40000000; + } } } - - return 1; + return false; } qemu_log_mask(CPU_LOG_MMU, "found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); @@ -519,20 +526,22 @@ int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, if (need_prot & ~prot) { /* Access right violation */ qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); - if (access_type == MMU_INST_FETCH) { - cs->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0x08000000; - } else { - cs->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - env->spr[SPR_DAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_DSISR] = 0x0a000000; + if (guest_visible) { + if (access_type == MMU_INST_FETCH) { + cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x08000000; } else { - env->spr[SPR_DSISR] = 0x08000000; + cs->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_DSISR] = 0x0a000000; + } else { + env->spr[SPR_DSISR] = 0x08000000; + } } } - return 1; + return false; } qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); @@ -556,45 +565,38 @@ int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, /* 9. Determine the real address from the PTE */ - raddr = ppc_hash32_pte_raddr(sr, pte, eaddr); + *raddrp = ppc_hash32_pte_raddr(sr, pte, eaddr); + *protp = prot; + return true; +} + +int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, + MMUAccessType access_type, int mmu_idx) +{ + CPUState *cs = CPU(cpu); + int page_size, prot; + hwaddr raddr; + + /* Translate eaddr to raddr (where raddr is addr qemu needs for access) */ + if (!ppc_hash32_xlate(cpu, eaddr, access_type, &raddr, + &page_size, &prot, true)) { + return 1; + } tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, - prot, mmu_idx, TARGET_PAGE_SIZE); - + prot, mmu_idx, 1UL << page_size); return 0; } hwaddr ppc_hash32_get_phys_page_debug(PowerPCCPU *cpu, target_ulong eaddr) { - CPUPPCState *env = &cpu->env; - target_ulong sr; - hwaddr pte_offset; - ppc_hash_pte32_t pte; - int prot; + int psize, prot; + hwaddr raddr; - if (msr_dr == 0) { - /* Translation is off */ - return eaddr; - } - - if (env->nb_BATs != 0) { - hwaddr raddr = ppc_hash32_bat_lookup(cpu, eaddr, 0, &prot); - if (raddr != -1) { - return raddr; - } - } - - sr = env->sr[eaddr >> 28]; - - if (sr & SR32_T) { - /* FIXME: Add suitable debug support for Direct Store segments */ + if (!ppc_hash32_xlate(cpu, eaddr, MMU_DATA_LOAD, &raddr, + &psize, &prot, false)) { return -1; } - pte_offset = ppc_hash32_htab_lookup(cpu, sr, eaddr, &pte); - if (pte_offset == -1) { - return -1; - } - - return ppc_hash32_pte_raddr(sr, pte, eaddr) & TARGET_PAGE_MASK; + return raddr & TARGET_PAGE_MASK; } From af44a1423691b6c93327fccfef20a5c5cbf8e517 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 21 Jun 2021 09:51:12 -0300 Subject: [PATCH 084/272] target/ppc: Split out ppc_jumbo_xlate Mirror the interface of ppc_radix64_xlate (mostly), putting all of the logic for older mmu translation into a single entry point. For booke, we need to add mmu_idx to the xlate-style interface. Signed-off-by: Richard Henderson Message-Id: <20210621125115.67717-8-bruno.larsen@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/mmu_helper.c | 181 +++++++++++++++++++++------------------- 1 file changed, 97 insertions(+), 84 deletions(-) diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index c4b1c93e47..2e92deb105 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -1435,48 +1435,6 @@ static int get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, } #endif -hwaddr ppc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) -{ - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; - mmu_ctx_t ctx; - - switch (env->mmu_model) { -#if defined(TARGET_PPC64) - case POWERPC_MMU_64B: - case POWERPC_MMU_2_03: - case POWERPC_MMU_2_06: - case POWERPC_MMU_2_07: - return ppc_hash64_get_phys_page_debug(cpu, addr); - case POWERPC_MMU_3_00: - return ppc64_v3_get_phys_page_debug(cpu, addr); -#endif - - case POWERPC_MMU_32B: - case POWERPC_MMU_601: - return ppc_hash32_get_phys_page_debug(cpu, addr); - - default: - ; - } - - if (unlikely(get_physical_address(env, &ctx, addr, MMU_DATA_LOAD, - ACCESS_INT) != 0)) { - - /* - * Some MMUs have separate TLBs for code and data. If we only - * try an ACCESS_INT, we may not be able to read instructions - * mapped by code TLBs, so we also try a ACCESS_CODE. - */ - if (unlikely(get_physical_address(env, &ctx, addr, MMU_INST_FETCH, - ACCESS_CODE) != 0)) { - return -1; - } - } - - return ctx.raddr & TARGET_PAGE_MASK; -} - static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, MMUAccessType access_type, int mmu_idx) { @@ -1532,30 +1490,38 @@ static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, } /* Perform address translation */ -static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, - MMUAccessType access_type, int mmu_idx) +/* TODO: Split this by mmu_model. */ +static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, + MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, + int mmu_idx, bool guest_visible) { - CPUState *cs = env_cpu(env); - PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; mmu_ctx_t ctx; int type; - int ret = 0; + int ret; if (access_type == MMU_INST_FETCH) { /* code access */ type = ACCESS_CODE; - } else { + } else if (guest_visible) { /* data access */ type = env->access_type; + } else { + type = ACCESS_INT; } - ret = get_physical_address_wtlb(env, &ctx, address, access_type, + + ret = get_physical_address_wtlb(env, &ctx, eaddr, access_type, type, mmu_idx); if (ret == 0) { - tlb_set_page(cs, address & TARGET_PAGE_MASK, - ctx.raddr & TARGET_PAGE_MASK, ctx.prot, - mmu_idx, TARGET_PAGE_SIZE); - ret = 0; - } else if (ret < 0) { + *raddrp = ctx.raddr; + *protp = ctx.prot; + *psizep = TARGET_PAGE_BITS; + return true; + } + + if (guest_visible) { LOG_MMU_STATE(cs); if (type == ACCESS_CODE) { switch (ret) { @@ -1565,7 +1531,7 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, case POWERPC_MMU_SOFT_6xx: cs->exception_index = POWERPC_EXCP_IFTLB; env->error_code = 1 << 18; - env->spr[SPR_IMISS] = address; + env->spr[SPR_IMISS] = eaddr; env->spr[SPR_ICMP] = 0x80000000 | ctx.ptem; goto tlb_miss; case POWERPC_MMU_SOFT_74xx: @@ -1575,29 +1541,25 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, case POWERPC_MMU_SOFT_4xx_Z: cs->exception_index = POWERPC_EXCP_ITLB; env->error_code = 0; - env->spr[SPR_40x_DEAR] = address; + env->spr[SPR_40x_DEAR] = eaddr; env->spr[SPR_40x_ESR] = 0x00000000; break; case POWERPC_MMU_BOOKE206: - booke206_update_mas_tlb_miss(env, address, 2, mmu_idx); + booke206_update_mas_tlb_miss(env, eaddr, 2, mmu_idx); /* fall through */ case POWERPC_MMU_BOOKE: cs->exception_index = POWERPC_EXCP_ITLB; env->error_code = 0; - env->spr[SPR_BOOKE_DEAR] = address; + env->spr[SPR_BOOKE_DEAR] = eaddr; env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, MMU_DATA_LOAD); - return -1; - case POWERPC_MMU_MPC8xx: - /* XXX: TODO */ - cpu_abort(cs, "MPC8xx MMU model is not implemented\n"); break; + case POWERPC_MMU_MPC8xx: + cpu_abort(cs, "MPC8xx MMU model is not implemented\n"); case POWERPC_MMU_REAL: cpu_abort(cs, "PowerPC in real mode should never raise " "any MMU exceptions\n"); - return -1; default: cpu_abort(cs, "Unknown or invalid MMU model\n"); - return -1; } break; case -2: @@ -1634,7 +1596,7 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, cs->exception_index = POWERPC_EXCP_DLTLB; env->error_code = 0; } - env->spr[SPR_DMISS] = address; + env->spr[SPR_DMISS] = eaddr; env->spr[SPR_DCMP] = 0x80000000 | ctx.ptem; tlb_miss: env->error_code |= ctx.key << 19; @@ -1652,7 +1614,7 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, tlb_miss_74xx: /* Implement LRU algorithm */ env->error_code = ctx.key << 19; - env->spr[SPR_TLBMISS] = (address & ~((target_ulong)0x3)) | + env->spr[SPR_TLBMISS] = (eaddr & ~((target_ulong)0x3)) | ((env->last_way + 1) & (env->nb_ways - 1)); env->spr[SPR_PTEHI] = 0x80000000 | ctx.ptem; break; @@ -1660,7 +1622,7 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, case POWERPC_MMU_SOFT_4xx_Z: cs->exception_index = POWERPC_EXCP_DTLB; env->error_code = 0; - env->spr[SPR_40x_DEAR] = address; + env->spr[SPR_40x_DEAR] = eaddr; if (access_type == MMU_DATA_STORE) { env->spr[SPR_40x_ESR] = 0x00800000; } else { @@ -1670,23 +1632,20 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, case POWERPC_MMU_MPC8xx: /* XXX: TODO */ cpu_abort(cs, "MPC8xx MMU model is not implemented\n"); - break; case POWERPC_MMU_BOOKE206: - booke206_update_mas_tlb_miss(env, address, access_type, mmu_idx); + booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); /* fall through */ case POWERPC_MMU_BOOKE: cs->exception_index = POWERPC_EXCP_DTLB; env->error_code = 0; - env->spr[SPR_BOOKE_DEAR] = address; + env->spr[SPR_BOOKE_DEAR] = eaddr; env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); - return -1; + break; case POWERPC_MMU_REAL: cpu_abort(cs, "PowerPC in real mode should never raise " "any MMU exceptions\n"); - return -1; default: cpu_abort(cs, "Unknown or invalid MMU model\n"); - return -1; } break; case -2: @@ -1695,16 +1654,16 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, env->error_code = 0; if (env->mmu_model == POWERPC_MMU_SOFT_4xx || env->mmu_model == POWERPC_MMU_SOFT_4xx_Z) { - env->spr[SPR_40x_DEAR] = address; + env->spr[SPR_40x_DEAR] = eaddr; if (access_type == MMU_DATA_STORE) { env->spr[SPR_40x_ESR] |= 0x00800000; } } else if ((env->mmu_model == POWERPC_MMU_BOOKE) || (env->mmu_model == POWERPC_MMU_BOOKE206)) { - env->spr[SPR_BOOKE_DEAR] = address; + env->spr[SPR_BOOKE_DEAR] = eaddr; env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); } else { - env->spr[SPR_DAR] = address; + env->spr[SPR_DAR] = eaddr; if (access_type == MMU_DATA_STORE) { env->spr[SPR_DSISR] = 0x0A000000; } else { @@ -1719,13 +1678,13 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, /* Floating point load/store */ cs->exception_index = POWERPC_EXCP_ALIGN; env->error_code = POWERPC_EXCP_ALIGN_FP; - env->spr[SPR_DAR] = address; + env->spr[SPR_DAR] = eaddr; break; case ACCESS_RES: /* lwarx, ldarx or stwcx. */ cs->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; - env->spr[SPR_DAR] = address; + env->spr[SPR_DAR] = eaddr; if (access_type == MMU_DATA_STORE) { env->spr[SPR_DSISR] = 0x06000000; } else { @@ -1736,7 +1695,7 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, /* eciwx or ecowx */ cs->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; - env->spr[SPR_DAR] = address; + env->spr[SPR_DAR] = eaddr; if (access_type == MMU_DATA_STORE) { env->spr[SPR_DSISR] = 0x06100000; } else { @@ -1748,16 +1707,14 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, cs->exception_index = POWERPC_EXCP_PROGRAM; env->error_code = POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL; - env->spr[SPR_DAR] = address; + env->spr[SPR_DAR] = eaddr; break; } break; } } - ret = 1; } - - return ret; + return false; } #ifdef CONFIG_TCG @@ -2942,6 +2899,62 @@ void helper_check_tlb_flush_global(CPUPPCState *env) /*****************************************************************************/ +static int cpu_ppc_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, + MMUAccessType access_type, int mmu_idx) +{ + CPUState *cs = CPU(cpu); + int page_size, prot; + hwaddr raddr; + + if (!ppc_jumbo_xlate(cpu, eaddr, access_type, &raddr, + &page_size, &prot, mmu_idx, true)) { + return 1; + } + + tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, + prot, mmu_idx, 1UL << page_size); + return 0; +} + +hwaddr ppc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + hwaddr raddr; + int s, p; + + switch (env->mmu_model) { +#if defined(TARGET_PPC64) + case POWERPC_MMU_64B: + case POWERPC_MMU_2_03: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_07: + return ppc_hash64_get_phys_page_debug(cpu, addr); + case POWERPC_MMU_3_00: + return ppc64_v3_get_phys_page_debug(cpu, addr); +#endif + + case POWERPC_MMU_32B: + case POWERPC_MMU_601: + return ppc_hash32_get_phys_page_debug(cpu, addr); + + default: + ; + } + + /* + * Some MMUs have separate TLBs for code and data. If we only + * try an MMU_DATA_LOAD, we may not be able to read instructions + * mapped by code TLBs, so we also try a MMU_INST_FETCH. + */ + if (ppc_jumbo_xlate(cpu, addr, MMU_DATA_LOAD, &raddr, &s, &p, 0, false) || + ppc_jumbo_xlate(cpu, addr, MMU_INST_FETCH, &raddr, &s, &p, 0, false)) { + return raddr & TARGET_PAGE_MASK; + } + return -1; +} + + bool ppc_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) @@ -2969,7 +2982,7 @@ bool ppc_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, break; default: - ret = cpu_ppc_handle_mmu_fault(env, addr, access_type, mmu_idx); + ret = cpu_ppc_handle_mmu_fault(cpu, addr, access_type, mmu_idx); break; } if (unlikely(ret != 0)) { From 51806b545834e0902dd2d17d1f66c7a2d83422f3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 21 Jun 2021 09:51:13 -0300 Subject: [PATCH 085/272] target/ppc: Introduce ppc_xlate Create one common dispatch for all of the ppc_*_xlate functions. Use ppc64_v3_radix to directly dispatch between ppc_radix64_xlate and ppc_hash64_xlate. Remove the separate *_handle_mmu_fault and *_get_phys_page_debug functions, using common code for ppc_cpu_tlb_fill and ppc_cpu_get_phys_page_debug. Signed-off-by: Richard Henderson Message-Id: <20210621125115.67717-9-bruno.larsen@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/mmu-book3s-v3.c | 19 ------- target/ppc/mmu-book3s-v3.h | 5 -- target/ppc/mmu-hash32.c | 38 ++----------- target/ppc/mmu-hash32.h | 6 +-- target/ppc/mmu-hash64.c | 37 ++----------- target/ppc/mmu-hash64.h | 6 +-- target/ppc/mmu-radix64.c | 38 ++----------- target/ppc/mmu-radix64.h | 6 +-- target/ppc/mmu_helper.c | 106 ++++++++++++++----------------------- 9 files changed, 58 insertions(+), 203 deletions(-) diff --git a/target/ppc/mmu-book3s-v3.c b/target/ppc/mmu-book3s-v3.c index c78fd8dc0e..f4985bae78 100644 --- a/target/ppc/mmu-book3s-v3.c +++ b/target/ppc/mmu-book3s-v3.c @@ -23,25 +23,6 @@ #include "mmu-book3s-v3.h" #include "mmu-radix64.h" -int ppc64_v3_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx, - int mmu_idx) -{ - if (ppc64_v3_radix(cpu)) { /* Guest uses radix */ - return ppc_radix64_handle_mmu_fault(cpu, eaddr, rwx, mmu_idx); - } else { /* Guest uses hash */ - return ppc_hash64_handle_mmu_fault(cpu, eaddr, rwx, mmu_idx); - } -} - -hwaddr ppc64_v3_get_phys_page_debug(PowerPCCPU *cpu, vaddr eaddr) -{ - if (ppc64_v3_radix(cpu)) { - return ppc_radix64_get_phys_page_debug(cpu, eaddr); - } else { - return ppc_hash64_get_phys_page_debug(cpu, eaddr); - } -} - bool ppc64_v3_get_pate(PowerPCCPU *cpu, target_ulong lpid, ppc_v3_pate_t *entry) { uint64_t patb = cpu->env.spr[SPR_PTCR] & PTCR_PATB; diff --git a/target/ppc/mmu-book3s-v3.h b/target/ppc/mmu-book3s-v3.h index 7b89be54b8..a1326df969 100644 --- a/target/ppc/mmu-book3s-v3.h +++ b/target/ppc/mmu-book3s-v3.h @@ -67,11 +67,6 @@ static inline bool ppc64_v3_radix(PowerPCCPU *cpu) return !!(cpu->env.spr[SPR_LPCR] & LPCR_HR); } -hwaddr ppc64_v3_get_phys_page_debug(PowerPCCPU *cpu, vaddr eaddr); - -int ppc64_v3_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx, - int mmu_idx); - static inline hwaddr ppc_hash64_hpt_base(PowerPCCPU *cpu) { uint64_t base; diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index ad22372c07..6a07c345e4 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -424,10 +424,9 @@ static hwaddr ppc_hash32_pte_raddr(target_ulong sr, ppc_hash_pte32_t pte, return (rpn & ~mask) | (eaddr & mask); } -static bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, - MMUAccessType access_type, - hwaddr *raddrp, int *psizep, int *protp, - bool guest_visible) +bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, + bool guest_visible) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; @@ -569,34 +568,3 @@ static bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, *protp = prot; return true; } - -int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, - MMUAccessType access_type, int mmu_idx) -{ - CPUState *cs = CPU(cpu); - int page_size, prot; - hwaddr raddr; - - /* Translate eaddr to raddr (where raddr is addr qemu needs for access) */ - if (!ppc_hash32_xlate(cpu, eaddr, access_type, &raddr, - &page_size, &prot, true)) { - return 1; - } - - tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, - prot, mmu_idx, 1UL << page_size); - return 0; -} - -hwaddr ppc_hash32_get_phys_page_debug(PowerPCCPU *cpu, target_ulong eaddr) -{ - int psize, prot; - hwaddr raddr; - - if (!ppc_hash32_xlate(cpu, eaddr, MMU_DATA_LOAD, &raddr, - &psize, &prot, false)) { - return -1; - } - - return raddr & TARGET_PAGE_MASK; -} diff --git a/target/ppc/mmu-hash32.h b/target/ppc/mmu-hash32.h index 30e35718a7..8694eccabd 100644 --- a/target/ppc/mmu-hash32.h +++ b/target/ppc/mmu-hash32.h @@ -4,9 +4,9 @@ #ifndef CONFIG_USER_ONLY hwaddr get_pteg_offset32(PowerPCCPU *cpu, hwaddr hash); -hwaddr ppc_hash32_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr); -int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, vaddr address, - MMUAccessType access_type, int mmu_idx); +bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, + bool guest_visible); /* * Segment register definitions diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index c6b167b4dc..c1b98a97e9 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -873,10 +873,9 @@ static int build_vrma_slbe(PowerPCCPU *cpu, ppc_slb_t *slb) return -1; } -static bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, - MMUAccessType access_type, - hwaddr *raddrp, int *psizep, int *protp, - bool guest_visible) +bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, + bool guest_visible) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; @@ -1094,36 +1093,6 @@ static bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, return true; } -int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, - MMUAccessType access_type, int mmu_idx) -{ - CPUState *cs = CPU(cpu); - int page_size, prot; - hwaddr raddr; - - if (!ppc_hash64_xlate(cpu, eaddr, access_type, &raddr, - &page_size, &prot, true)) { - return 1; - } - - tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, - prot, mmu_idx, 1UL << page_size); - return 0; -} - -hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong eaddr) -{ - int psize, prot; - hwaddr raddr; - - if (!ppc_hash64_xlate(cpu, eaddr, MMU_DATA_LOAD, &raddr, - &psize, &prot, false)) { - return -1; - } - - return raddr & TARGET_PAGE_MASK; -} - void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu, target_ulong ptex, target_ulong pte0, target_ulong pte1) { diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h index 3e8a8eec1f..9f338e1fe9 100644 --- a/target/ppc/mmu-hash64.h +++ b/target/ppc/mmu-hash64.h @@ -7,9 +7,9 @@ void dump_slb(PowerPCCPU *cpu); int ppc_store_slb(PowerPCCPU *cpu, target_ulong slot, target_ulong esid, target_ulong vsid); -hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr); -int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr address, - MMUAccessType access_type, int mmu_idx); +bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, + bool guest_visible); void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu, target_ulong pte_index, target_ulong pte0, target_ulong pte1); diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index 2d5f0850c9..cbd404bfa4 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -463,10 +463,9 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, * | = On | Process Scoped | Scoped | * +-------------+----------------+---------------+ */ -static bool ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, - MMUAccessType access_type, - hwaddr *raddr, int *psizep, int *protp, - bool guest_visible) +bool ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, + hwaddr *raddr, int *psizep, int *protp, + bool guest_visible) { CPUPPCState *env = &cpu->env; uint64_t lpid, pid; @@ -584,34 +583,3 @@ static bool ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, return true; } - -int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, - MMUAccessType access_type, int mmu_idx) -{ - CPUState *cs = CPU(cpu); - int page_size, prot; - hwaddr raddr; - - /* Translate eaddr to raddr (where raddr is addr qemu needs for access) */ - if (!ppc_radix64_xlate(cpu, eaddr, access_type, &raddr, - &page_size, &prot, true)) { - return 1; - } - - tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, - prot, mmu_idx, 1UL << page_size); - return 0; -} - -hwaddr ppc_radix64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong eaddr) -{ - int psize, prot; - hwaddr raddr; - - if (!ppc_radix64_xlate(cpu, eaddr, MMU_DATA_LOAD, &raddr, - &psize, &prot, false)) { - return -1; - } - - return raddr & TARGET_PAGE_MASK; -} diff --git a/target/ppc/mmu-radix64.h b/target/ppc/mmu-radix64.h index 94bd72cb38..6b13b89b64 100644 --- a/target/ppc/mmu-radix64.h +++ b/target/ppc/mmu-radix64.h @@ -44,9 +44,9 @@ #ifdef TARGET_PPC64 -int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, - MMUAccessType access_type, int mmu_idx); -hwaddr ppc_radix64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr); +bool ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, + hwaddr *raddr, int *psizep, int *protp, + bool guest_visible); static inline int ppc_radix64_get_prot_eaa(uint64_t pte) { diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index 2e92deb105..a0e4e027d3 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -2899,98 +2899,72 @@ void helper_check_tlb_flush_global(CPUPPCState *env) /*****************************************************************************/ -static int cpu_ppc_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, - MMUAccessType access_type, int mmu_idx) +static bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, + int mmu_idx, bool guest_visible) { - CPUState *cs = CPU(cpu); - int page_size, prot; - hwaddr raddr; + switch (cpu->env.mmu_model) { +#if defined(TARGET_PPC64) + case POWERPC_MMU_3_00: + if (ppc64_v3_radix(cpu)) { + return ppc_radix64_xlate(cpu, eaddr, access_type, + raddrp, psizep, protp, guest_visible); + } + /* fall through */ + case POWERPC_MMU_64B: + case POWERPC_MMU_2_03: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_07: + return ppc_hash64_xlate(cpu, eaddr, access_type, + raddrp, psizep, protp, guest_visible); +#endif - if (!ppc_jumbo_xlate(cpu, eaddr, access_type, &raddr, - &page_size, &prot, mmu_idx, true)) { - return 1; + case POWERPC_MMU_32B: + case POWERPC_MMU_601: + return ppc_hash32_xlate(cpu, eaddr, access_type, + raddrp, psizep, protp, guest_visible); + + default: + return ppc_jumbo_xlate(cpu, eaddr, access_type, raddrp, + psizep, protp, mmu_idx, guest_visible); } - - tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, - prot, mmu_idx, 1UL << page_size); - return 0; } hwaddr ppc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; hwaddr raddr; int s, p; - switch (env->mmu_model) { -#if defined(TARGET_PPC64) - case POWERPC_MMU_64B: - case POWERPC_MMU_2_03: - case POWERPC_MMU_2_06: - case POWERPC_MMU_2_07: - return ppc_hash64_get_phys_page_debug(cpu, addr); - case POWERPC_MMU_3_00: - return ppc64_v3_get_phys_page_debug(cpu, addr); -#endif - - case POWERPC_MMU_32B: - case POWERPC_MMU_601: - return ppc_hash32_get_phys_page_debug(cpu, addr); - - default: - ; - } - /* * Some MMUs have separate TLBs for code and data. If we only * try an MMU_DATA_LOAD, we may not be able to read instructions * mapped by code TLBs, so we also try a MMU_INST_FETCH. */ - if (ppc_jumbo_xlate(cpu, addr, MMU_DATA_LOAD, &raddr, &s, &p, 0, false) || - ppc_jumbo_xlate(cpu, addr, MMU_INST_FETCH, &raddr, &s, &p, 0, false)) { + if (ppc_xlate(cpu, addr, MMU_DATA_LOAD, &raddr, &s, &p, 0, false) || + ppc_xlate(cpu, addr, MMU_INST_FETCH, &raddr, &s, &p, 0, false)) { return raddr & TARGET_PAGE_MASK; } return -1; } - -bool ppc_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, +bool ppc_cpu_tlb_fill(CPUState *cs, vaddr eaddr, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) { PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; - int ret; + hwaddr raddr; + int page_size, prot; - switch (env->mmu_model) { -#if defined(TARGET_PPC64) - case POWERPC_MMU_64B: - case POWERPC_MMU_2_03: - case POWERPC_MMU_2_06: - case POWERPC_MMU_2_07: - ret = ppc_hash64_handle_mmu_fault(cpu, addr, access_type, mmu_idx); - break; - case POWERPC_MMU_3_00: - ret = ppc64_v3_handle_mmu_fault(cpu, addr, access_type, mmu_idx); - break; -#endif - - case POWERPC_MMU_32B: - case POWERPC_MMU_601: - ret = ppc_hash32_handle_mmu_fault(cpu, addr, access_type, mmu_idx); - break; - - default: - ret = cpu_ppc_handle_mmu_fault(cpu, addr, access_type, mmu_idx); - break; + if (ppc_xlate(cpu, eaddr, access_type, &raddr, + &page_size, &prot, mmu_idx, !probe)) { + tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, + prot, mmu_idx, 1UL << page_size); + return true; } - if (unlikely(ret != 0)) { - if (probe) { - return false; - } - raise_exception_err_ra(env, cs->exception_index, env->error_code, - retaddr); + if (probe) { + return false; } - return true; + raise_exception_err_ra(&cpu->env, cs->exception_index, + cpu->env.error_code, retaddr); } From cbf35bac39265f278863f9452ceb9ad69cc311ef Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 21 Jun 2021 09:51:14 -0300 Subject: [PATCH 086/272] target/ppc: Restrict ppc_cpu_tlb_fill to TCG This function is used by TCGCPUOps, and is thus TCG specific. Signed-off-by: Richard Henderson Message-Id: <20210621125115.67717-10-bruno.larsen@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/mmu_helper.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index a0e4e027d3..ba1952c77d 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -2948,6 +2948,7 @@ hwaddr ppc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) return -1; } +#ifdef CONFIG_TCG bool ppc_cpu_tlb_fill(CPUState *cs, vaddr eaddr, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) @@ -2968,3 +2969,4 @@ bool ppc_cpu_tlb_fill(CPUState *cs, vaddr eaddr, int size, raise_exception_err_ra(&cpu->env, cs->exception_index, cpu->env.error_code, retaddr); } +#endif From 26ba91db6c0fea5ff6a696e32fc532af32f6629b Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 2 Jul 2021 18:52:33 -0300 Subject: [PATCH 087/272] target/ppc: Fix compilation with DUMP_PAGE_TABLES debug option ../target/ppc/mmu_helper.c: In function 'get_segment_6xx_tlb': ../target/ppc/mmu_helper.c:514:46: error: passing argument 1 of 'ppc_hash32_hpt_mask' from incompatible pointer type [-Werror=incompatible-pointer-types] 514 | ppc_hash32_hpt_mask(env) + 0x80); | ^~~ | | | CPUPPCState * Fixes: 36778660d7 ("target/ppc: Eliminate htab_base and htab_mask variables") Signed-off-by: Fabiano Rosas Message-Id: <20210702215235.1941771-2-farosas@linux.ibm.com> Signed-off-by: David Gibson --- target/ppc/mmu_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index ba1952c77d..4c534b534b 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -511,7 +511,7 @@ static int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, qemu_log("Page table: " TARGET_FMT_plx " len " TARGET_FMT_plx "\n", ppc_hash32_hpt_base(cpu), - ppc_hash32_hpt_mask(env) + 0x80); + ppc_hash32_hpt_mask(cpu) + 0x80); for (curaddr = ppc_hash32_hpt_base(cpu); curaddr < (ppc_hash32_hpt_base(cpu) + ppc_hash32_hpt_mask(cpu) + 0x80); From d3841fce0d5cf474a5f03eec07226bb300d75a9b Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 2 Jul 2021 18:52:34 -0300 Subject: [PATCH 088/272] target/ppc: Fix compilation with FLUSH_ALL_TLBS debug option ../target/ppc/mmu_helper.c: In function 'helper_store_ibatu': ../target/ppc/mmu_helper.c:1802:17: error: unused variable 'cpu' [-Werror=unused-variable] 1802 | PowerPCCPU *cpu = env_archcpu(env); | ^~~ ../target/ppc/mmu_helper.c: In function 'helper_store_dbatu': ../target/ppc/mmu_helper.c:1838:17: error: unused variable 'cpu' [-Werror=unused-variable] 1838 | PowerPCCPU *cpu = env_archcpu(env); | ^~~ ../target/ppc/mmu_helper.c: In function 'helper_store_601_batu': ../target/ppc/mmu_helper.c:1874:17: error: unused variable 'cpu' [-Werror=unused-variable] 1874 | PowerPCCPU *cpu = env_archcpu(env); | ^~~ ../target/ppc/mmu_helper.c: In function 'helper_store_601_batl': ../target/ppc/mmu_helper.c:1919:17: error: unused variable 'cpu' [-Werror=unused-variable] 1919 | PowerPCCPU *cpu = env_archcpu(env); Fixes: db70b31144 ("target/ppc: Use env_cpu, env_archcpu") Signed-off-by: Fabiano Rosas Message-Id: <20210702215235.1941771-3-farosas@linux.ibm.com> Signed-off-by: David Gibson --- target/ppc/mmu_helper.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index 4c534b534b..945ac41d42 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -1755,9 +1755,6 @@ static inline void dump_store_bat(CPUPPCState *env, char ID, int ul, int nr, void helper_store_ibatu(CPUPPCState *env, uint32_t nr, target_ulong value) { target_ulong mask; -#if defined(FLUSH_ALL_TLBS) - PowerPCCPU *cpu = env_archcpu(env); -#endif dump_store_bat(env, 'I', 0, nr, value); if (env->IBAT[0][nr] != value) { @@ -1791,9 +1788,6 @@ void helper_store_ibatl(CPUPPCState *env, uint32_t nr, target_ulong value) void helper_store_dbatu(CPUPPCState *env, uint32_t nr, target_ulong value) { target_ulong mask; -#if defined(FLUSH_ALL_TLBS) - PowerPCCPU *cpu = env_archcpu(env); -#endif dump_store_bat(env, 'D', 0, nr, value); if (env->DBAT[0][nr] != value) { @@ -1828,7 +1822,6 @@ void helper_store_601_batu(CPUPPCState *env, uint32_t nr, target_ulong value) { target_ulong mask; #if defined(FLUSH_ALL_TLBS) - PowerPCCPU *cpu = env_archcpu(env); int do_inval; #endif @@ -1873,7 +1866,6 @@ void helper_store_601_batl(CPUPPCState *env, uint32_t nr, target_ulong value) #if !defined(FLUSH_ALL_TLBS) target_ulong mask; #else - PowerPCCPU *cpu = env_archcpu(env); int do_inval; #endif From ba1b5df070bb4cf1632aaefa4e17d42881d49988 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 2 Jul 2021 18:52:35 -0300 Subject: [PATCH 089/272] target/ppc: Fix compilation with DEBUG_BATS debug option ../target/ppc/mmu-hash32.c: In function 'ppc_hash32_bat_lookup': ../target/ppc/mmu-hash32.c:204:13: error: 'BATu' undeclared (first use in this function); 204 | BATu = &BATut[i]; | ^~~~ | BATut ../target/ppc/mmu-hash32.c:205:13: error: 'BATl' undeclared (first use in this function); 205 | BATl = &BATlt[i]; | ^~~~ | BATlt ../target/ppc/mmu-hash32.c:206:13: error: 'BEPIu' undeclared (first use in this function) 206 | BEPIu = *BATu & BATU32_BEPIU; | ^~~~~ ../target/ppc/mmu-hash32.c:206:29: error: 'BATU32_BEPIU' undeclared (first use in this function); 206 | BEPIu = *BATu & BATU32_BEPIU; | ^~~~~~~~~~~~ | BATU32_BEPI ../target/ppc/mmu-hash32.c:207:13: error: 'BEPIl' undeclared (first use in this function) 207 | BEPIl = *BATu & BATU32_BEPIL; | ^~~~~ ../target/ppc/mmu-hash32.c:207:29: error: 'BATU32_BEPIL' undeclared (first use in this function); 207 | BEPIl = *BATu & BATU32_BEPIL; | ^~~~~~~~~~~~ | BATU32_BEPI ../target/ppc/mmu-hash32.c:208:13: error: 'bl' undeclared (first use in this function) 208 | bl = (*BATu & 0x00001FFC) << 15; | ^~ Fixes: 9813279664 ("target-ppc: Disentangle BAT code for 32-bit hash MMUs") Signed-off-by: Fabiano Rosas Message-Id: <20210702215235.1941771-4-farosas@linux.ibm.com> Signed-off-by: David Gibson --- target/ppc/mmu-hash32.c | 5 ++++- target/ppc/mmu-hash32.h | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index 6a07c345e4..4edd5ffe14 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -27,7 +27,7 @@ #include "mmu-hash32.h" #include "exec/log.h" -/* #define DEBUG_BAT */ +/* #define DEBUG_BATS */ #ifdef DEBUG_BATS # define LOG_BATS(...) qemu_log_mask(CPU_LOG_MMU, __VA_ARGS__) @@ -199,6 +199,9 @@ static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea, /* No hit */ #if defined(DEBUG_BATS) if (qemu_log_enabled()) { + target_ulong *BATu, *BATl; + target_ulong BEPIl, BEPIu, bl; + LOG_BATS("no BAT match for " TARGET_FMT_lx ":\n", ea); for (i = 0; i < 4; i++) { BATu = &BATut[i]; diff --git a/target/ppc/mmu-hash32.h b/target/ppc/mmu-hash32.h index 8694eccabd..c9f584b8ee 100644 --- a/target/ppc/mmu-hash32.h +++ b/target/ppc/mmu-hash32.h @@ -22,6 +22,8 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, * Block Address Translation (BAT) definitions */ +#define BATU32_BEPIU 0xf0000000 +#define BATU32_BEPIL 0x0ffe0000 #define BATU32_BEPI 0xfffe0000 #define BATU32_BL 0x00001ffc #define BATU32_VS 0x00000002 From 3f9f76d5bb27c3700ae1d5336e8921f842caad2e Mon Sep 17 00:00:00 2001 From: "Bruno Larsen (billionai)" Date: Mon, 28 Jun 2021 10:36:08 -0300 Subject: [PATCH 090/272] target/ppc: fix address translation bug for radix mmus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit attempts to fix a technical hiccup first mentioned by Richard Henderson in https://lists.nongnu.org/archive/html/qemu-devel/2021-05/msg06247.html To sumarize the hiccup here, when radix-style mmus are translating an address, they might need to call a second level of translation, with hypervisor privileges. However, the way it was being done up until this point meant that the second level translation had the same privileges as the first level. It could lead to a bug in address translation when running KVM inside a TCG guest, but this bug was never experienced by users, so this isn't as much a bug fix as it is a correctness cleanup. This patch attempts that cleanup by making radix64_*_xlate functions receive the mmu_idx, and passing one with the correct permission for the second level translation. The mmuidx macros added by this patch are only correct for non-bookE mmus, because BookE style set the IS and DS bits inverted and there might be other subtle differences. However, there doesn't seem to be BookE cpus that have radix-style mmus, so we left a comment there to document the issue, in case a machine does have that and was missed. As part of this cleanup, we now need to send the correct mmmu_idx when calling get_phys_page_debug, otherwise we might not be able to see the memory that the CPU could Suggested-by: Richard Henderson Signed-off-by: Bruno Larsen (billionai) Reviewed-by: Richard Henderson Reviewed-by: Cédric Le Goater Tested-by: Cédric Le Goater Message-Id: <20210628133610.1143-2-bruno.larsen@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/mmu-book3s-v3.h | 13 +++++++++++++ target/ppc/mmu-radix64.c | 37 +++++++++++++++++++++---------------- target/ppc/mmu-radix64.h | 2 +- target/ppc/mmu_helper.c | 8 +++++--- 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/target/ppc/mmu-book3s-v3.h b/target/ppc/mmu-book3s-v3.h index a1326df969..c89d0bccfd 100644 --- a/target/ppc/mmu-book3s-v3.h +++ b/target/ppc/mmu-book3s-v3.h @@ -47,6 +47,19 @@ struct prtb_entry { uint64_t prtbe0, prtbe1; }; +/* + * These correspond to the mmu_idx values computed in + * hreg_compute_hflags_value. See the tables therein + * + * They are here because some bits are inverted for BookE MMUs + * not necessarily because they only work for BookS. However, + * we only needed to change BookS MMUs, we left the functions + * here to avoid other possible bugs for untested MMUs + */ +static inline bool mmuidx_pr(int idx) { return !(idx & 1); } +static inline bool mmuidx_real(int idx) { return idx & 2; } +static inline bool mmuidx_hv(int idx) { return idx & 4; } + #ifdef TARGET_PPC64 static inline bool ppc64_use_proc_tbl(PowerPCCPU *cpu) diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index cbd404bfa4..5b0e62e676 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -155,7 +155,7 @@ static void ppc_radix64_raise_hsi(PowerPCCPU *cpu, MMUAccessType access_type, static bool ppc_radix64_check_prot(PowerPCCPU *cpu, MMUAccessType access_type, uint64_t pte, int *fault_cause, int *prot, - bool partition_scoped) + int mmu_idx, bool partition_scoped) { CPUPPCState *env = &cpu->env; int need_prot; @@ -173,7 +173,8 @@ static bool ppc_radix64_check_prot(PowerPCCPU *cpu, MMUAccessType access_type, /* Determine permissions allowed by Encoded Access Authority */ if (!partition_scoped && (pte & R_PTE_EAA_PRIV) && msr_pr) { *prot = 0; - } else if (msr_pr || (pte & R_PTE_EAA_PRIV) || partition_scoped) { + } else if (mmuidx_pr(mmu_idx) || (pte & R_PTE_EAA_PRIV) || + partition_scoped) { *prot = ppc_radix64_get_prot_eaa(pte); } else { /* !msr_pr && !(pte & R_PTE_EAA_PRIV) && !partition_scoped */ *prot = ppc_radix64_get_prot_eaa(pte); @@ -299,7 +300,7 @@ static int ppc_radix64_partition_scoped_xlate(PowerPCCPU *cpu, ppc_v3_pate_t pate, hwaddr *h_raddr, int *h_prot, int *h_page_size, bool pde_addr, - bool guest_visible) + int mmu_idx, bool guest_visible) { int fault_cause = 0; hwaddr pte_addr; @@ -310,7 +311,8 @@ static int ppc_radix64_partition_scoped_xlate(PowerPCCPU *cpu, if (ppc_radix64_walk_tree(CPU(cpu)->as, g_raddr, pate.dw0 & PRTBE_R_RPDB, pate.dw0 & PRTBE_R_RPDS, h_raddr, h_page_size, &pte, &fault_cause, &pte_addr) || - ppc_radix64_check_prot(cpu, access_type, pte, &fault_cause, h_prot, true)) { + ppc_radix64_check_prot(cpu, access_type, pte, + &fault_cause, h_prot, mmu_idx, true)) { if (pde_addr) { /* address being translated was that of a guest pde */ fault_cause |= DSISR_PRTABLE_FAULT; } @@ -332,7 +334,7 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, vaddr eaddr, uint64_t pid, ppc_v3_pate_t pate, hwaddr *g_raddr, int *g_prot, int *g_page_size, - bool guest_visible) + int mmu_idx, bool guest_visible) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; @@ -367,7 +369,8 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, ret = ppc_radix64_partition_scoped_xlate(cpu, 0, eaddr, prtbe_addr, pate, &h_raddr, &h_prot, &h_page_size, true, - guest_visible); + /* mmu_idx is 5 because we're translating from hypervisor scope */ + 5, guest_visible); if (ret) { return ret; } @@ -407,7 +410,8 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, ret = ppc_radix64_partition_scoped_xlate(cpu, 0, eaddr, pte_addr, pate, &h_raddr, &h_prot, &h_page_size, true, - guest_visible); + /* mmu_idx is 5 because we're translating from hypervisor scope */ + 5, guest_visible); if (ret) { return ret; } @@ -431,7 +435,8 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, *g_raddr = (rpn & ~mask) | (eaddr & mask); } - if (ppc_radix64_check_prot(cpu, access_type, pte, &fault_cause, g_prot, false)) { + if (ppc_radix64_check_prot(cpu, access_type, pte, &fault_cause, + g_prot, mmu_idx, false)) { /* Access denied due to protection */ if (guest_visible) { ppc_radix64_raise_si(cpu, access_type, eaddr, fault_cause); @@ -464,7 +469,7 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, * +-------------+----------------+---------------+ */ bool ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, - hwaddr *raddr, int *psizep, int *protp, + hwaddr *raddr, int *psizep, int *protp, int mmu_idx, bool guest_visible) { CPUPPCState *env = &cpu->env; @@ -474,17 +479,17 @@ bool ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, hwaddr g_raddr; bool relocation; - assert(!(msr_hv && cpu->vhyp)); + assert(!(mmuidx_hv(mmu_idx) && cpu->vhyp)); - relocation = (access_type == MMU_INST_FETCH ? msr_ir : msr_dr); + relocation = !mmuidx_real(mmu_idx); /* HV or virtual hypervisor Real Mode Access */ - if (!relocation && (msr_hv || cpu->vhyp)) { + if (!relocation && (mmuidx_hv(mmu_idx) || cpu->vhyp)) { /* In real mode top 4 effective addr bits (mostly) ignored */ *raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL; /* In HV mode, add HRMOR if top EA bit is clear */ - if (msr_hv || !env->has_hv_mode) { + if (mmuidx_hv(mmu_idx) || !env->has_hv_mode) { if (!(eaddr >> 63)) { *raddr |= env->spr[SPR_HRMOR]; } @@ -546,7 +551,7 @@ bool ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, if (relocation) { int ret = ppc_radix64_process_scoped_xlate(cpu, access_type, eaddr, pid, pate, &g_raddr, &prot, - &psize, guest_visible); + &psize, mmu_idx, guest_visible); if (ret) { return false; } @@ -564,13 +569,13 @@ bool ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, * quadrants 1 or 2. Translates a guest real address to a host * real address. */ - if (lpid || !msr_hv) { + if (lpid || !mmuidx_hv(mmu_idx)) { int ret; ret = ppc_radix64_partition_scoped_xlate(cpu, access_type, eaddr, g_raddr, pate, raddr, &prot, &psize, false, - guest_visible); + mmu_idx, guest_visible); if (ret) { return false; } diff --git a/target/ppc/mmu-radix64.h b/target/ppc/mmu-radix64.h index 6b13b89b64..b70357cf34 100644 --- a/target/ppc/mmu-radix64.h +++ b/target/ppc/mmu-radix64.h @@ -45,7 +45,7 @@ #ifdef TARGET_PPC64 bool ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, - hwaddr *raddr, int *psizep, int *protp, + hwaddr *raddr, int *psizep, int *protp, int mmu_idx, bool guest_visible); static inline int ppc_radix64_get_prot_eaa(uint64_t pte) diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index 945ac41d42..46b4afce54 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -2900,7 +2900,7 @@ static bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, case POWERPC_MMU_3_00: if (ppc64_v3_radix(cpu)) { return ppc_radix64_xlate(cpu, eaddr, access_type, - raddrp, psizep, protp, guest_visible); + raddrp, psizep, protp, mmu_idx, guest_visible); } /* fall through */ case POWERPC_MMU_64B: @@ -2933,8 +2933,10 @@ hwaddr ppc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) * try an MMU_DATA_LOAD, we may not be able to read instructions * mapped by code TLBs, so we also try a MMU_INST_FETCH. */ - if (ppc_xlate(cpu, addr, MMU_DATA_LOAD, &raddr, &s, &p, 0, false) || - ppc_xlate(cpu, addr, MMU_INST_FETCH, &raddr, &s, &p, 0, false)) { + if (ppc_xlate(cpu, addr, MMU_DATA_LOAD, &raddr, &s, &p, + cpu_mmu_index(&cpu->env, false), false) || + ppc_xlate(cpu, addr, MMU_INST_FETCH, &raddr, &s, &p, + cpu_mmu_index(&cpu->env, true), false)) { return raddr & TARGET_PAGE_MASK; } return -1; From 03695a9870662ddaeef2e4f2129df31beb6e73bd Mon Sep 17 00:00:00 2001 From: "Bruno Larsen (billionai)" Date: Mon, 28 Jun 2021 10:36:10 -0300 Subject: [PATCH 091/272] target/ppc: changed ppc_hash64_xlate to use mmu_idx Changed hash64 address translation to use the supplied mmu_idx instead of using the one stored in the msr, for parity purposes (other book3s MMUs already use it). Signed-off-by: Bruno Larsen (billionai) Reviewed-by: Richard Henderson Message-Id: <20210628133610.1143-4-bruno.larsen@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/mmu-hash64.c | 43 ++++++++++++++++++++--------------------- target/ppc/mmu-hash64.h | 2 +- target/ppc/mmu_helper.c | 2 +- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index c1b98a97e9..19832c4b46 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -366,10 +366,9 @@ static inline int ppc_hash64_pte_noexec_guard(PowerPCCPU *cpu, } /* Check Basic Storage Protection */ -static int ppc_hash64_pte_prot(PowerPCCPU *cpu, +static int ppc_hash64_pte_prot(int mmu_idx, ppc_slb_t *slb, ppc_hash_pte64_t pte) { - CPUPPCState *env = &cpu->env; unsigned pp, key; /* * Some pp bit combinations have undefined behaviour, so default @@ -377,7 +376,7 @@ static int ppc_hash64_pte_prot(PowerPCCPU *cpu, */ int prot = 0; - key = !!(msr_pr ? (slb->vsid & SLB_VSID_KP) + key = !!(mmuidx_pr(mmu_idx) ? (slb->vsid & SLB_VSID_KP) : (slb->vsid & SLB_VSID_KS)); pp = (pte.pte1 & HPTE64_R_PP) | ((pte.pte1 & HPTE64_R_PP0) >> 61); @@ -744,17 +743,17 @@ static bool ppc_hash64_use_vrma(CPUPPCState *env) } } -static void ppc_hash64_set_isi(CPUState *cs, uint64_t error_code) +static void ppc_hash64_set_isi(CPUState *cs, int mmu_idx, uint64_t error_code) { CPUPPCState *env = &POWERPC_CPU(cs)->env; bool vpm; - if (msr_ir) { + if (!mmuidx_real(mmu_idx)) { vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM1); } else { vpm = ppc_hash64_use_vrma(env); } - if (vpm && !msr_hv) { + if (vpm && !mmuidx_hv(mmu_idx)) { cs->exception_index = POWERPC_EXCP_HISI; } else { cs->exception_index = POWERPC_EXCP_ISI; @@ -762,17 +761,17 @@ static void ppc_hash64_set_isi(CPUState *cs, uint64_t error_code) env->error_code = error_code; } -static void ppc_hash64_set_dsi(CPUState *cs, uint64_t dar, uint64_t dsisr) +static void ppc_hash64_set_dsi(CPUState *cs, int mmu_idx, uint64_t dar, uint64_t dsisr) { CPUPPCState *env = &POWERPC_CPU(cs)->env; bool vpm; - if (msr_dr) { + if (!mmuidx_real(mmu_idx)) { vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM1); } else { vpm = ppc_hash64_use_vrma(env); } - if (vpm && !msr_hv) { + if (vpm && !mmuidx_hv(mmu_idx)) { cs->exception_index = POWERPC_EXCP_HDSI; env->spr[SPR_HDAR] = dar; env->spr[SPR_HDSISR] = dsisr; @@ -874,7 +873,7 @@ static int build_vrma_slbe(PowerPCCPU *cpu, ppc_slb_t *slb) } bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, - hwaddr *raddrp, int *psizep, int *protp, + hwaddr *raddrp, int *psizep, int *protp, int mmu_idx, bool guest_visible) { CPUState *cs = CPU(cpu); @@ -897,7 +896,7 @@ bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, */ /* 1. Handle real mode accesses */ - if (access_type == MMU_INST_FETCH ? !msr_ir : !msr_dr) { + if (mmuidx_real(mmu_idx)) { /* * Translation is supposedly "off", but in real mode the top 4 * effective address bits are (mostly) ignored @@ -909,7 +908,7 @@ bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, * In virtual hypervisor mode, there's nothing to do: * EA == GPA == qemu guest address */ - } else if (msr_hv || !env->has_hv_mode) { + } else if (mmuidx_hv(mmu_idx) || !env->has_hv_mode) { /* In HV mode, add HRMOR if top EA bit is clear */ if (!(eaddr >> 63)) { raddr |= env->spr[SPR_HRMOR]; @@ -937,13 +936,13 @@ bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, } switch (access_type) { case MMU_INST_FETCH: - ppc_hash64_set_isi(cs, SRR1_PROTFAULT); + ppc_hash64_set_isi(cs, mmu_idx, SRR1_PROTFAULT); break; case MMU_DATA_LOAD: - ppc_hash64_set_dsi(cs, eaddr, DSISR_PROTFAULT); + ppc_hash64_set_dsi(cs, mmu_idx, eaddr, DSISR_PROTFAULT); break; case MMU_DATA_STORE: - ppc_hash64_set_dsi(cs, eaddr, + ppc_hash64_set_dsi(cs, mmu_idx, eaddr, DSISR_PROTFAULT | DSISR_ISSTORE); break; default: @@ -996,7 +995,7 @@ bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, /* 3. Check for segment level no-execute violation */ if (access_type == MMU_INST_FETCH && (slb->vsid & SLB_VSID_N)) { if (guest_visible) { - ppc_hash64_set_isi(cs, SRR1_NOEXEC_GUARD); + ppc_hash64_set_isi(cs, mmu_idx, SRR1_NOEXEC_GUARD); } return false; } @@ -1009,13 +1008,13 @@ bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, } switch (access_type) { case MMU_INST_FETCH: - ppc_hash64_set_isi(cs, SRR1_NOPTE); + ppc_hash64_set_isi(cs, mmu_idx, SRR1_NOPTE); break; case MMU_DATA_LOAD: - ppc_hash64_set_dsi(cs, eaddr, DSISR_NOPTE); + ppc_hash64_set_dsi(cs, mmu_idx, eaddr, DSISR_NOPTE); break; case MMU_DATA_STORE: - ppc_hash64_set_dsi(cs, eaddr, DSISR_NOPTE | DSISR_ISSTORE); + ppc_hash64_set_dsi(cs, mmu_idx, eaddr, DSISR_NOPTE | DSISR_ISSTORE); break; default: g_assert_not_reached(); @@ -1028,7 +1027,7 @@ bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, /* 5. Check access permissions */ exec_prot = ppc_hash64_pte_noexec_guard(cpu, pte); - pp_prot = ppc_hash64_pte_prot(cpu, slb, pte); + pp_prot = ppc_hash64_pte_prot(mmu_idx, slb, pte); amr_prot = ppc_hash64_amr_prot(cpu, pte); prot = exec_prot & pp_prot & amr_prot; @@ -1049,7 +1048,7 @@ bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, if (PAGE_EXEC & ~amr_prot) { srr1 |= SRR1_IAMR; /* Access violates virt pg class key prot */ } - ppc_hash64_set_isi(cs, srr1); + ppc_hash64_set_isi(cs, mmu_idx, srr1); } else { int dsisr = 0; if (need_prot & ~pp_prot) { @@ -1061,7 +1060,7 @@ bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, if (need_prot & ~amr_prot) { dsisr |= DSISR_AMR; } - ppc_hash64_set_dsi(cs, eaddr, dsisr); + ppc_hash64_set_dsi(cs, mmu_idx, eaddr, dsisr); } return false; } diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h index 9f338e1fe9..c5b2f97ff7 100644 --- a/target/ppc/mmu-hash64.h +++ b/target/ppc/mmu-hash64.h @@ -8,7 +8,7 @@ void dump_slb(PowerPCCPU *cpu); int ppc_store_slb(PowerPCCPU *cpu, target_ulong slot, target_ulong esid, target_ulong vsid); bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, - hwaddr *raddrp, int *psizep, int *protp, + hwaddr *raddrp, int *psizep, int *protp, int mmu_idx, bool guest_visible); void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu, target_ulong pte_index, diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index 46b4afce54..819ee27b62 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -2908,7 +2908,7 @@ static bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, case POWERPC_MMU_2_06: case POWERPC_MMU_2_07: return ppc_hash64_xlate(cpu, eaddr, access_type, - raddrp, psizep, protp, guest_visible); + raddrp, psizep, protp, mmu_idx, guest_visible); #endif case POWERPC_MMU_32B: From a97c4d3c1e55f3098549bc4481f58a91a5834620 Mon Sep 17 00:00:00 2001 From: "Bruno Larsen (billionai)" Date: Tue, 6 Jul 2021 12:03:15 -0300 Subject: [PATCH 092/272] target/ppc: introduce mmu-books.h Intrudoce a header common to all BookS MMUs, that can hold code that is common to hash32 and book3s-v3 MMUs. Suggested-by: David Gibson Signed-off-by: Bruno Larsen (billionai) Message-Id: <20210706150316.21005-2-bruno.larsen@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/mmu-book3s-v3.h | 14 +------------- target/ppc/mmu-books.h | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 13 deletions(-) create mode 100644 target/ppc/mmu-books.h diff --git a/target/ppc/mmu-book3s-v3.h b/target/ppc/mmu-book3s-v3.h index c89d0bccfd..d6d5ed8f8e 100644 --- a/target/ppc/mmu-book3s-v3.h +++ b/target/ppc/mmu-book3s-v3.h @@ -21,6 +21,7 @@ #define PPC_MMU_BOOK3S_V3_H #include "mmu-hash64.h" +#include "mmu-books.h" #ifndef CONFIG_USER_ONLY @@ -47,19 +48,6 @@ struct prtb_entry { uint64_t prtbe0, prtbe1; }; -/* - * These correspond to the mmu_idx values computed in - * hreg_compute_hflags_value. See the tables therein - * - * They are here because some bits are inverted for BookE MMUs - * not necessarily because they only work for BookS. However, - * we only needed to change BookS MMUs, we left the functions - * here to avoid other possible bugs for untested MMUs - */ -static inline bool mmuidx_pr(int idx) { return !(idx & 1); } -static inline bool mmuidx_real(int idx) { return idx & 2; } -static inline bool mmuidx_hv(int idx) { return idx & 4; } - #ifdef TARGET_PPC64 static inline bool ppc64_use_proc_tbl(PowerPCCPU *cpu) diff --git a/target/ppc/mmu-books.h b/target/ppc/mmu-books.h new file mode 100644 index 0000000000..0d12551867 --- /dev/null +++ b/target/ppc/mmu-books.h @@ -0,0 +1,30 @@ +/* + * PowerPC BookS emulation generic mmu definitions for qemu. + * + * Copyright (c) 2021 Instituto de Pesquisas Eldorado (eldorado.org.br) + * + * 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 PPC_MMU_BOOKS_H +#define PPC_MMU_BOOKS_H + +/* + * These correspond to the mmu_idx values computed in + * hreg_compute_hflags_value. See the tables therein + */ +static inline bool mmuidx_pr(int idx) { return !(idx & 1); } +static inline bool mmuidx_real(int idx) { return idx & 2; } +static inline bool mmuidx_hv(int idx) { return idx & 4; } +#endif /* PPC_MMU_BOOKS_H */ From d423baf9b48cc4749e5f7d77214a089651bc3325 Mon Sep 17 00:00:00 2001 From: "Bruno Larsen (billionai)" Date: Tue, 6 Jul 2021 12:03:16 -0300 Subject: [PATCH 093/272] target/ppc: change ppc_hash32_xlate to use mmu_idx Changed hash32 address translation to use the supplied mmu_idx, instead of using what was stored in the msr, for parity purposes (radix64 already uses that) and for conceptual correctness, all the relevant functions should always use the supplied mmu_idx, as there are no guarantees that the mmu_idx stored in the CPU variable will not desync. Signed-off-by: Bruno Larsen (billionai) Reviewed-by: David Gibson Message-Id: <20210706150316.21005-3-bruno.larsen@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/mmu-hash32.c | 40 +++++++++++++++++++--------------------- target/ppc/mmu-hash32.h | 2 +- target/ppc/mmu_helper.c | 2 +- 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index 4edd5ffe14..3957aab2dc 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -25,6 +25,7 @@ #include "kvm_ppc.h" #include "internal.h" #include "mmu-hash32.h" +#include "mmu-books.h" #include "exec/log.h" /* #define DEBUG_BATS */ @@ -86,25 +87,22 @@ static int ppc_hash32_pp_prot(int key, int pp, int nx) return prot; } -static int ppc_hash32_pte_prot(PowerPCCPU *cpu, +static int ppc_hash32_pte_prot(int mmu_idx, target_ulong sr, ppc_hash_pte32_t pte) { - CPUPPCState *env = &cpu->env; unsigned pp, key; - key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS)); + key = !!(mmuidx_pr(mmu_idx) ? (sr & SR32_KP) : (sr & SR32_KS)); pp = pte.pte1 & HPTE32_R_PP; return ppc_hash32_pp_prot(key, pp, !!(sr & SR32_NX)); } -static target_ulong hash32_bat_size(PowerPCCPU *cpu, +static target_ulong hash32_bat_size(int mmu_idx, target_ulong batu, target_ulong batl) { - CPUPPCState *env = &cpu->env; - - if ((msr_pr && !(batu & BATU32_VP)) - || (!msr_pr && !(batu & BATU32_VS))) { + if ((mmuidx_pr(mmu_idx) && !(batu & BATU32_VP)) + || (!mmuidx_pr(mmu_idx) && !(batu & BATU32_VS))) { return 0; } @@ -137,14 +135,13 @@ static target_ulong hash32_bat_601_size(PowerPCCPU *cpu, return BATU32_BEPI & ~((batl & BATL32_601_BL) << 17); } -static int hash32_bat_601_prot(PowerPCCPU *cpu, +static int hash32_bat_601_prot(int mmu_idx, target_ulong batu, target_ulong batl) { - CPUPPCState *env = &cpu->env; int key, pp; pp = batu & BATU32_601_PP; - if (msr_pr == 0) { + if (mmuidx_pr(mmu_idx) == 0) { key = !!(batu & BATU32_601_KS); } else { key = !!(batu & BATU32_601_KP); @@ -153,7 +150,8 @@ static int hash32_bat_601_prot(PowerPCCPU *cpu, } static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea, - MMUAccessType access_type, int *prot) + MMUAccessType access_type, int *prot, + int mmu_idx) { CPUPPCState *env = &cpu->env; target_ulong *BATlt, *BATut; @@ -177,7 +175,7 @@ static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea, if (unlikely(env->mmu_model == POWERPC_MMU_601)) { mask = hash32_bat_601_size(cpu, batu, batl); } else { - mask = hash32_bat_size(cpu, batu, batl); + mask = hash32_bat_size(mmu_idx, batu, batl); } LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n", __func__, @@ -187,7 +185,7 @@ static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea, hwaddr raddr = (batl & mask) | (ea & ~mask); if (unlikely(env->mmu_model == POWERPC_MMU_601)) { - *prot = hash32_bat_601_prot(cpu, batu, batl); + *prot = hash32_bat_601_prot(mmu_idx, batu, batl); } else { *prot = hash32_bat_prot(cpu, batu, batl); } @@ -224,12 +222,12 @@ static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea, static bool ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, target_ulong eaddr, MMUAccessType access_type, - hwaddr *raddr, int *prot, + hwaddr *raddr, int *prot, int mmu_idx, bool guest_visible) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - int key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS)); + int key = !!(mmuidx_pr(mmu_idx) ? (sr & SR32_KP) : (sr & SR32_KS)); qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); @@ -428,7 +426,7 @@ static hwaddr ppc_hash32_pte_raddr(target_ulong sr, ppc_hash_pte32_t pte, } bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, - hwaddr *raddrp, int *psizep, int *protp, + hwaddr *raddrp, int *psizep, int *protp, int mmu_idx, bool guest_visible) { CPUState *cs = CPU(cpu); @@ -444,7 +442,7 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, *psizep = TARGET_PAGE_BITS; /* 1. Handle real mode accesses */ - if (access_type == MMU_INST_FETCH ? !msr_ir : !msr_dr) { + if (mmuidx_real(mmu_idx)) { /* Translation is off */ *raddrp = eaddr; *protp = PAGE_READ | PAGE_WRITE | PAGE_EXEC; @@ -455,7 +453,7 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, /* 2. Check Block Address Translation entries (BATs) */ if (env->nb_BATs != 0) { - raddr = ppc_hash32_bat_lookup(cpu, eaddr, access_type, protp); + raddr = ppc_hash32_bat_lookup(cpu, eaddr, access_type, protp, mmu_idx); if (raddr != -1) { if (need_prot & ~*protp) { if (guest_visible) { @@ -486,7 +484,7 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, /* 4. Handle direct store segments */ if (sr & SR32_T) { return ppc_hash32_direct_store(cpu, sr, eaddr, access_type, - raddrp, protp, guest_visible); + raddrp, protp, mmu_idx, guest_visible); } /* 5. Check for segment level no-execute violation */ @@ -523,7 +521,7 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, /* 7. Check access permissions */ - prot = ppc_hash32_pte_prot(cpu, sr, pte); + prot = ppc_hash32_pte_prot(mmu_idx, sr, pte); if (need_prot & ~prot) { /* Access right violation */ diff --git a/target/ppc/mmu-hash32.h b/target/ppc/mmu-hash32.h index c9f584b8ee..3892b693d6 100644 --- a/target/ppc/mmu-hash32.h +++ b/target/ppc/mmu-hash32.h @@ -5,7 +5,7 @@ hwaddr get_pteg_offset32(PowerPCCPU *cpu, hwaddr hash); bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, - hwaddr *raddrp, int *psizep, int *protp, + hwaddr *raddrp, int *psizep, int *protp, int mmu_idx, bool guest_visible); /* diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index 819ee27b62..47e9f9529e 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -2914,7 +2914,7 @@ static bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, case POWERPC_MMU_32B: case POWERPC_MMU_601: return ppc_hash32_xlate(cpu, eaddr, access_type, - raddrp, psizep, protp, guest_visible); + raddrp, psizep, protp, mmu_idx, guest_visible); default: return ppc_jumbo_xlate(cpu, eaddr, access_type, raddrp, From a0c3747e14689583b84a6c15b80e7bc2209111be Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Tue, 6 Jul 2021 10:46:41 +0800 Subject: [PATCH 094/272] roms/u-boot: Bump ppce500 u-boot to v2021.07 to add eTSEC support Update the QEMU shipped u-boot.e500 image built from U-Boot mainline v2021.07 release, which added eTSEC support to the QEMU ppce500 target, via the following U-Boot series: http://patchwork.ozlabs.org/project/uboot/list/?series=233875&state=* The cross-compilation toolchain used to build the U-Boot image is: https://mirrors.edge.kernel.org/pub/tools/crosstool/files/bin/x86_64/10.1.0/x86_64-gcc-10.1.0-nolibc-powerpc-linux.tar.xz Signed-off-by: Bin Meng Signed-off-by: David Gibson --- pc-bios/u-boot.e500 | Bin 406920 -> 421720 bytes roms/u-boot | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/pc-bios/u-boot.e500 b/pc-bios/u-boot.e500 index d2e29f81d648e4fa7e010ab7c44f2d22154094aa..8e635c8a5c9e53469710be27aa0c5e5a4045c2cd 100644 GIT binary patch delta 117630 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 F{{aRmAFu!b delta 102979 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 J^cErV{{aOP5+MKp diff --git a/roms/u-boot b/roms/u-boot index b46dd116ce..840658b093 160000 --- a/roms/u-boot +++ b/roms/u-boot @@ -1 +1 @@ -Subproject commit b46dd116ce03e235f2a7d4843c6278e1da44b5e1 +Subproject commit 840658b093976390e9537724f802281c9c8439f5 From ea41397055f6dec68d3357df412f9f5fb3d89f84 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Tue, 6 Jul 2021 11:19:01 +0800 Subject: [PATCH 095/272] docs/system: ppc: Update ppce500 documentation with eTSEC support This adds eTSEC support to the PowerPC `ppce500` machine documentation. Signed-off-by: Bin Meng Signed-off-by: David Gibson --- docs/system/ppc/ppce500.rst | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/system/ppc/ppce500.rst b/docs/system/ppc/ppce500.rst index 7a815c1881..afc58f60f5 100644 --- a/docs/system/ppc/ppce500.rst +++ b/docs/system/ppc/ppce500.rst @@ -19,6 +19,7 @@ The ``ppce500`` machine supports the following devices: * Power-off functionality via one GPIO pin * 1 Freescale MPC8xxx PCI host controller * VirtIO devices via PCI bus +* 1 Freescale Enhanced Triple Speed Ethernet controller (eTSEC) Hardware configuration information ---------------------------------- @@ -121,7 +122,7 @@ To boot the 32-bit Linux kernel: Running U-Boot -------------- -U-Boot mainline v2021.04 release is tested at the time of writing. To build a +U-Boot mainline v2021.07 release is tested at the time of writing. To build a U-Boot mainline bootloader that can be booted by the ``ppce500`` machine, use the qemu-ppce500_defconfig with similar commands as described above for Linux: @@ -154,3 +155,10 @@ interface at PCI address 0.1.0, but we can switch that to an e1000 NIC by: -display none -serial stdio \ -bios u-boot \ -nic tap,ifname=tap0,script=no,downscript=no,model=e1000 + +The QEMU ``ppce500`` machine can also dynamically instantiate an eTSEC device +if “-device eTSEC” is given to QEMU: + +.. code-block:: bash + + -netdev tap,ifname=tap0,script=no,downscript=no,id=net0 -device eTSEC,netdev=net0 From fc8c745d50150a63f6c5ba2cd0b83b430963b7e8 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Fri, 25 Jun 2021 15:51:55 +1000 Subject: [PATCH 096/272] spapr: Implement Open Firmware client interface The PAPR platform describes an OS environment that's presented by a combination of a hypervisor and firmware. The features it specifies require collaboration between the firmware and the hypervisor. Since the beginning, the runtime component of the firmware (RTAS) has been implemented as a 20 byte shim which simply forwards it to a hypercall implemented in qemu. The boot time firmware component is SLOF - but a build that's specific to qemu, and has always needed to be updated in sync with it. Even though we've managed to limit the amount of runtime communication we need between qemu and SLOF, there's some, and it has become increasingly awkward to handle as we've implemented new features. This implements a boot time OF client interface (CI) which is enabled by a new "x-vof" pseries machine option (stands for "Virtual Open Firmware). When enabled, QEMU implements the custom H_OF_CLIENT hcall which implements Open Firmware Client Interface (OF CI). This allows using a smaller stateless firmware which does not have to manage the device tree. The new "vof.bin" firmware image is included with source code under pc-bios/. It also includes RTAS blob. This implements a handful of CI methods just to get -kernel/-initrd working. In particular, this implements the device tree fetching and simple memory allocator - "claim" (an OF CI memory allocator) and updates "/memory@0/available" to report the client about available memory. This implements changing some device tree properties which we know how to deal with, the rest is ignored. To allow changes, this skips fdt_pack() when x-vof=on as not packing the blob leaves some room for appending. In absence of SLOF, this assigns phandles to device tree nodes to make device tree traversing work. When x-vof=on, this adds "/chosen" every time QEMU (re)builds a tree. This adds basic instances support which are managed by a hash map ihandle -> [phandle]. Before the guest started, the used memory is: 0..e60 - the initial firmware 8000..10000 - stack 400000.. - kernel 3ea0000.. - initramdisk This OF CI does not implement "interpret". Unlike SLOF, this does not format uninitialized nvram. Instead, this includes a disk image with pre-formatted nvram. With this basic support, this can only boot into kernel directly. However this is just enough for the petitboot kernel and initradmdisk to boot from any possible source. Note this requires reasonably recent guest kernel with: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=df5be5be8735 The immediate benefit is much faster booting time which especially crucial with fully emulated early CPU bring up environments. Also this may come handy when/if GRUB-in-the-userspace sees light of the day. This separates VOF and sPAPR in a hope that VOF bits may be reused by other POWERPC boards which do not support pSeries. This assumes potential support for booting from QEMU backends such as blockdev or netdev without devices/drivers used. Signed-off-by: Alexey Kardashevskiy Message-Id: <20210625055155.2252896-1-aik@ozlabs.ru> Reviewed-by: BALATON Zoltan [dwg: Adjusted some includes which broke compile in some more obscure compilation setups] Signed-off-by: David Gibson --- MAINTAINERS | 12 + hw/ppc/Kconfig | 4 + hw/ppc/meson.build | 3 + hw/ppc/spapr.c | 67 ++- hw/ppc/spapr_hcall.c | 25 +- hw/ppc/spapr_vof.c | 153 ++++++ hw/ppc/trace-events | 24 + hw/ppc/vof.c | 1049 +++++++++++++++++++++++++++++++++++++++ include/hw/ppc/spapr.h | 19 +- include/hw/ppc/vof.h | 58 +++ pc-bios/README | 4 + pc-bios/vof-nvram.bin | Bin 0 -> 16384 bytes pc-bios/vof.bin | Bin 0 -> 3784 bytes pc-bios/vof/Makefile | 23 + pc-bios/vof/bootmem.c | 14 + pc-bios/vof/ci.c | 91 ++++ pc-bios/vof/entry.S | 49 ++ pc-bios/vof/libc.c | 92 ++++ pc-bios/vof/main.c | 21 + pc-bios/vof/vof.h | 43 ++ pc-bios/vof/vof.lds | 48 ++ tests/qtest/rtas-test.c | 15 +- 22 files changed, 1801 insertions(+), 13 deletions(-) create mode 100644 hw/ppc/spapr_vof.c create mode 100644 hw/ppc/vof.c create mode 100644 include/hw/ppc/vof.h create mode 100644 pc-bios/vof-nvram.bin create mode 100755 pc-bios/vof.bin create mode 100644 pc-bios/vof/Makefile create mode 100644 pc-bios/vof/bootmem.c create mode 100644 pc-bios/vof/ci.c create mode 100644 pc-bios/vof/entry.S create mode 100644 pc-bios/vof/libc.c create mode 100644 pc-bios/vof/main.c create mode 100644 pc-bios/vof/vof.h create mode 100644 pc-bios/vof/vof.lds diff --git a/MAINTAINERS b/MAINTAINERS index 684142e12e..ce122eeced 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1360,6 +1360,18 @@ F: hw/pci-host/mv64361.c F: hw/pci-host/mv643xx.h F: include/hw/pci-host/mv64361.h +Virtual Open Firmware (VOF) +M: Alexey Kardashevskiy +M: David Gibson +M: Greg Kurz +L: qemu-ppc@nongnu.org +S: Maintained +F: hw/ppc/spapr_vof* +F: hw/ppc/vof* +F: include/hw/ppc/vof* +F: pc-bios/vof/* +F: pc-bios/vof* + RISC-V Machines --------------- OpenTitan diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig index 66e0b15d9e..67630f80e1 100644 --- a/hw/ppc/Kconfig +++ b/hw/ppc/Kconfig @@ -13,6 +13,7 @@ config PSERIES select MSI_NONBROKEN select FDT_PPC select CHRP_NVRAM + select VOF config SPAPR_RNG bool @@ -144,3 +145,6 @@ config FW_CFG_PPC config FDT_PPC bool + +config VOF + bool diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build index 597d974dd4..aa4c8e6a2e 100644 --- a/hw/ppc/meson.build +++ b/hw/ppc/meson.build @@ -84,4 +84,7 @@ ppc_ss.add(when: 'CONFIG_VIRTEX', if_true: files('virtex_ml507.c')) # Pegasos2 ppc_ss.add(when: 'CONFIG_PEGASOS2', if_true: files('pegasos2.c')) +ppc_ss.add(when: 'CONFIG_VOF', if_true: files('vof.c')) +ppc_ss.add(when: ['CONFIG_VOF', 'CONFIG_PSERIES'], if_true: files('spapr_vof.c')) + hw_arch += {'ppc': ppc_ss} diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 9e19c57032..e9b6d0f587 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -101,6 +101,7 @@ #define FDT_MAX_ADDR 0x80000000 /* FDT must stay below that */ #define FW_MAX_SIZE 0x400000 #define FW_FILE_NAME "slof.bin" +#define FW_FILE_NAME_VOF "vof.bin" #define FW_OVERHEAD 0x2800000 #define KERNEL_LOAD_ADDR FW_MAX_SIZE @@ -1643,22 +1644,37 @@ static void spapr_machine_reset(MachineState *machine) fdt_addr = MIN(spapr->rma_size, FDT_MAX_ADDR) - FDT_MAX_SIZE; fdt = spapr_build_fdt(spapr, true, FDT_MAX_SIZE); + if (spapr->vof) { + target_ulong stack_ptr = 0; - rc = fdt_pack(fdt); + spapr_vof_reset(spapr, fdt, &stack_ptr, &error_fatal); - /* Should only fail if we've built a corrupted tree */ - assert(rc == 0); + spapr_cpu_set_entry_state(first_ppc_cpu, SPAPR_ENTRY_POINT, + stack_ptr, spapr->initrd_base, + spapr->initrd_size); + /* VOF is 32bit BE so enforce MSR here */ + first_ppc_cpu->env.msr &= ~((1ULL << MSR_SF) | (1ULL << MSR_LE)); + /* + * Do not pack the FDT as the client may change properties. + * VOF client does not expect the FDT so we do not load it to the VM. + */ + } else { + rc = fdt_pack(fdt); + /* Should only fail if we've built a corrupted tree */ + assert(rc == 0); - /* Load the fdt */ + spapr_cpu_set_entry_state(first_ppc_cpu, SPAPR_ENTRY_POINT, + 0, fdt_addr, 0); + cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt)); + } qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt)); - cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt)); + g_free(spapr->fdt_blob); spapr->fdt_size = fdt_totalsize(fdt); spapr->fdt_initial_size = spapr->fdt_size; spapr->fdt_blob = fdt; /* Set up the entry state */ - spapr_cpu_set_entry_state(first_ppc_cpu, SPAPR_ENTRY_POINT, 0, fdt_addr, 0); first_ppc_cpu->env.gpr[5] = 0; spapr->fwnmi_system_reset_addr = -1; @@ -2661,7 +2677,8 @@ static void spapr_machine_init(MachineState *machine) SpaprMachineState *spapr = SPAPR_MACHINE(machine); SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); MachineClass *mc = MACHINE_GET_CLASS(machine); - const char *bios_name = machine->firmware ?: FW_FILE_NAME; + const char *bios_default = spapr->vof ? FW_FILE_NAME_VOF : FW_FILE_NAME; + const char *bios_name = machine->firmware ?: bios_default; const char *kernel_filename = machine->kernel_filename; const char *initrd_filename = machine->initrd_filename; PCIHostState *phb; @@ -3018,6 +3035,10 @@ static void spapr_machine_init(MachineState *machine) } qemu_cond_init(&spapr->fwnmi_machine_check_interlock_cond); + if (spapr->vof) { + spapr->vof->fw_size = fw_size; /* for claim() on itself */ + spapr_register_hypercall(KVMPPC_H_VOF_CLIENT, spapr_h_vof_client); + } } #define DEFAULT_KVM_TYPE "auto" @@ -3208,6 +3229,28 @@ static void spapr_set_resize_hpt(Object *obj, const char *value, Error **errp) } } +static bool spapr_get_vof(Object *obj, Error **errp) +{ + SpaprMachineState *spapr = SPAPR_MACHINE(obj); + + return spapr->vof != NULL; +} + +static void spapr_set_vof(Object *obj, bool value, Error **errp) +{ + SpaprMachineState *spapr = SPAPR_MACHINE(obj); + + if (spapr->vof) { + vof_cleanup(spapr->vof); + g_free(spapr->vof); + spapr->vof = NULL; + } + if (!value) { + return; + } + spapr->vof = g_malloc0(sizeof(*spapr->vof)); +} + static char *spapr_get_ic_mode(Object *obj, Error **errp) { SpaprMachineState *spapr = SPAPR_MACHINE(obj); @@ -3333,6 +3376,11 @@ static void spapr_instance_init(Object *obj) stringify(KERNEL_LOAD_ADDR) " for -kernel is the default"); spapr->kernel_addr = KERNEL_LOAD_ADDR; + + object_property_add_bool(obj, "x-vof", spapr_get_vof, spapr_set_vof); + object_property_set_description(obj, "x-vof", + "Enable Virtual Open Firmware (experimental)"); + /* The machine class defines the default interrupt controller mode */ spapr->irq = smc->irq; object_property_add_str(obj, "ic-mode", spapr_get_ic_mode, @@ -4496,6 +4544,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) XICSFabricClass *xic = XICS_FABRIC_CLASS(oc); InterruptStatsProviderClass *ispc = INTERRUPT_STATS_PROVIDER_CLASS(oc); XiveFabricClass *xfc = XIVE_FABRIC_CLASS(oc); + VofMachineIfClass *vmc = VOF_MACHINE_CLASS(oc); mc->desc = "pSeries Logical Partition (PAPR compliant)"; mc->ignore_boot_device_suffixes = true; @@ -4584,6 +4633,9 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) smc->smp_threads_vsmt = true; smc->nr_xirqs = SPAPR_NR_XIRQS; xfc->match_nvt = spapr_match_nvt; + vmc->client_architecture_support = spapr_vof_client_architecture_support; + vmc->quiesce = spapr_vof_quiesce; + vmc->setprop = spapr_vof_setprop; } static const TypeInfo spapr_machine_info = { @@ -4603,6 +4655,7 @@ static const TypeInfo spapr_machine_info = { { TYPE_XICS_FABRIC }, { TYPE_INTERRUPT_STATS_PROVIDER }, { TYPE_XIVE_FABRIC }, + { TYPE_VOF_MACHINE_IF }, { } }, }; diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index f25014afda..03fc191599 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -1080,7 +1080,7 @@ target_ulong do_client_architecture_support(PowerPCCPU *cpu, SpaprOptionVector *ov1_guest, *ov5_guest; bool guest_radix; bool raw_mode_supported = false; - bool guest_xive; + bool guest_xive, reset_fdt = false; CPUState *cs; void *fdt; uint32_t max_compat = spapr->max_compat_pvr; @@ -1233,8 +1233,8 @@ target_ulong do_client_architecture_support(PowerPCCPU *cpu, spapr_setup_hpt(spapr); } - fdt = spapr_build_fdt(spapr, false, fdt_bufsize); - + reset_fdt = spapr->vof != NULL; + fdt = spapr_build_fdt(spapr, reset_fdt, fdt_bufsize); g_free(spapr->fdt_blob); spapr->fdt_size = fdt_totalsize(fdt); spapr->fdt_initial_size = spapr->fdt_size; @@ -1277,6 +1277,25 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, return ret; } +target_ulong spapr_vof_client_architecture_support(MachineState *ms, + CPUState *cs, + target_ulong ovec_addr) +{ + SpaprMachineState *spapr = SPAPR_MACHINE(ms); + + target_ulong ret = do_client_architecture_support(POWERPC_CPU(cs), spapr, + ovec_addr, FDT_MAX_SIZE); + + /* + * This adds stdout and generates phandles for boottime and CAS FDTs. + * It is alright to update the FDT here as do_client_architecture_support() + * does not pack it. + */ + spapr_vof_client_dt_finalize(spapr, spapr->fdt_blob); + + return ret; +} + static target_ulong h_get_cpu_characteristics(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, diff --git a/hw/ppc/spapr_vof.c b/hw/ppc/spapr_vof.c new file mode 100644 index 0000000000..131a03fec0 --- /dev/null +++ b/hw/ppc/spapr_vof.c @@ -0,0 +1,153 @@ +/* + * SPAPR machine hooks to Virtual Open Firmware, + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qapi/error.h" +#include "hw/ppc/spapr.h" +#include "hw/ppc/spapr_vio.h" +#include "hw/ppc/fdt.h" +#include "hw/ppc/vof.h" +#include "sysemu/sysemu.h" +#include "qom/qom-qobject.h" +#include "trace.h" + +target_ulong spapr_h_vof_client(PowerPCCPU *cpu, SpaprMachineState *spapr, + target_ulong opcode, target_ulong *_args) +{ + int ret = vof_client_call(MACHINE(spapr), spapr->vof, spapr->fdt_blob, + ppc64_phys_to_real(_args[0])); + + if (ret) { + return H_PARAMETER; + } + return H_SUCCESS; +} + +void spapr_vof_client_dt_finalize(SpaprMachineState *spapr, void *fdt) +{ + char *stdout_path = spapr_vio_stdout_path(spapr->vio_bus); + int chosen; + + vof_build_dt(fdt, spapr->vof); + + _FDT(chosen = fdt_path_offset(fdt, "/chosen")); + _FDT(fdt_setprop_string(fdt, chosen, "bootargs", + spapr->vof->bootargs ? : "")); + + /* + * SLOF-less setup requires an open instance of stdout for early + * kernel printk. By now all phandles are settled so we can open + * the default serial console. + */ + if (stdout_path) { + _FDT(vof_client_open_store(fdt, spapr->vof, "/chosen", "stdout", + stdout_path)); + } +} + +void spapr_vof_reset(SpaprMachineState *spapr, void *fdt, + target_ulong *stack_ptr, Error **errp) +{ + Vof *vof = spapr->vof; + + vof_init(vof, spapr->rma_size, errp); + + *stack_ptr = vof_claim(vof, 0, VOF_STACK_SIZE, VOF_STACK_SIZE); + if (*stack_ptr == -1) { + error_setg(errp, "Memory allocation for stack failed"); + return; + } + /* Stack grows downwards plus reserve space for the minimum stack frame */ + *stack_ptr += VOF_STACK_SIZE - 0x20; + + if (spapr->kernel_size && + vof_claim(vof, spapr->kernel_addr, spapr->kernel_size, 0) == -1) { + error_setg(errp, "Memory for kernel is in use"); + return; + } + + if (spapr->initrd_size && + vof_claim(vof, spapr->initrd_base, spapr->initrd_size, 0) == -1) { + error_setg(errp, "Memory for initramdisk is in use"); + return; + } + + spapr_vof_client_dt_finalize(spapr, fdt); + + /* + * At this point the expected allocation map is: + * + * 0..c38 - the initial firmware + * 8000..10000 - stack + * 400000.. - kernel + * 3ea0000.. - initramdisk + * + * We skip writing FDT as nothing expects it; OF client interface is + * going to be used for reading the device tree. + */ +} + +void spapr_vof_quiesce(MachineState *ms) +{ + SpaprMachineState *spapr = SPAPR_MACHINE(ms); + + spapr->fdt_size = fdt_totalsize(spapr->fdt_blob); + spapr->fdt_initial_size = spapr->fdt_size; +} + +bool spapr_vof_setprop(MachineState *ms, const char *path, const char *propname, + void *val, int vallen) +{ + SpaprMachineState *spapr = SPAPR_MACHINE(ms); + + /* + * We only allow changing properties which we know how to update in QEMU + * OR + * the ones which we know that they need to survive during "quiesce". + */ + + if (strcmp(path, "/rtas") == 0) { + if (strcmp(propname, "linux,rtas-base") == 0 || + strcmp(propname, "linux,rtas-entry") == 0) { + /* These need to survive quiesce so let them store in the FDT */ + return true; + } + } + + if (strcmp(path, "/chosen") == 0) { + if (strcmp(propname, "bootargs") == 0) { + Vof *vof = spapr->vof; + + g_free(vof->bootargs); + vof->bootargs = g_strndup(val, vallen); + return true; + } + if (strcmp(propname, "linux,initrd-start") == 0) { + if (vallen == sizeof(uint32_t)) { + spapr->initrd_base = ldl_be_p(val); + return true; + } + if (vallen == sizeof(uint64_t)) { + spapr->initrd_base = ldq_be_p(val); + return true; + } + return false; + } + if (strcmp(propname, "linux,initrd-end") == 0) { + if (vallen == sizeof(uint32_t)) { + spapr->initrd_size = ldl_be_p(val) - spapr->initrd_base; + return true; + } + if (vallen == sizeof(uint64_t)) { + spapr->initrd_size = ldq_be_p(val) - spapr->initrd_base; + return true; + } + return false; + } + } + + return true; +} diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events index 0ba3e40353..6e90a01072 100644 --- a/hw/ppc/trace-events +++ b/hw/ppc/trace-events @@ -71,6 +71,30 @@ spapr_rtas_ibm_configure_connector_invalid(uint32_t index) "DRC index: 0x%"PRIx3 spapr_vio_h_reg_crq(uint64_t reg, uint64_t queue_addr, uint64_t queue_len) "CRQ for dev 0x%" PRIx64 " registered at 0x%" PRIx64 "/0x%" PRIx64 spapr_vio_free_crq(uint32_t reg) "CRQ for dev 0x%" PRIx32 " freed" +# vof.c +vof_error_str_truncated(const char *s, int len) "%s truncated to %d" +vof_error_param(const char *method, int nargscheck, int nretcheck, int nargs, int nret) "%s takes/returns %d/%d, not %d/%d" +vof_error_unknown_service(const char *service, int nargs, int nret) "\"%s\" args=%d rets=%d" +vof_error_unknown_method(const char *method) "\"%s\"" +vof_error_unknown_ihandle_close(uint32_t ih) "ih=0x%x" +vof_error_unknown_path(const char *path) "\"%s\"" +vof_error_write(uint32_t ih) "ih=0x%x" +vof_finddevice(const char *path, uint32_t ph) "\"%s\" => ph=0x%x" +vof_claim(uint32_t virt, uint32_t size, uint32_t align, uint32_t ret) "virt=0x%x size=0x%x align=0x%x => 0x%x" +vof_release(uint32_t virt, uint32_t size, uint32_t ret) "virt=0x%x size=0x%x => 0x%x" +vof_method(uint32_t ihandle, const char *method, uint32_t param, uint32_t ret, uint32_t ret2) "ih=0x%x \"%s\"(0x%x) => 0x%x 0x%x" +vof_getprop(uint32_t ph, const char *prop, uint32_t ret, const char *val) "ph=0x%x \"%s\" => len=%d [%s]" +vof_getproplen(uint32_t ph, const char *prop, uint32_t ret) "ph=0x%x \"%s\" => len=%d" +vof_setprop(uint32_t ph, const char *prop, const char *val, uint32_t vallen, uint32_t ret) "ph=0x%x \"%s\" [%s] len=%d => ret=%d" +vof_open(const char *path, uint32_t ph, uint32_t ih) "%s ph=0x%x => ih=0x%x" +vof_interpret(const char *cmd, uint32_t param1, uint32_t param2, uint32_t ret, uint32_t ret2) "[%s] 0x%x 0x%x => 0x%x 0x%x" +vof_package_to_path(uint32_t ph, const char *tmp, uint32_t ret) "ph=0x%x => %s len=%d" +vof_instance_to_path(uint32_t ih, uint32_t ph, const char *tmp, uint32_t ret) "ih=0x%x ph=0x%x => %s len=%d" +vof_instance_to_package(uint32_t ih, uint32_t ph) "ih=0x%x => ph=0x%x" +vof_write(uint32_t ih, unsigned cb, const char *msg) "ih=0x%x [%u] \"%s\"" +vof_avail(uint64_t start, uint64_t end, uint64_t size) "0x%"PRIx64"..0x%"PRIx64" size=0x%"PRIx64 +vof_claimed(uint64_t start, uint64_t end, uint64_t size) "0x%"PRIx64"..0x%"PRIx64" size=0x%"PRIx64 + # ppc.c ppc_tb_adjust(uint64_t offs1, uint64_t offs2, int64_t diff, int64_t seconds) "adjusted from 0x%"PRIx64" to 0x%"PRIx64", diff %"PRId64" (%"PRId64"s)" diff --git a/hw/ppc/vof.c b/hw/ppc/vof.c new file mode 100644 index 0000000000..47c86e394e --- /dev/null +++ b/hw/ppc/vof.c @@ -0,0 +1,1049 @@ +/* + * QEMU PowerPC Virtual Open Firmware. + * + * This implements client interface from OpenFirmware IEEE1275 on the QEMU + * side to leave only a very basic firmware in the VM. + * + * Copyright (c) 2021 IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/timer.h" +#include "qemu/range.h" +#include "qemu/units.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "exec/ram_addr.h" +#include "exec/address-spaces.h" +#include "hw/ppc/vof.h" +#include "hw/ppc/fdt.h" +#include "sysemu/runstate.h" +#include "qom/qom-qobject.h" +#include "trace.h" + +#include + +/* + * OF 1275 "nextprop" description suggests is it 32 bytes max but + * LoPAPR defines "ibm,query-interrupt-source-number" which is 33 chars long. + */ +#define OF_PROPNAME_LEN_MAX 64 + +#define VOF_MAX_PATH 256 +#define VOF_MAX_SETPROPLEN 2048 +#define VOF_MAX_METHODLEN 256 +#define VOF_MAX_FORTHCODE 256 +#define VOF_VTY_BUF_SIZE 256 + +typedef struct { + uint64_t start; + uint64_t size; +} OfClaimed; + +typedef struct { + char *path; /* the path used to open the instance */ + uint32_t phandle; +} OfInstance; + +static int readstr(hwaddr pa, char *buf, int size) +{ + if (VOF_MEM_READ(pa, buf, size) != MEMTX_OK) { + return -1; + } + if (strnlen(buf, size) == size) { + buf[size - 1] = '\0'; + trace_vof_error_str_truncated(buf, size); + return -1; + } + return 0; +} + +static bool cmpservice(const char *s, unsigned nargs, unsigned nret, + const char *s1, unsigned nargscheck, unsigned nretcheck) +{ + if (strcmp(s, s1)) { + return false; + } + if ((nargscheck && (nargs != nargscheck)) || + (nretcheck && (nret != nretcheck))) { + trace_vof_error_param(s, nargscheck, nretcheck, nargs, nret); + return false; + } + + return true; +} + +static void prop_format(char *tval, int tlen, const void *prop, int len) +{ + int i; + const unsigned char *c; + char *t; + const char bin[] = "..."; + + for (i = 0, c = prop; i < len; ++i, ++c) { + if (*c == '\0' && i == len - 1) { + strncpy(tval, prop, tlen - 1); + return; + } + if (*c < 0x20 || *c >= 0x80) { + break; + } + } + + for (i = 0, c = prop, t = tval; i < len; ++i, ++c) { + if (t >= tval + tlen - sizeof(bin) - 1 - 2 - 1) { + strcpy(t, bin); + return; + } + if (i && i % 4 == 0 && i != len - 1) { + strcat(t, " "); + ++t; + } + t += sprintf(t, "%02X", *c & 0xFF); + } +} + +static int get_path(const void *fdt, int offset, char *buf, int len) +{ + int ret; + + ret = fdt_get_path(fdt, offset, buf, len - 1); + if (ret < 0) { + return ret; + } + + buf[len - 1] = '\0'; + + return strlen(buf) + 1; +} + +static int phandle_to_path(const void *fdt, uint32_t ph, char *buf, int len) +{ + int ret; + + ret = fdt_node_offset_by_phandle(fdt, ph); + if (ret < 0) { + return ret; + } + + return get_path(fdt, ret, buf, len); +} + +static int path_offset(const void *fdt, const char *path) +{ + g_autofree char *p = NULL; + char *at; + + /* + * https://www.devicetree.org/open-firmware/bindings/ppc/release/ppc-2_1.html#HDR16 + * + * "Conversion from numeric representation to text representation shall use + * the lower case forms of the hexadecimal digits in the range a..f, + * suppressing leading zeros". + */ + at = strchr(path, '@'); + if (!at) { + return fdt_path_offset(fdt, path); + } + + p = g_strdup(path); + for (at = at - path + p + 1; *at; ++at) { + *at = tolower(*at); + } + return fdt_path_offset(fdt, p); +} + +static uint32_t vof_finddevice(const void *fdt, uint32_t nodeaddr) +{ + char fullnode[VOF_MAX_PATH]; + uint32_t ret = -1; + int offset; + + if (readstr(nodeaddr, fullnode, sizeof(fullnode))) { + return (uint32_t) ret; + } + + offset = path_offset(fdt, fullnode); + if (offset >= 0) { + ret = fdt_get_phandle(fdt, offset); + } + trace_vof_finddevice(fullnode, ret); + return (uint32_t) ret; +} + +static const void *getprop(const void *fdt, int nodeoff, const char *propname, + int *proplen, bool *write0) +{ + const char *unit, *prop; + const void *ret = fdt_getprop(fdt, nodeoff, propname, proplen); + + if (ret) { + if (write0) { + *write0 = false; + } + return ret; + } + + if (strcmp(propname, "name")) { + return NULL; + } + /* + * We return a value for "name" from path if queried but property does not + * exist. @proplen does not include the unit part in this case. + */ + prop = fdt_get_name(fdt, nodeoff, proplen); + if (!prop) { + *proplen = 0; + return NULL; + } + + unit = memchr(prop, '@', *proplen); + if (unit) { + *proplen = unit - prop; + } + *proplen += 1; + + /* + * Since it might be cut at "@" and there will be no trailing zero + * in the prop buffer, tell the caller to write zero at the end. + */ + if (write0) { + *write0 = true; + } + return prop; +} + +static uint32_t vof_getprop(const void *fdt, uint32_t nodeph, uint32_t pname, + uint32_t valaddr, uint32_t vallen) +{ + char propname[OF_PROPNAME_LEN_MAX + 1]; + uint32_t ret = 0; + int proplen = 0; + const void *prop; + char trval[64] = ""; + int nodeoff = fdt_node_offset_by_phandle(fdt, nodeph); + bool write0; + + if (nodeoff < 0) { + return -1; + } + if (readstr(pname, propname, sizeof(propname))) { + return -1; + } + prop = getprop(fdt, nodeoff, propname, &proplen, &write0); + if (prop) { + const char zero = 0; + int cb = MIN(proplen, vallen); + + if (VOF_MEM_WRITE(valaddr, prop, cb) != MEMTX_OK || + /* if that was "name" with a unit address, overwrite '@' with '0' */ + (write0 && + cb == proplen && + VOF_MEM_WRITE(valaddr + cb - 1, &zero, 1) != MEMTX_OK)) { + ret = -1; + } else { + /* + * OF1275 says: + * "Size is either the actual size of the property, or -1 if name + * does not exist", hence returning proplen instead of cb. + */ + ret = proplen; + /* Do not format a value if tracepoint is silent, for performance */ + if (trace_event_get_state(TRACE_VOF_GETPROP) && + qemu_loglevel_mask(LOG_TRACE)) { + prop_format(trval, sizeof(trval), prop, ret); + } + } + } else { + ret = -1; + } + trace_vof_getprop(nodeph, propname, ret, trval); + + return ret; +} + +static uint32_t vof_getproplen(const void *fdt, uint32_t nodeph, uint32_t pname) +{ + char propname[OF_PROPNAME_LEN_MAX + 1]; + uint32_t ret = 0; + int proplen = 0; + const void *prop; + int nodeoff = fdt_node_offset_by_phandle(fdt, nodeph); + + if (nodeoff < 0) { + return -1; + } + if (readstr(pname, propname, sizeof(propname))) { + return -1; + } + prop = getprop(fdt, nodeoff, propname, &proplen, NULL); + if (prop) { + ret = proplen; + } else { + ret = -1; + } + trace_vof_getproplen(nodeph, propname, ret); + + return ret; +} + +static uint32_t vof_setprop(MachineState *ms, void *fdt, Vof *vof, + uint32_t nodeph, uint32_t pname, + uint32_t valaddr, uint32_t vallen) +{ + char propname[OF_PROPNAME_LEN_MAX + 1]; + uint32_t ret = -1; + int offset; + char trval[64] = ""; + char nodepath[VOF_MAX_PATH] = ""; + Object *vmo = object_dynamic_cast(OBJECT(ms), TYPE_VOF_MACHINE_IF); + g_autofree char *val = NULL; + + if (vallen > VOF_MAX_SETPROPLEN) { + goto trace_exit; + } + if (readstr(pname, propname, sizeof(propname))) { + goto trace_exit; + } + offset = fdt_node_offset_by_phandle(fdt, nodeph); + if (offset < 0) { + goto trace_exit; + } + ret = get_path(fdt, offset, nodepath, sizeof(nodepath)); + if (ret <= 0) { + goto trace_exit; + } + + val = g_malloc0(vallen); + if (VOF_MEM_READ(valaddr, val, vallen) != MEMTX_OK) { + goto trace_exit; + } + + if (vmo) { + VofMachineIfClass *vmc = VOF_MACHINE_GET_CLASS(vmo); + + if (vmc->setprop && + !vmc->setprop(ms, nodepath, propname, val, vallen)) { + goto trace_exit; + } + } + + ret = fdt_setprop(fdt, offset, propname, val, vallen); + if (ret) { + goto trace_exit; + } + + if (trace_event_get_state(TRACE_VOF_SETPROP) && + qemu_loglevel_mask(LOG_TRACE)) { + prop_format(trval, sizeof(trval), val, vallen); + } + ret = vallen; + +trace_exit: + trace_vof_setprop(nodeph, propname, trval, vallen, ret); + + return ret; +} + +static uint32_t vof_nextprop(const void *fdt, uint32_t phandle, + uint32_t prevaddr, uint32_t nameaddr) +{ + int offset, nodeoff = fdt_node_offset_by_phandle(fdt, phandle); + char prev[OF_PROPNAME_LEN_MAX + 1]; + const char *tmp; + + if (readstr(prevaddr, prev, sizeof(prev))) { + return -1; + } + + fdt_for_each_property_offset(offset, fdt, nodeoff) { + if (!fdt_getprop_by_offset(fdt, offset, &tmp, NULL)) { + return 0; + } + if (prev[0] == '\0' || strcmp(prev, tmp) == 0) { + if (prev[0] != '\0') { + offset = fdt_next_property_offset(fdt, offset); + if (offset < 0) { + return 0; + } + } + if (!fdt_getprop_by_offset(fdt, offset, &tmp, NULL)) { + return 0; + } + + if (VOF_MEM_WRITE(nameaddr, tmp, strlen(tmp) + 1) != MEMTX_OK) { + return -1; + } + return 1; + } + } + + return 0; +} + +static uint32_t vof_peer(const void *fdt, uint32_t phandle) +{ + int ret; + + if (phandle == 0) { + ret = fdt_path_offset(fdt, "/"); + } else { + ret = fdt_next_subnode(fdt, fdt_node_offset_by_phandle(fdt, phandle)); + } + + if (ret < 0) { + ret = 0; + } else { + ret = fdt_get_phandle(fdt, ret); + } + + return ret; +} + +static uint32_t vof_child(const void *fdt, uint32_t phandle) +{ + int ret = fdt_first_subnode(fdt, fdt_node_offset_by_phandle(fdt, phandle)); + + if (ret < 0) { + ret = 0; + } else { + ret = fdt_get_phandle(fdt, ret); + } + + return ret; +} + +static uint32_t vof_parent(const void *fdt, uint32_t phandle) +{ + int ret = fdt_parent_offset(fdt, fdt_node_offset_by_phandle(fdt, phandle)); + + if (ret < 0) { + ret = 0; + } else { + ret = fdt_get_phandle(fdt, ret); + } + + return ret; +} + +static uint32_t vof_do_open(void *fdt, Vof *vof, int offset, const char *path) +{ + uint32_t ret = -1; + OfInstance *inst = NULL; + + if (vof->of_instance_last == 0xFFFFFFFF) { + /* We do not recycle ihandles yet */ + goto trace_exit; + } + + inst = g_new0(OfInstance, 1); + inst->phandle = fdt_get_phandle(fdt, offset); + g_assert(inst->phandle); + ++vof->of_instance_last; + + inst->path = g_strdup(path); + g_hash_table_insert(vof->of_instances, + GINT_TO_POINTER(vof->of_instance_last), + inst); + ret = vof->of_instance_last; + +trace_exit: + trace_vof_open(path, inst ? inst->phandle : 0, ret); + + return ret; +} + +uint32_t vof_client_open_store(void *fdt, Vof *vof, const char *nodename, + const char *prop, const char *path) +{ + int node = fdt_path_offset(fdt, nodename); + int inst, offset; + + offset = fdt_path_offset(fdt, path); + if (offset < 0) { + trace_vof_error_unknown_path(path); + return offset; + } + + inst = vof_do_open(fdt, vof, offset, path); + + return fdt_setprop_cell(fdt, node, prop, inst); +} + +static uint32_t vof_open(void *fdt, Vof *vof, uint32_t pathaddr) +{ + char path[VOF_MAX_PATH]; + int offset; + + if (readstr(pathaddr, path, sizeof(path))) { + return -1; + } + + offset = path_offset(fdt, path); + if (offset < 0) { + trace_vof_error_unknown_path(path); + return offset; + } + + return vof_do_open(fdt, vof, offset, path); +} + +static void vof_close(Vof *vof, uint32_t ihandle) +{ + if (!g_hash_table_remove(vof->of_instances, GINT_TO_POINTER(ihandle))) { + trace_vof_error_unknown_ihandle_close(ihandle); + } +} + +static uint32_t vof_instance_to_package(Vof *vof, uint32_t ihandle) +{ + gpointer instp = g_hash_table_lookup(vof->of_instances, + GINT_TO_POINTER(ihandle)); + uint32_t ret = -1; + + if (instp) { + ret = ((OfInstance *)instp)->phandle; + } + trace_vof_instance_to_package(ihandle, ret); + + return ret; +} + +static uint32_t vof_package_to_path(const void *fdt, uint32_t phandle, + uint32_t buf, uint32_t len) +{ + uint32_t ret = -1; + char tmp[VOF_MAX_PATH] = ""; + + ret = phandle_to_path(fdt, phandle, tmp, sizeof(tmp)); + if (ret > 0) { + if (VOF_MEM_WRITE(buf, tmp, ret) != MEMTX_OK) { + ret = -1; + } + } + + trace_vof_package_to_path(phandle, tmp, ret); + + return ret; +} + +static uint32_t vof_instance_to_path(void *fdt, Vof *vof, uint32_t ihandle, + uint32_t buf, uint32_t len) +{ + uint32_t ret = -1; + uint32_t phandle = vof_instance_to_package(vof, ihandle); + char tmp[VOF_MAX_PATH] = ""; + + if (phandle != -1) { + ret = phandle_to_path(fdt, phandle, tmp, sizeof(tmp)); + if (ret > 0) { + if (VOF_MEM_WRITE(buf, tmp, ret) != MEMTX_OK) { + ret = -1; + } + } + } + trace_vof_instance_to_path(ihandle, phandle, tmp, ret); + + return ret; +} + +static uint32_t vof_write(Vof *vof, uint32_t ihandle, uint32_t buf, + uint32_t len) +{ + char tmp[VOF_VTY_BUF_SIZE]; + unsigned cb; + OfInstance *inst = (OfInstance *) + g_hash_table_lookup(vof->of_instances, GINT_TO_POINTER(ihandle)); + + if (!inst) { + trace_vof_error_write(ihandle); + return -1; + } + + for ( ; len > 0; len -= cb) { + cb = MIN(len, sizeof(tmp) - 1); + if (VOF_MEM_READ(buf, tmp, cb) != MEMTX_OK) { + return -1; + } + + /* FIXME: there is no backend(s) yet so just call a trace */ + if (trace_event_get_state(TRACE_VOF_WRITE) && + qemu_loglevel_mask(LOG_TRACE)) { + tmp[cb] = '\0'; + trace_vof_write(ihandle, cb, tmp); + } + } + + return len; +} + +static void vof_claimed_dump(GArray *claimed) +{ + int i; + OfClaimed c; + + if (trace_event_get_state(TRACE_VOF_CLAIMED) && + qemu_loglevel_mask(LOG_TRACE)) { + + for (i = 0; i < claimed->len; ++i) { + c = g_array_index(claimed, OfClaimed, i); + trace_vof_claimed(c.start, c.start + c.size, c.size); + } + } +} + +static bool vof_claim_avail(GArray *claimed, uint64_t virt, uint64_t size) +{ + int i; + OfClaimed c; + + for (i = 0; i < claimed->len; ++i) { + c = g_array_index(claimed, OfClaimed, i); + if (ranges_overlap(c.start, c.size, virt, size)) { + return false; + } + } + + return true; +} + +static void vof_claim_add(GArray *claimed, uint64_t virt, uint64_t size) +{ + OfClaimed newclaim; + + newclaim.start = virt; + newclaim.size = size; + g_array_append_val(claimed, newclaim); +} + +static gint of_claimed_compare_func(gconstpointer a, gconstpointer b) +{ + return ((OfClaimed *)a)->start - ((OfClaimed *)b)->start; +} + +static void vof_dt_memory_available(void *fdt, GArray *claimed, uint64_t base) +{ + int i, n, offset, proplen = 0, sc, ac; + target_ulong mem0_end; + const uint8_t *mem0_reg; + g_autofree uint8_t *avail = NULL; + uint8_t *availcur; + + if (!fdt || !claimed) { + return; + } + + offset = fdt_path_offset(fdt, "/"); + _FDT(offset); + ac = fdt_address_cells(fdt, offset); + g_assert(ac == 1 || ac == 2); + sc = fdt_size_cells(fdt, offset); + g_assert(sc == 1 || sc == 2); + + offset = fdt_path_offset(fdt, "/memory@0"); + _FDT(offset); + + mem0_reg = fdt_getprop(fdt, offset, "reg", &proplen); + g_assert(mem0_reg && proplen == sizeof(uint32_t) * (ac + sc)); + if (sc == 2) { + mem0_end = be64_to_cpu(*(uint64_t *)(mem0_reg + sizeof(uint32_t) * ac)); + } else { + mem0_end = be32_to_cpu(*(uint32_t *)(mem0_reg + sizeof(uint32_t) * ac)); + } + + g_array_sort(claimed, of_claimed_compare_func); + vof_claimed_dump(claimed); + + /* + * VOF resides in the first page so we do not need to check if there is + * available memory before the first claimed block + */ + g_assert(claimed->len && (g_array_index(claimed, OfClaimed, 0).start == 0)); + + avail = g_malloc0(sizeof(uint32_t) * (ac + sc) * claimed->len); + for (i = 0, n = 0, availcur = avail; i < claimed->len; ++i) { + OfClaimed c = g_array_index(claimed, OfClaimed, i); + uint64_t start, size; + + start = c.start + c.size; + if (i < claimed->len - 1) { + OfClaimed cn = g_array_index(claimed, OfClaimed, i + 1); + + size = cn.start - start; + } else { + size = mem0_end - start; + } + + if (ac == 2) { + *(uint64_t *) availcur = cpu_to_be64(start); + } else { + *(uint32_t *) availcur = cpu_to_be32(start); + } + availcur += sizeof(uint32_t) * ac; + if (sc == 2) { + *(uint64_t *) availcur = cpu_to_be64(size); + } else { + *(uint32_t *) availcur = cpu_to_be32(size); + } + availcur += sizeof(uint32_t) * sc; + + if (size) { + trace_vof_avail(c.start + c.size, c.start + c.size + size, size); + ++n; + } + } + _FDT((fdt_setprop(fdt, offset, "available", avail, availcur - avail))); +} + +/* + * OF1275: + * "Allocates size bytes of memory. If align is zero, the allocated range + * begins at the virtual address virt. Otherwise, an aligned address is + * automatically chosen and the input argument virt is ignored". + * + * In other words, exactly one of @virt and @align is non-zero. + */ +uint64_t vof_claim(Vof *vof, uint64_t virt, uint64_t size, + uint64_t align) +{ + uint64_t ret; + + if (size == 0) { + ret = -1; + } else if (align == 0) { + if (!vof_claim_avail(vof->claimed, virt, size)) { + ret = -1; + } else { + ret = virt; + } + } else { + vof->claimed_base = QEMU_ALIGN_UP(vof->claimed_base, align); + while (1) { + if (vof->claimed_base >= vof->top_addr) { + error_report("Out of RMA memory for the OF client"); + return -1; + } + if (vof_claim_avail(vof->claimed, vof->claimed_base, size)) { + break; + } + vof->claimed_base += size; + } + ret = vof->claimed_base; + } + + if (ret != -1) { + vof->claimed_base = MAX(vof->claimed_base, ret + size); + vof_claim_add(vof->claimed, ret, size); + } + trace_vof_claim(virt, size, align, ret); + + return ret; +} + +static uint32_t vof_release(Vof *vof, uint64_t virt, uint64_t size) +{ + uint32_t ret = -1; + int i; + GArray *claimed = vof->claimed; + OfClaimed c; + + for (i = 0; i < claimed->len; ++i) { + c = g_array_index(claimed, OfClaimed, i); + if (c.start == virt && c.size == size) { + g_array_remove_index(claimed, i); + ret = 0; + break; + } + } + + trace_vof_release(virt, size, ret); + + return ret; +} + +static void vof_instantiate_rtas(Error **errp) +{ + error_setg(errp, "The firmware should have instantiated RTAS"); +} + +static uint32_t vof_call_method(MachineState *ms, Vof *vof, uint32_t methodaddr, + uint32_t ihandle, uint32_t param1, + uint32_t param2, uint32_t param3, + uint32_t param4, uint32_t *ret2) +{ + uint32_t ret = -1; + char method[VOF_MAX_METHODLEN] = ""; + OfInstance *inst; + + if (!ihandle) { + goto trace_exit; + } + + inst = (OfInstance *)g_hash_table_lookup(vof->of_instances, + GINT_TO_POINTER(ihandle)); + if (!inst) { + goto trace_exit; + } + + if (readstr(methodaddr, method, sizeof(method))) { + goto trace_exit; + } + + if (strcmp(inst->path, "/") == 0) { + if (strcmp(method, "ibm,client-architecture-support") == 0) { + Object *vmo = object_dynamic_cast(OBJECT(ms), TYPE_VOF_MACHINE_IF); + + if (vmo) { + VofMachineIfClass *vmc = VOF_MACHINE_GET_CLASS(vmo); + + g_assert(vmc->client_architecture_support); + ret = vmc->client_architecture_support(ms, first_cpu, param1); + } + + *ret2 = 0; + } + } else if (strcmp(inst->path, "/rtas") == 0) { + if (strcmp(method, "instantiate-rtas") == 0) { + vof_instantiate_rtas(&error_fatal); + ret = 0; + *ret2 = param1; /* rtas-base */ + } + } else { + trace_vof_error_unknown_method(method); + } + +trace_exit: + trace_vof_method(ihandle, method, param1, ret, *ret2); + + return ret; +} + +static uint32_t vof_call_interpret(uint32_t cmdaddr, uint32_t param1, + uint32_t param2, uint32_t *ret2) +{ + uint32_t ret = -1; + char cmd[VOF_MAX_FORTHCODE] = ""; + + /* No interpret implemented so just call a trace */ + readstr(cmdaddr, cmd, sizeof(cmd)); + trace_vof_interpret(cmd, param1, param2, ret, *ret2); + + return ret; +} + +static void vof_quiesce(MachineState *ms, void *fdt, Vof *vof) +{ + Object *vmo = object_dynamic_cast(OBJECT(ms), TYPE_VOF_MACHINE_IF); + /* After "quiesce", no change is expected to the FDT, pack FDT to ensure */ + int rc = fdt_pack(fdt); + + assert(rc == 0); + + if (vmo) { + VofMachineIfClass *vmc = VOF_MACHINE_GET_CLASS(vmo); + + if (vmc->quiesce) { + vmc->quiesce(ms); + } + } + + vof_claimed_dump(vof->claimed); +} + +static uint32_t vof_client_handle(MachineState *ms, void *fdt, Vof *vof, + const char *service, + uint32_t *args, unsigned nargs, + uint32_t *rets, unsigned nrets) +{ + uint32_t ret = 0; + + /* @nrets includes the value which this function returns */ +#define cmpserv(s, a, r) \ + cmpservice(service, nargs, nrets, (s), (a), (r)) + + if (cmpserv("finddevice", 1, 1)) { + ret = vof_finddevice(fdt, args[0]); + } else if (cmpserv("getprop", 4, 1)) { + ret = vof_getprop(fdt, args[0], args[1], args[2], args[3]); + } else if (cmpserv("getproplen", 2, 1)) { + ret = vof_getproplen(fdt, args[0], args[1]); + } else if (cmpserv("setprop", 4, 1)) { + ret = vof_setprop(ms, fdt, vof, args[0], args[1], args[2], args[3]); + } else if (cmpserv("nextprop", 3, 1)) { + ret = vof_nextprop(fdt, args[0], args[1], args[2]); + } else if (cmpserv("peer", 1, 1)) { + ret = vof_peer(fdt, args[0]); + } else if (cmpserv("child", 1, 1)) { + ret = vof_child(fdt, args[0]); + } else if (cmpserv("parent", 1, 1)) { + ret = vof_parent(fdt, args[0]); + } else if (cmpserv("open", 1, 1)) { + ret = vof_open(fdt, vof, args[0]); + } else if (cmpserv("close", 1, 0)) { + vof_close(vof, args[0]); + } else if (cmpserv("instance-to-package", 1, 1)) { + ret = vof_instance_to_package(vof, args[0]); + } else if (cmpserv("package-to-path", 3, 1)) { + ret = vof_package_to_path(fdt, args[0], args[1], args[2]); + } else if (cmpserv("instance-to-path", 3, 1)) { + ret = vof_instance_to_path(fdt, vof, args[0], args[1], args[2]); + } else if (cmpserv("write", 3, 1)) { + ret = vof_write(vof, args[0], args[1], args[2]); + } else if (cmpserv("claim", 3, 1)) { + ret = vof_claim(vof, args[0], args[1], args[2]); + if (ret != -1) { + vof_dt_memory_available(fdt, vof->claimed, vof->claimed_base); + } + } else if (cmpserv("release", 2, 0)) { + ret = vof_release(vof, args[0], args[1]); + if (ret != -1) { + vof_dt_memory_available(fdt, vof->claimed, vof->claimed_base); + } + } else if (cmpserv("call-method", 0, 0)) { + ret = vof_call_method(ms, vof, args[0], args[1], args[2], args[3], + args[4], args[5], rets); + } else if (cmpserv("interpret", 0, 0)) { + ret = vof_call_interpret(args[0], args[1], args[2], rets); + } else if (cmpserv("milliseconds", 0, 1)) { + ret = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); + } else if (cmpserv("quiesce", 0, 0)) { + vof_quiesce(ms, fdt, vof); + } else if (cmpserv("exit", 0, 0)) { + error_report("Stopped as the VM requested \"exit\""); + vm_stop(RUN_STATE_PAUSED); + } else { + trace_vof_error_unknown_service(service, nargs, nrets); + ret = -1; + } + + return ret; +} + +/* Defined as Big Endian */ +struct prom_args { + uint32_t service; + uint32_t nargs; + uint32_t nret; + uint32_t args[10]; +} QEMU_PACKED; + +int vof_client_call(MachineState *ms, Vof *vof, void *fdt, + target_ulong args_real) +{ + struct prom_args args_be; + uint32_t args[ARRAY_SIZE(args_be.args)]; + uint32_t rets[ARRAY_SIZE(args_be.args)] = { 0 }, ret; + char service[64]; + unsigned nargs, nret, i; + + if (VOF_MEM_READ(args_real, &args_be, sizeof(args_be)) != MEMTX_OK) { + return -EINVAL; + } + nargs = be32_to_cpu(args_be.nargs); + if (nargs >= ARRAY_SIZE(args_be.args)) { + return -EINVAL; + } + + if (VOF_MEM_READ(be32_to_cpu(args_be.service), service, sizeof(service)) != + MEMTX_OK) { + return -EINVAL; + } + if (strnlen(service, sizeof(service)) == sizeof(service)) { + /* Too long service name */ + return -EINVAL; + } + + for (i = 0; i < nargs; ++i) { + args[i] = be32_to_cpu(args_be.args[i]); + } + + nret = be32_to_cpu(args_be.nret); + ret = vof_client_handle(ms, fdt, vof, service, args, nargs, rets, nret); + if (!nret) { + return 0; + } + + args_be.args[nargs] = cpu_to_be32(ret); + for (i = 1; i < nret; ++i) { + args_be.args[nargs + i] = cpu_to_be32(rets[i - 1]); + } + + if (VOF_MEM_WRITE(args_real + offsetof(struct prom_args, args[nargs]), + args_be.args + nargs, sizeof(args_be.args[0]) * nret) != + MEMTX_OK) { + return -EINVAL; + } + + return 0; +} + +static void vof_instance_free(gpointer data) +{ + OfInstance *inst = (OfInstance *)data; + + g_free(inst->path); + g_free(inst); +} + +void vof_init(Vof *vof, uint64_t top_addr, Error **errp) +{ + vof_cleanup(vof); + + vof->of_instances = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, vof_instance_free); + vof->claimed = g_array_new(false, false, sizeof(OfClaimed)); + + /* Keep allocations in 32bit as CLI ABI can only return cells==32bit */ + vof->top_addr = MIN(top_addr, 4 * GiB); + if (vof_claim(vof, 0, vof->fw_size, 0) == -1) { + error_setg(errp, "Memory for firmware is in use"); + } +} + +void vof_cleanup(Vof *vof) +{ + if (vof->claimed) { + g_array_unref(vof->claimed); + } + if (vof->of_instances) { + g_hash_table_unref(vof->of_instances); + } + vof->claimed = NULL; + vof->of_instances = NULL; +} + +void vof_build_dt(void *fdt, Vof *vof) +{ + uint32_t phandle = fdt_get_max_phandle(fdt); + int offset, proplen = 0; + const void *prop; + + /* Assign phandles to nodes without predefined phandles (like XICS/XIVE) */ + for (offset = fdt_next_node(fdt, -1, NULL); + offset >= 0; + offset = fdt_next_node(fdt, offset, NULL)) { + prop = fdt_getprop(fdt, offset, "phandle", &proplen); + if (prop) { + continue; + } + ++phandle; + _FDT(fdt_setprop_cell(fdt, offset, "phandle", phandle)); + } + + vof_dt_memory_available(fdt, vof->claimed, vof->claimed_base); +} + +static const TypeInfo vof_machine_if_info = { + .name = TYPE_VOF_MACHINE_IF, + .parent = TYPE_INTERFACE, + .class_size = sizeof(VofMachineIfClass), +}; + +static void vof_machine_if_register_types(void) +{ + type_register_static(&vof_machine_if_info); +} +type_init(vof_machine_if_register_types) diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 5697327e4c..1e63f33e9a 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -12,6 +12,7 @@ #include "hw/ppc/spapr_xive.h" /* For SpaprXive */ #include "hw/ppc/xics.h" /* For ICSState */ #include "hw/ppc/spapr_tpm_proxy.h" +#include "hw/ppc/vof.h" struct SpaprVioBus; struct SpaprPhbState; @@ -180,6 +181,7 @@ struct SpaprMachineState { uint64_t kernel_addr; uint32_t initrd_base; long initrd_size; + Vof *vof; uint64_t rtc_offset; /* Now used only during incoming migration */ struct PPCTimebase tb; bool has_graphics; @@ -558,7 +560,9 @@ struct SpaprMachineState { /* Client Architecture support */ #define KVMPPC_H_CAS (KVMPPC_HCALL_BASE + 0x2) #define KVMPPC_H_UPDATE_DT (KVMPPC_HCALL_BASE + 0x3) -#define KVMPPC_HCALL_MAX KVMPPC_H_UPDATE_DT +/* 0x4 was used for KVMPPC_H_UPDATE_PHANDLE in SLOF */ +#define KVMPPC_H_VOF_CLIENT (KVMPPC_HCALL_BASE + 0x5) +#define KVMPPC_HCALL_MAX KVMPPC_H_VOF_CLIENT /* * The hcall range 0xEF00 to 0xEF80 is reserved for use in facilitating @@ -956,4 +960,17 @@ bool spapr_check_pagesize(SpaprMachineState *spapr, hwaddr pagesize, void spapr_set_all_lpcrs(target_ulong value, target_ulong mask); hwaddr spapr_get_rtas_addr(void); bool spapr_memory_hot_unplug_supported(SpaprMachineState *spapr); + +void spapr_vof_reset(SpaprMachineState *spapr, void *fdt, + target_ulong *stack_ptr, Error **errp); +void spapr_vof_quiesce(MachineState *ms); +bool spapr_vof_setprop(MachineState *ms, const char *path, const char *propname, + void *val, int vallen); +target_ulong spapr_h_vof_client(PowerPCCPU *cpu, SpaprMachineState *spapr, + target_ulong opcode, target_ulong *args); +target_ulong spapr_vof_client_architecture_support(MachineState *ms, + CPUState *cs, + target_ulong ovec_addr); +void spapr_vof_client_dt_finalize(SpaprMachineState *spapr, void *fdt); + #endif /* HW_SPAPR_H */ diff --git a/include/hw/ppc/vof.h b/include/hw/ppc/vof.h new file mode 100644 index 0000000000..640be46163 --- /dev/null +++ b/include/hw/ppc/vof.h @@ -0,0 +1,58 @@ +/* + * Virtual Open Firmware + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef HW_VOF_H +#define HW_VOF_H + +typedef struct Vof { + uint64_t top_addr; /* copied from rma_size */ + GArray *claimed; /* array of SpaprOfClaimed */ + uint64_t claimed_base; + GHashTable *of_instances; /* ihandle -> SpaprOfInstance */ + uint32_t of_instance_last; + char *bootargs; + long fw_size; +} Vof; + +int vof_client_call(MachineState *ms, Vof *vof, void *fdt, + target_ulong args_real); +uint64_t vof_claim(Vof *vof, uint64_t virt, uint64_t size, uint64_t align); +void vof_init(Vof *vof, uint64_t top_addr, Error **errp); +void vof_cleanup(Vof *vof); +void vof_build_dt(void *fdt, Vof *vof); +uint32_t vof_client_open_store(void *fdt, Vof *vof, const char *nodename, + const char *prop, const char *path); + +#define TYPE_VOF_MACHINE_IF "vof-machine-if" + +typedef struct VofMachineIfClass VofMachineIfClass; +DECLARE_CLASS_CHECKERS(VofMachineIfClass, VOF_MACHINE, TYPE_VOF_MACHINE_IF) + +struct VofMachineIfClass { + InterfaceClass parent; + target_ulong (*client_architecture_support)(MachineState *ms, CPUState *cs, + target_ulong vec); + void (*quiesce)(MachineState *ms); + bool (*setprop)(MachineState *ms, const char *path, const char *propname, + void *val, int vallen); +}; + +/* + * Initial stack size is from + * https://www.devicetree.org/open-firmware/bindings/ppc/release/ppc-2_1.html#REF27292 + * + * "Client programs shall be invoked with a valid stack pointer (r1) with + * at least 32K bytes of memory available for stack growth". + */ +#define VOF_STACK_SIZE 0x8000 + +#define VOF_MEM_READ(pa, buf, size) \ + address_space_read(&address_space_memory, \ + (pa), MEMTXATTRS_UNSPECIFIED, (buf), (size)) +#define VOF_MEM_WRITE(pa, buf, size) \ + address_space_write(&address_space_memory, \ + (pa), MEMTXATTRS_UNSPECIFIED, (buf), (size)) + +#endif /* HW_VOF_H */ diff --git a/pc-bios/README b/pc-bios/README index c101c9a04f..d344e3bc1b 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -16,6 +16,10 @@ https://github.com/aik/SLOF, and the image currently in qemu is built from git tag qemu-slof-20210217. +- VOF (Virtual Open Firmware) is a minimalistic firmware to work with + -machine pseries,x-vof=on. When enabled, the firmware acts as a slim shim and + QEMU implements parts of the IEEE 1275 Open Firmware interface. + - sgabios (the Serial Graphics Adapter option ROM) provides a means for legacy x86 software to communicate with an attached serial console as if a video card were attached. The master sources reside in a subversion diff --git a/pc-bios/vof-nvram.bin b/pc-bios/vof-nvram.bin new file mode 100644 index 0000000000000000000000000000000000000000..d183901cf980a91d81c4348bb20487c7bb62a2ec GIT binary patch literal 16384 zcmeI%Jx;?g6bEpZJ8*)oSZeqZi&Z2pKnD)sI4{AHlNb4;RW}a70XPHaW57uo=-#R7 zKSLBhJJ0sdixY3IuY@hzo0r$OmE%T;XE9uh@s1k=AOHafKmY;|fB*y_009U<00Izz z00bZa0SG_<0uX=z1Rwwb2tWV=XCbip6d#B4{{rX#XR%}$Bm^J;0SG|gWP$!?Aq=-I zcT+0Ix{{?1q>9J8r+eW^JK1tYYZZMWQCUwW%0S*~w^p@wfkX-kxMAEA<~5@*g)@mb%KD5!;O~8c)>8rRQBx55=trhk#+1+T3J_ zaf*G4vZAduqy$qda{``6Gnc2DQg7B^s4f5i literal 0 HcmV?d00001 diff --git a/pc-bios/vof.bin b/pc-bios/vof.bin new file mode 100755 index 0000000000000000000000000000000000000000..1ec670be82134adcb5ae128732aff6e371281360 GIT binary patch literal 3784 zcmeHIL1-Lh6n>lC=%gLW9QLr#l}v1e-6f$B_K?xg-PA2?k`e+EC^WLWX18WB$*##L zqwKgdDcGd6vY{31LDNGBdJsJHpr=+43D`ppJ*-fhdMkMGXwf=;Z|0vS*)$~>4}u-| znVJ88^S<}K_ue-||Lg{vfz^9((;= zR2{WQtrd?N131V}{??u$da-4X{5^achQIqYsco$hpzbT*Q3SrDwbWtbHvOlgK%SoO^cXd?|*UBR(E-jM#A^vwOq<4rW;PMn9Gb2(NRw{|F-PFs(0bb%9Qj!*~J zMtsklfRFZNVH1L%$aq|N#Y_x`wK#mF!RZi@e~!HX+t|YtwmXGw%(G=X=D<8QVxX>l z2dnGFJDe|YCH6Ps{Pq5JZVMJ4pv;#&V8o>5r(pnIfy1qs3;O8qF2@w0V=RH#g~vrbA!<)JfkU)-x{0DVo4t z!FwKe?!(}<6s4IP+y_y^CiXM9^LbYE4eCh6CD_F}==njLy1{dn-?a`3j7!g*xYkK& zzc#RC{~2wFLWnP8do_~yTBLIv5FrpsDqtCcEMIHy#vkP|_$JbBvVi^4KtSOcQ zE#1No0bdpPFYFWlmY9VMW}zRmFtB?TE?B^i0lx>c&-;?I4*A3P=Gx!>2`Qf)*dr_D4Q|i9q>n*T-edRfc z!tYierf}xOwsF`R?I&<6hD_@Vt{_MdYHumUUSbnz)YdSyOlLx7+tq>$j(G_WMujh?uservice)) { + return -1; + } + + if (strcmp("instantiate-rtas", (void *)(unsigned long)pargs->args[0])) { + return -1; + } + + rtas = ci_finddevice("/rtas"); + /* rtas-size is set by QEMU depending of FWNMI support */ + ci_getprop(rtas, "rtas-size", &rtassize, sizeof(rtassize)); + if (rtassize < hv_rtas_size) { + return -1; + } + + rtasbase = (void *)(unsigned long) pargs->args[2]; + + memcpy(rtasbase, hv_rtas, hv_rtas_size); + pargs->args[pargs->nargs] = 0; + pargs->args[pargs->nargs + 1] = pargs->args[2]; + + return 0; +} + +void prom_entry(uint32_t args) +{ + if (prom_handle((void *)(unsigned long) args)) { + ci_entry(args); + } +} + +static int call_ci(const char *service, int nargs, int nret, ...) +{ + int i; + struct prom_args args; + va_list list; + + args.service = ADDR(service); + args.nargs = nargs; + args.nret = nret; + + va_start(list, nret); + for (i = 0; i < nargs; i++) { + args.args[i] = va_arg(list, prom_arg_t); + } + va_end(list); + + for (i = 0; i < nret; i++) { + args.args[nargs + i] = 0; + } + + if (ci_entry((uint32_t)(&args)) < 0) { + return PROM_ERROR; + } + + return (nret > 0) ? args.args[nargs] : 0; +} + +void ci_panic(const char *str) +{ + call_ci("exit", 0, 0); +} + +phandle ci_finddevice(const char *path) +{ + return call_ci("finddevice", 1, 1, path); +} + +uint32_t ci_getprop(phandle ph, const char *propname, void *prop, int len) +{ + return call_ci("getprop", 4, 1, ph, propname, prop, len); +} diff --git a/pc-bios/vof/entry.S b/pc-bios/vof/entry.S new file mode 100644 index 0000000000..10a101fb6d --- /dev/null +++ b/pc-bios/vof/entry.S @@ -0,0 +1,49 @@ +#define LOAD32(rn, name) \ + lis rn,name##@h; \ + ori rn,rn,name##@l + +#define ENTRY(func_name) \ + .text; \ + .align 2; \ + .globl .func_name; \ + .func_name: \ + .globl func_name; \ + func_name: + +#define KVMPPC_HCALL_BASE 0xf000 +#define KVMPPC_H_RTAS (KVMPPC_HCALL_BASE + 0x0) +#define KVMPPC_H_VOF_CLIENT (KVMPPC_HCALL_BASE + 0x5) + + . = 0x100 /* Do exactly as SLOF does */ + +ENTRY(_start) + LOAD32(2, __toc_start) + b entry_c + +ENTRY(_prom_entry) + LOAD32(2, __toc_start) + stwu %r1,-112(%r1) + stw %r31,104(%r1) + mflr %r31 + bl prom_entry + nop + mtlr %r31 + lwz %r31,104(%r1) + addi %r1,%r1,112 + blr + +ENTRY(ci_entry) + mr 4,3 + LOAD32(3,KVMPPC_H_VOF_CLIENT) + sc 1 + blr + +/* This is the actual RTAS blob copied to the OS at instantiate-rtas */ +ENTRY(hv_rtas) + mr %r4,%r3 + LOAD32(3,KVMPPC_H_RTAS) + sc 1 + blr + .globl hv_rtas_size +hv_rtas_size: + .long . - hv_rtas; diff --git a/pc-bios/vof/libc.c b/pc-bios/vof/libc.c new file mode 100644 index 0000000000..00c10e6e7d --- /dev/null +++ b/pc-bios/vof/libc.c @@ -0,0 +1,92 @@ +#include "vof.h" + +int strlen(const char *s) +{ + int len = 0; + + while (*s != 0) { + len += 1; + s += 1; + } + + return len; +} + +int strcmp(const char *s1, const char *s2) +{ + while (*s1 != 0 && *s2 != 0) { + if (*s1 != *s2) { + break; + } + s1 += 1; + s2 += 1; + } + + return *s1 - *s2; +} + +void *memcpy(void *dest, const void *src, size_t n) +{ + char *cdest; + const char *csrc = src; + + cdest = dest; + while (n-- > 0) { + *cdest++ = *csrc++; + } + + return dest; +} + +int memcmp(const void *ptr1, const void *ptr2, size_t n) +{ + const unsigned char *p1 = ptr1; + const unsigned char *p2 = ptr2; + + while (n-- > 0) { + if (*p1 != *p2) { + return *p1 - *p2; + } + p1 += 1; + p2 += 1; + } + + return 0; +} + +void *memmove(void *dest, const void *src, size_t n) +{ + char *cdest; + const char *csrc; + int i; + + /* Do the buffers overlap in a bad way? */ + if (src < dest && src + n >= dest) { + /* Copy from end to start */ + cdest = dest + n - 1; + csrc = src + n - 1; + for (i = 0; i < n; i++) { + *cdest-- = *csrc--; + } + } else { + /* Normal copy is possible */ + cdest = dest; + csrc = src; + for (i = 0; i < n; i++) { + *cdest++ = *csrc++; + } + } + + return dest; +} + +void *memset(void *dest, int c, size_t size) +{ + unsigned char *d = (unsigned char *)dest; + + while (size-- > 0) { + *d++ = (unsigned char)c; + } + + return dest; +} diff --git a/pc-bios/vof/main.c b/pc-bios/vof/main.c new file mode 100644 index 0000000000..9fc30d2d09 --- /dev/null +++ b/pc-bios/vof/main.c @@ -0,0 +1,21 @@ +#include "vof.h" + +void do_boot(unsigned long addr, unsigned long _r3, unsigned long _r4) +{ + register unsigned long r3 __asm__("r3") = _r3; + register unsigned long r4 __asm__("r4") = _r4; + register unsigned long r5 __asm__("r5") = (unsigned long) _prom_entry; + + ((client *)(uint32_t)addr)(); +} + +void entry_c(void) +{ + register unsigned long r3 __asm__("r3"); + register unsigned long r4 __asm__("r4"); + register unsigned long r5 __asm__("r5"); + uint64_t initrd = r3, initrdsize = r4; + + boot_from_memory(initrd, initrdsize); + ci_panic("*** No boot target ***\n"); +} diff --git a/pc-bios/vof/vof.h b/pc-bios/vof/vof.h new file mode 100644 index 0000000000..2d89580769 --- /dev/null +++ b/pc-bios/vof/vof.h @@ -0,0 +1,43 @@ +/* + * Virtual Open Firmware + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned long uint32_t; +typedef unsigned long long uint64_t; +#define NULL (0) +#define PROM_ERROR (-1u) +typedef unsigned long ihandle; +typedef unsigned long phandle; +typedef int size_t; +typedef void client(void); + +/* globals */ +extern void _prom_entry(void); /* OF CI entry point (i.e. this firmware) */ + +void do_boot(unsigned long addr, unsigned long r3, unsigned long r4); + +/* libc */ +int strlen(const char *s); +int strcmp(const char *s1, const char *s2); +void *memcpy(void *dest, const void *src, size_t n); +int memcmp(const void *ptr1, const void *ptr2, size_t n); +void *memmove(void *dest, const void *src, size_t n); +void *memset(void *dest, int c, size_t size); + +/* CI wrappers */ +void ci_panic(const char *str); +phandle ci_finddevice(const char *path); +uint32_t ci_getprop(phandle ph, const char *propname, void *prop, int len); + +/* booting from -kernel */ +void boot_from_memory(uint64_t initrd, uint64_t initrdsize); + +/* Entry points for CI and RTAS */ +extern uint32_t ci_entry(uint32_t params); +extern unsigned long hv_rtas(unsigned long params); +extern unsigned int hv_rtas_size; diff --git a/pc-bios/vof/vof.lds b/pc-bios/vof/vof.lds new file mode 100644 index 0000000000..1506ab4b01 --- /dev/null +++ b/pc-bios/vof/vof.lds @@ -0,0 +1,48 @@ +OUTPUT_FORMAT("elf32-powerpc") +OUTPUT_ARCH(powerpc:common) + +/* set the entry point */ +ENTRY ( __start ) + +SECTIONS { + __executable_start = .; + + .text : { + *(.text) + } + + __etext = .; + + . = ALIGN(8); + + .data : { + *(.data) + *(.rodata .rodata.*) + *(.got1) + *(.sdata) + *(.opd) + } + + /* FIXME bss at end ??? */ + + . = ALIGN(8); + __bss_start = .; + .bss : { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + } + + . = ALIGN(8); + __bss_end = .; + __bss_size = (__bss_end - __bss_start); + + . = ALIGN(256); + __toc_start = DEFINED (.TOC.) ? .TOC. : ADDR (.got) + 0x8000; + .got : + { + *(.toc .got) + } + . = ALIGN(8); + __toc_end = .; +} diff --git a/tests/qtest/rtas-test.c b/tests/qtest/rtas-test.c index 16751dbd2f..5f1194a6eb 100644 --- a/tests/qtest/rtas-test.c +++ b/tests/qtest/rtas-test.c @@ -5,7 +5,7 @@ #include "libqos/libqos-spapr.h" #include "libqos/rtas.h" -static void test_rtas_get_time_of_day(void) +static void run_test_rtas_get_time_of_day(const char *machine) { QOSState *qs; struct tm tm; @@ -13,7 +13,7 @@ static void test_rtas_get_time_of_day(void) uint64_t ret; time_t t1, t2; - qs = qtest_spapr_boot("-machine pseries"); + qs = qtest_spapr_boot(machine); t1 = time(NULL); ret = qrtas_get_time_of_day(qs->qts, &qs->alloc, &tm, &ns); @@ -24,6 +24,16 @@ static void test_rtas_get_time_of_day(void) qtest_shutdown(qs); } +static void test_rtas_get_time_of_day(void) +{ + run_test_rtas_get_time_of_day("-machine pseries"); +} + +static void test_rtas_get_time_of_day_vof(void) +{ + run_test_rtas_get_time_of_day("-machine pseries,x-vof=on"); +} + int main(int argc, char *argv[]) { const char *arch = qtest_get_arch(); @@ -35,6 +45,7 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } qtest_add_func("rtas/get-time-of-day", test_rtas_get_time_of_day); + qtest_add_func("rtas/get-time-of-day-vof", test_rtas_get_time_of_day_vof); return g_test_run(); } From caf590ddc9f514f88cc409319c06550f1f2b4014 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 6 Jul 2021 15:13:21 +1000 Subject: [PATCH 097/272] target/ppc: mtmsrd is an illegal instruction on BookE MSR is a 32-bit register in BookE and there is no mtmsrd instruction. Cc: Christian Zigotzky Signed-off-by: Nicholas Piggin Message-Id: <20210706051321.609046-1-npiggin@gmail.com> Signed-off-by: David Gibson --- target/ppc/translate.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index f65d1e81ea..d1f482b0f3 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -4940,6 +4940,11 @@ static void gen_mtcrf(DisasContext *ctx) #if defined(TARGET_PPC64) static void gen_mtmsrd(DisasContext *ctx) { + if (unlikely(!is_book3s_arch2x(ctx))) { + gen_invalid(ctx); + return; + } + CHK_SV; #if !defined(CONFIG_USER_ONLY) From a8eda5ed3db61d7de6fda4a5216ae126a6bb5eb6 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sun, 27 Jun 2021 18:27:13 +0200 Subject: [PATCH 098/272] ppc/pegasos2: Introduce Pegasos2MachineState structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add own machine state structure which will be used to store state needed for firmware emulation. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-Id: <7f6d5fbf4f70c64dba001483174a2921dd616ecd.1624811233.git.balaton@eik.bme.hu> Signed-off-by: David Gibson --- hw/ppc/pegasos2.c | 50 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index 0bfd0928aa..07971175c9 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -1,7 +1,7 @@ /* * QEMU PowerPC CHRP (Genesi/bPlan Pegasos II) hardware System Emulator * - * Copyright (c) 2018-2020 BALATON Zoltan + * Copyright (c) 2018-2021 BALATON Zoltan * * This work is licensed under the GNU GPL license version 2 or later. * @@ -41,6 +41,15 @@ #define BUS_FREQ_HZ 133333333 +#define TYPE_PEGASOS2_MACHINE MACHINE_TYPE_NAME("pegasos2") +OBJECT_DECLARE_TYPE(Pegasos2MachineState, MachineClass, PEGASOS2_MACHINE) + +struct Pegasos2MachineState { + MachineState parent_obj; + PowerPCCPU *cpu; + DeviceState *mv; +}; + static void pegasos2_cpu_reset(void *opaque) { PowerPCCPU *cpu = opaque; @@ -51,9 +60,9 @@ static void pegasos2_cpu_reset(void *opaque) static void pegasos2_init(MachineState *machine) { - PowerPCCPU *cpu = NULL; + Pegasos2MachineState *pm = PEGASOS2_MACHINE(machine); + CPUPPCState *env; MemoryRegion *rom = g_new(MemoryRegion, 1); - DeviceState *mv; PCIBus *pci_bus; PCIDevice *dev; I2CBus *i2c_bus; @@ -63,15 +72,16 @@ static void pegasos2_init(MachineState *machine) uint8_t *spd_data; /* init CPU */ - cpu = POWERPC_CPU(cpu_create(machine->cpu_type)); - if (PPC_INPUT(&cpu->env) != PPC_FLAGS_INPUT_6xx) { + pm->cpu = POWERPC_CPU(cpu_create(machine->cpu_type)); + env = &pm->cpu->env; + if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) { error_report("Incompatible CPU, only 6xx bus supported"); exit(1); } /* Set time-base frequency */ - cpu_ppc_tb_init(&cpu->env, BUS_FREQ_HZ / 4); - qemu_register_reset(pegasos2_cpu_reset, cpu); + cpu_ppc_tb_init(env, BUS_FREQ_HZ / 4); + qemu_register_reset(pegasos2_cpu_reset, pm->cpu); /* RAM */ memory_region_add_subregion(get_system_memory(), 0, machine->ram); @@ -96,16 +106,16 @@ static void pegasos2_init(MachineState *machine) g_free(filename); /* Marvell Discovery II system controller */ - mv = DEVICE(sysbus_create_simple(TYPE_MV64361, -1, - ((qemu_irq *)cpu->env.irq_inputs)[PPC6xx_INPUT_INT])); - pci_bus = mv64361_get_pci_bus(mv, 1); + pm->mv = DEVICE(sysbus_create_simple(TYPE_MV64361, -1, + ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT])); + pci_bus = mv64361_get_pci_bus(pm->mv, 1); /* VIA VT8231 South Bridge (multifunction PCI device) */ /* VT8231 function 0: PCI-to-ISA Bridge */ dev = pci_create_simple_multifunction(pci_bus, PCI_DEVFN(12, 0), true, TYPE_VT8231_ISA); qdev_connect_gpio_out(DEVICE(dev), 0, - qdev_get_gpio_in_named(mv, "gpp", 31)); + qdev_get_gpio_in_named(pm->mv, "gpp", 31)); /* VT8231 function 1: IDE Controller */ dev = pci_create_simple(pci_bus, PCI_DEVFN(12, 1), "via-ide"); @@ -129,8 +139,10 @@ static void pegasos2_init(MachineState *machine) pci_vga_init(pci_bus); } -static void pegasos2_machine(MachineClass *mc) +static void pegasos2_machine_class_init(ObjectClass *oc, void *data) { + MachineClass *mc = MACHINE_CLASS(oc); + mc->desc = "Genesi/bPlan Pegasos II"; mc->init = pegasos2_init; mc->block_default_type = IF_IDE; @@ -141,4 +153,16 @@ static void pegasos2_machine(MachineClass *mc) mc->default_ram_size = 512 * MiB; } -DEFINE_MACHINE("pegasos2", pegasos2_machine) +static const TypeInfo pegasos2_machine_info = { + .name = TYPE_PEGASOS2_MACHINE, + .parent = TYPE_MACHINE, + .class_init = pegasos2_machine_class_init, + .instance_size = sizeof(Pegasos2MachineState), +}; + +static void pegasos2_machine_register_types(void) +{ + type_register_static(&pegasos2_machine_info); +} + +type_init(pegasos2_machine_register_types) From 5e994fc019862e77ee8fd2c8808c5fdcf2d249de Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sun, 27 Jun 2021 18:27:13 +0200 Subject: [PATCH 099/272] target/ppc: Allow virtual hypervisor on CPU without HV Change the assert in ppc_store_sdr1() to allow vhyp to be set on CPUs without HV bit. This allows using the vhyp interface for firmware emulation on pegasos2. Signed-off-by: BALATON Zoltan Message-Id: <21c7745aabbb68fcc50bb2ffaf16b939ba21261c.1624811233.git.balaton@eik.bme.hu> Signed-off-by: David Gibson --- target/ppc/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/ppc/cpu.c b/target/ppc/cpu.c index 19d67b5b07..a29299882a 100644 --- a/target/ppc/cpu.c +++ b/target/ppc/cpu.c @@ -72,7 +72,7 @@ void ppc_store_sdr1(CPUPPCState *env, target_ulong value) { PowerPCCPU *cpu = env_archcpu(env); qemu_log_mask(CPU_LOG_MMU, "%s: " TARGET_FMT_lx "\n", __func__, value); - assert(!cpu->vhyp); + assert(!cpu->env.has_hv_mode || !cpu->vhyp); #if defined(TARGET_PPC64) if (mmu_is_64bit(env->mmu_model)) { target_ulong sdr_mask = SDR_64_HTABORG | SDR_64_HTABSIZE; From 17fd09c0212b1595377fd62ade033dcd4147f8b6 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 15 Jun 2021 14:41:07 +1000 Subject: [PATCH 100/272] target/ppc/spapr: Update H_GET_CPU_CHARACTERISTICS L1D cache flush bits There are several new L1D cache flush bits added to the hcall which reflect hardware security features for speculative cache access issues. These behaviours are now being specified as negative in order to simplify patched kernel compatibility with older firmware (a new problem found in existing systems would automatically be vulnerable). [dwg: Technically this changes behaviour for existing machine types. After discussion with Nick, we've determined this is safe, because the worst that will happen if a guest gets the wrong information due to a migration is that it will perform some unnecessary workarounds, but will remain correct and secure (well, as secure as it was going to be anyway). In addition the change only affects cap-cfpc=safe which is not enabled by default, and in fact is not possible to set on any current hardware (though it's expected it will be possible on POWER10)] Signed-off-by: Nicholas Piggin Message-Id: <20210615044107.1481608-1-npiggin@gmail.com> Signed-off-by: David Gibson --- hw/ppc/spapr_hcall.c | 2 ++ include/hw/ppc/spapr.h | 3 +++ 2 files changed, 5 insertions(+) diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 03fc191599..80ae8eaadd 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -1318,6 +1318,8 @@ static target_ulong h_get_cpu_characteristics(PowerPCCPU *cpu, behaviour |= H_CPU_BEHAV_L1D_FLUSH_PR; break; case SPAPR_CAP_FIXED: + behaviour |= H_CPU_BEHAV_NO_L1D_FLUSH_ENTRY; + behaviour |= H_CPU_BEHAV_NO_L1D_FLUSH_UACCESS; break; default: /* broken */ assert(safe_cache == SPAPR_CAP_BROKEN); diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 1e63f33e9a..a25e69fe4c 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -400,10 +400,13 @@ struct SpaprMachineState { #define H_CPU_CHAR_THR_RECONF_TRIG PPC_BIT(6) #define H_CPU_CHAR_CACHE_COUNT_DIS PPC_BIT(7) #define H_CPU_CHAR_BCCTR_FLUSH_ASSIST PPC_BIT(9) + #define H_CPU_BEHAV_FAVOUR_SECURITY PPC_BIT(0) #define H_CPU_BEHAV_L1D_FLUSH_PR PPC_BIT(1) #define H_CPU_BEHAV_BNDS_CHK_SPEC_BAR PPC_BIT(2) #define H_CPU_BEHAV_FLUSH_COUNT_CACHE PPC_BIT(5) +#define H_CPU_BEHAV_NO_L1D_FLUSH_ENTRY PPC_BIT(7) +#define H_CPU_BEHAV_NO_L1D_FLUSH_UACCESS PPC_BIT(8) /* Each control block has to be on a 4K boundary */ #define H_CB_ALIGNMENT 4096 From a6c9808a689764cba980280fc4581e2deb5023a4 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sun, 27 Jun 2021 18:27:13 +0200 Subject: [PATCH 101/272] ppc/pegasos2: Use Virtual Open Firmware as firmware replacement The pegasos2 board comes with an Open Firmware compliant ROM based on SmartFirmware but it has some changes that are not open source therefore the ROM binary cannot be included in QEMU. Guests running on the board however depend on services provided by the firmware. The Virtual Open Firmware recently added to QEMU implements a minimal set of these services to allow some guests to boot without the original firmware. This patch adds VOF as the default firmware for pegasos2 which allows booting Linux and MorphOS via -kernel option while a ROM image can still be used with -bios for guests that don't run with VOF. Signed-off-by: BALATON Zoltan Message-Id: <1d6ed6f290c5c1f0b5a1e1c51cf1151452d70d9a.1624811233.git.balaton@eik.bme.hu> Signed-off-by: David Gibson --- hw/ppc/Kconfig | 1 + hw/ppc/pegasos2.c | 602 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 601 insertions(+), 2 deletions(-) diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig index 67630f80e1..7fcafec60a 100644 --- a/hw/ppc/Kconfig +++ b/hw/ppc/Kconfig @@ -76,6 +76,7 @@ config PEGASOS2 select VT82C686 select IDE_VIA select SMBUS_EEPROM + select VOF # This should come with VT82C686 select ACPI_X86 diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index 07971175c9..f1741a4512 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -34,13 +34,33 @@ #include "trace.h" #include "qemu/datadir.h" #include "sysemu/device_tree.h" +#include "hw/ppc/vof.h" -#define PROM_FILENAME "pegasos2.rom" +#include + +#define PROM_FILENAME "vof.bin" #define PROM_ADDR 0xfff00000 #define PROM_SIZE 0x80000 +#define KVMPPC_HCALL_BASE 0xf000 +#define KVMPPC_H_VOF_CLIENT (KVMPPC_HCALL_BASE + 0x5) + +#define H_SUCCESS 0 +#define H_PRIVILEGE -3 /* Caller not privileged */ +#define H_PARAMETER -4 /* Parameter invalid, out-of-range or conflicting */ + #define BUS_FREQ_HZ 133333333 +#define PCI0_MEM_BASE 0xc0000000 +#define PCI0_MEM_SIZE 0x20000000 +#define PCI0_IO_BASE 0xf8000000 +#define PCI0_IO_SIZE 0x10000 + +#define PCI1_MEM_BASE 0x80000000 +#define PCI1_MEM_SIZE 0x40000000 +#define PCI1_IO_BASE 0xfe000000 +#define PCI1_IO_SIZE 0x10000 + #define TYPE_PEGASOS2_MACHINE MACHINE_TYPE_NAME("pegasos2") OBJECT_DECLARE_TYPE(Pegasos2MachineState, MachineClass, PEGASOS2_MACHINE) @@ -48,14 +68,26 @@ struct Pegasos2MachineState { MachineState parent_obj; PowerPCCPU *cpu; DeviceState *mv; + Vof *vof; + void *fdt_blob; + uint64_t kernel_addr; + uint64_t kernel_entry; + uint64_t kernel_size; }; +static void *build_fdt(MachineState *machine, int *fdt_size); + static void pegasos2_cpu_reset(void *opaque) { PowerPCCPU *cpu = opaque; + Pegasos2MachineState *pm = PEGASOS2_MACHINE(current_machine); cpu_reset(CPU(cpu)); cpu->env.spr[SPR_HID1] = 7ULL << 28; + if (pm->vof) { + cpu->env.gpr[1] = 2 * VOF_STACK_SIZE - 0x20; + cpu->env.nip = 0x100; + } } static void pegasos2_init(MachineState *machine) @@ -92,18 +124,24 @@ static void pegasos2_init(MachineState *machine) error_report("Could not find firmware '%s'", fwname); exit(1); } + if (!machine->firmware && !pm->vof) { + pm->vof = g_malloc0(sizeof(*pm->vof)); + } memory_region_init_rom(rom, NULL, "pegasos2.rom", PROM_SIZE, &error_fatal); memory_region_add_subregion(get_system_memory(), PROM_ADDR, rom); sz = load_elf(filename, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); if (sz <= 0) { - sz = load_image_targphys(filename, PROM_ADDR, PROM_SIZE); + sz = load_image_targphys(filename, pm->vof ? 0 : PROM_ADDR, PROM_SIZE); } if (sz <= 0 || sz > PROM_SIZE) { error_report("Could not load firmware '%s'", filename); exit(1); } g_free(filename); + if (pm->vof) { + pm->vof->fw_size = sz; + } /* Marvell Discovery II system controller */ pm->mv = DEVICE(sysbus_create_simple(TYPE_MV64361, -1, @@ -137,20 +175,185 @@ static void pegasos2_init(MachineState *machine) /* other PC hardware */ pci_vga_init(pci_bus); + + if (machine->kernel_filename) { + sz = load_elf(machine->kernel_filename, NULL, NULL, NULL, + &pm->kernel_entry, &pm->kernel_addr, NULL, NULL, 1, + PPC_ELF_MACHINE, 0, 0); + if (sz <= 0) { + error_report("Could not load kernel '%s'", + machine->kernel_filename); + exit(1); + } + pm->kernel_size = sz; + if (!pm->vof) { + warn_report("Option -kernel may be ineffective with -bios."); + } + } + if (machine->kernel_cmdline && !pm->vof) { + warn_report("Option -append may be ineffective with -bios."); + } +} + +static void pegasos2_pci_config_write(AddressSpace *as, int bus, uint32_t addr, + uint32_t len, uint32_t val) +{ + hwaddr pcicfg = (bus ? 0xf1000c78 : 0xf1000cf8); + + stl_le_phys(as, pcicfg, addr | BIT(31)); + switch (len) { + case 4: + stl_le_phys(as, pcicfg + 4, val); + break; + case 2: + stw_le_phys(as, pcicfg + 4, val); + break; + case 1: + stb_phys(as, pcicfg + 4, val); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid length\n", __func__); + break; + } +} + +static void pegasos2_machine_reset(MachineState *machine) +{ + Pegasos2MachineState *pm = PEGASOS2_MACHINE(machine); + AddressSpace *as = CPU(pm->cpu)->as; + void *fdt; + uint64_t d[2]; + int sz; + + qemu_devices_reset(); + if (!pm->vof) { + return; /* Firmware should set up machine so nothing to do */ + } + + /* Otherwise, set up devices that board firmware would normally do */ + stl_le_phys(as, 0xf1000000, 0x28020ff); + stl_le_phys(as, 0xf1000278, 0xa31fc); + stl_le_phys(as, 0xf100f300, 0x11ff0400); + stl_le_phys(as, 0xf100f10c, 0x80000000); + stl_le_phys(as, 0xf100001c, 0x8000000); + pegasos2_pci_config_write(as, 0, PCI_COMMAND, 2, PCI_COMMAND_IO | + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + pegasos2_pci_config_write(as, 1, PCI_COMMAND, 2, PCI_COMMAND_IO | + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + + pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 0) << 8) | + PCI_INTERRUPT_LINE, 2, 0x9); + pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 0) << 8) | + 0x50, 1, 0x2); + + pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) | + PCI_INTERRUPT_LINE, 2, 0x109); + pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) | + PCI_CLASS_PROG, 1, 0xf); + pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) | + 0x40, 1, 0xb); + pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) | + 0x50, 4, 0x17171717); + pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) | + PCI_COMMAND, 2, 0x87); + + pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 2) << 8) | + PCI_INTERRUPT_LINE, 2, 0x409); + + pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 3) << 8) | + PCI_INTERRUPT_LINE, 2, 0x409); + + pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 4) << 8) | + PCI_INTERRUPT_LINE, 2, 0x9); + pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 4) << 8) | + 0x48, 4, 0xf00); + pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 4) << 8) | + 0x40, 4, 0x558020); + pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 4) << 8) | + 0x90, 4, 0xd00); + + pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 5) << 8) | + PCI_INTERRUPT_LINE, 2, 0x309); + + pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 6) << 8) | + PCI_INTERRUPT_LINE, 2, 0x309); + + /* Device tree and VOF set up */ + vof_init(pm->vof, machine->ram_size, &error_fatal); + if (vof_claim(pm->vof, 0, VOF_STACK_SIZE, VOF_STACK_SIZE) == -1) { + error_report("Memory allocation for stack failed"); + exit(1); + } + if (pm->kernel_size && + vof_claim(pm->vof, pm->kernel_addr, pm->kernel_size, 0) == -1) { + error_report("Memory for kernel is in use"); + exit(1); + } + fdt = build_fdt(machine, &sz); + /* FIXME: VOF assumes entry is same as load address */ + d[0] = cpu_to_be64(pm->kernel_entry); + d[1] = cpu_to_be64(pm->kernel_size - (pm->kernel_entry - pm->kernel_addr)); + qemu_fdt_setprop(fdt, "/chosen", "qemu,boot-kernel", d, sizeof(d)); + + qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt)); + g_free(pm->fdt_blob); + pm->fdt_blob = fdt; + + vof_build_dt(fdt, pm->vof); + vof_client_open_store(fdt, pm->vof, "/chosen", "stdout", "/failsafe"); + pm->cpu->vhyp = PPC_VIRTUAL_HYPERVISOR(machine); +} + +static void pegasos2_hypercall(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu) +{ + Pegasos2MachineState *pm = PEGASOS2_MACHINE(vhyp); + CPUPPCState *env = &cpu->env; + + /* The TCG path should also be holding the BQL at this point */ + g_assert(qemu_mutex_iothread_locked()); + + if (msr_pr) { + qemu_log_mask(LOG_GUEST_ERROR, "Hypercall made with MSR[PR]=1\n"); + env->gpr[3] = H_PRIVILEGE; + } else if (env->gpr[3] == KVMPPC_H_VOF_CLIENT) { + int ret = vof_client_call(MACHINE(pm), pm->vof, pm->fdt_blob, + env->gpr[4]); + env->gpr[3] = (ret ? H_PARAMETER : H_SUCCESS); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "Unsupported hypercall " TARGET_FMT_lx + "\n", env->gpr[3]); + env->gpr[3] = -1; + } +} + +static void vhyp_nop(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu) +{ +} + +static target_ulong vhyp_encode_hpt_for_kvm_pr(PPCVirtualHypervisor *vhyp) +{ + return POWERPC_CPU(current_cpu)->env.spr[SPR_SDR1]; } static void pegasos2_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + PPCVirtualHypervisorClass *vhc = PPC_VIRTUAL_HYPERVISOR_CLASS(oc); mc->desc = "Genesi/bPlan Pegasos II"; mc->init = pegasos2_init; + mc->reset = pegasos2_machine_reset; mc->block_default_type = IF_IDE; mc->default_boot_order = "cd"; mc->default_display = "std"; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("7400_v2.9"); mc->default_ram_id = "pegasos2.ram"; mc->default_ram_size = 512 * MiB; + + vhc->hypercall = pegasos2_hypercall; + vhc->cpu_exec_enter = vhyp_nop; + vhc->cpu_exec_exit = vhyp_nop; + vhc->encode_hpt_for_kvm_pr = vhyp_encode_hpt_for_kvm_pr; } static const TypeInfo pegasos2_machine_info = { @@ -158,6 +361,10 @@ static const TypeInfo pegasos2_machine_info = { .parent = TYPE_MACHINE, .class_init = pegasos2_machine_class_init, .instance_size = sizeof(Pegasos2MachineState), + .interfaces = (InterfaceInfo[]) { + { TYPE_PPC_VIRTUAL_HYPERVISOR }, + { } + }, }; static void pegasos2_machine_register_types(void) @@ -166,3 +373,394 @@ static void pegasos2_machine_register_types(void) } type_init(pegasos2_machine_register_types) + +/* FDT creation for passing to firmware */ + +typedef struct { + void *fdt; + const char *path; +} FDTInfo; + +/* We do everything in reverse order so it comes out right in the tree */ + +static void dt_ide(PCIBus *bus, PCIDevice *d, FDTInfo *fi) +{ + qemu_fdt_setprop_string(fi->fdt, fi->path, "device_type", "spi"); +} + +static void dt_usb(PCIBus *bus, PCIDevice *d, FDTInfo *fi) +{ + qemu_fdt_setprop_cell(fi->fdt, fi->path, "#size-cells", 0); + qemu_fdt_setprop_cell(fi->fdt, fi->path, "#address-cells", 1); + qemu_fdt_setprop_string(fi->fdt, fi->path, "device_type", "usb"); +} + +static void dt_isa(PCIBus *bus, PCIDevice *d, FDTInfo *fi) +{ + GString *name = g_string_sized_new(64); + uint32_t cells[3]; + + qemu_fdt_setprop_cell(fi->fdt, fi->path, "#size-cells", 1); + qemu_fdt_setprop_cell(fi->fdt, fi->path, "#address-cells", 2); + qemu_fdt_setprop_string(fi->fdt, fi->path, "device_type", "isa"); + qemu_fdt_setprop_string(fi->fdt, fi->path, "name", "isa"); + + /* addional devices */ + g_string_printf(name, "%s/lpt@i3bc", fi->path); + qemu_fdt_add_subnode(fi->fdt, name->str); + qemu_fdt_setprop_cell(fi->fdt, name->str, "clock-frequency", 0); + cells[0] = cpu_to_be32(7); + cells[1] = 0; + qemu_fdt_setprop(fi->fdt, name->str, "interrupts", + cells, 2 * sizeof(cells[0])); + cells[0] = cpu_to_be32(1); + cells[1] = cpu_to_be32(0x3bc); + cells[2] = cpu_to_be32(8); + qemu_fdt_setprop(fi->fdt, name->str, "reg", cells, 3 * sizeof(cells[0])); + qemu_fdt_setprop_string(fi->fdt, name->str, "device_type", "lpt"); + qemu_fdt_setprop_string(fi->fdt, name->str, "name", "lpt"); + + g_string_printf(name, "%s/fdc@i3f0", fi->path); + qemu_fdt_add_subnode(fi->fdt, name->str); + qemu_fdt_setprop_cell(fi->fdt, name->str, "clock-frequency", 0); + cells[0] = cpu_to_be32(6); + cells[1] = 0; + qemu_fdt_setprop(fi->fdt, name->str, "interrupts", + cells, 2 * sizeof(cells[0])); + cells[0] = cpu_to_be32(1); + cells[1] = cpu_to_be32(0x3f0); + cells[2] = cpu_to_be32(8); + qemu_fdt_setprop(fi->fdt, name->str, "reg", cells, 3 * sizeof(cells[0])); + qemu_fdt_setprop_string(fi->fdt, name->str, "device_type", "fdc"); + qemu_fdt_setprop_string(fi->fdt, name->str, "name", "fdc"); + + g_string_printf(name, "%s/timer@i40", fi->path); + qemu_fdt_add_subnode(fi->fdt, name->str); + qemu_fdt_setprop_cell(fi->fdt, name->str, "clock-frequency", 0); + cells[0] = cpu_to_be32(1); + cells[1] = cpu_to_be32(0x40); + cells[2] = cpu_to_be32(8); + qemu_fdt_setprop(fi->fdt, name->str, "reg", cells, 3 * sizeof(cells[0])); + qemu_fdt_setprop_string(fi->fdt, name->str, "device_type", "timer"); + qemu_fdt_setprop_string(fi->fdt, name->str, "name", "timer"); + + g_string_printf(name, "%s/rtc@i70", fi->path); + qemu_fdt_add_subnode(fi->fdt, name->str); + qemu_fdt_setprop_string(fi->fdt, name->str, "compatible", "ds1385-rtc"); + qemu_fdt_setprop_cell(fi->fdt, name->str, "clock-frequency", 0); + cells[0] = cpu_to_be32(8); + cells[1] = 0; + qemu_fdt_setprop(fi->fdt, name->str, "interrupts", + cells, 2 * sizeof(cells[0])); + cells[0] = cpu_to_be32(1); + cells[1] = cpu_to_be32(0x70); + cells[2] = cpu_to_be32(2); + qemu_fdt_setprop(fi->fdt, name->str, "reg", cells, 3 * sizeof(cells[0])); + qemu_fdt_setprop_string(fi->fdt, name->str, "device_type", "rtc"); + qemu_fdt_setprop_string(fi->fdt, name->str, "name", "rtc"); + + g_string_printf(name, "%s/keyboard@i60", fi->path); + qemu_fdt_add_subnode(fi->fdt, name->str); + cells[0] = cpu_to_be32(1); + cells[1] = 0; + qemu_fdt_setprop(fi->fdt, name->str, "interrupts", + cells, 2 * sizeof(cells[0])); + cells[0] = cpu_to_be32(1); + cells[1] = cpu_to_be32(0x60); + cells[2] = cpu_to_be32(5); + qemu_fdt_setprop(fi->fdt, name->str, "reg", cells, 3 * sizeof(cells[0])); + qemu_fdt_setprop_string(fi->fdt, name->str, "device_type", "keyboard"); + qemu_fdt_setprop_string(fi->fdt, name->str, "name", "keyboard"); + + g_string_printf(name, "%s/8042@i60", fi->path); + qemu_fdt_add_subnode(fi->fdt, name->str); + qemu_fdt_setprop_cell(fi->fdt, name->str, "#interrupt-cells", 2); + qemu_fdt_setprop_cell(fi->fdt, name->str, "#size-cells", 0); + qemu_fdt_setprop_cell(fi->fdt, name->str, "#address-cells", 1); + qemu_fdt_setprop_string(fi->fdt, name->str, "interrupt-controller", ""); + qemu_fdt_setprop_cell(fi->fdt, name->str, "clock-frequency", 0); + cells[0] = cpu_to_be32(1); + cells[1] = cpu_to_be32(0x60); + cells[2] = cpu_to_be32(5); + qemu_fdt_setprop(fi->fdt, name->str, "reg", cells, 3 * sizeof(cells[0])); + qemu_fdt_setprop_string(fi->fdt, name->str, "device_type", ""); + qemu_fdt_setprop_string(fi->fdt, name->str, "name", "8042"); + + g_string_printf(name, "%s/serial@i2f8", fi->path); + qemu_fdt_add_subnode(fi->fdt, name->str); + qemu_fdt_setprop_cell(fi->fdt, name->str, "clock-frequency", 0); + cells[0] = cpu_to_be32(3); + cells[1] = 0; + qemu_fdt_setprop(fi->fdt, name->str, "interrupts", + cells, 2 * sizeof(cells[0])); + cells[0] = cpu_to_be32(1); + cells[1] = cpu_to_be32(0x2f8); + cells[2] = cpu_to_be32(8); + qemu_fdt_setprop(fi->fdt, name->str, "reg", cells, 3 * sizeof(cells[0])); + qemu_fdt_setprop_string(fi->fdt, name->str, "device_type", "serial"); + qemu_fdt_setprop_string(fi->fdt, name->str, "name", "serial"); + + g_string_free(name, TRUE); +} + +static struct { + const char *id; + const char *name; + void (*dtf)(PCIBus *bus, PCIDevice *d, FDTInfo *fi); +} device_map[] = { + { "pci11ab,6460", "host", NULL }, + { "pci1106,8231", "isa", dt_isa }, + { "pci1106,571", "ide", dt_ide }, + { "pci1106,3044", "firewire", NULL }, + { "pci1106,3038", "usb", dt_usb }, + { "pci1106,8235", "other", NULL }, + { "pci1106,3058", "sound", NULL }, + { NULL, NULL } +}; + +static void add_pci_device(PCIBus *bus, PCIDevice *d, void *opaque) +{ + FDTInfo *fi = opaque; + GString *node = g_string_new(NULL); + uint32_t cells[(PCI_NUM_REGIONS + 1) * 5]; + int i, j; + const char *name = NULL; + g_autofree const gchar *pn = g_strdup_printf("pci%x,%x", + pci_get_word(&d->config[PCI_VENDOR_ID]), + pci_get_word(&d->config[PCI_DEVICE_ID])); + + for (i = 0; device_map[i].id; i++) { + if (!strcmp(pn, device_map[i].id)) { + name = device_map[i].name; + break; + } + } + g_string_printf(node, "%s/%s@%x", fi->path, (name ?: pn), + PCI_SLOT(d->devfn)); + if (PCI_FUNC(d->devfn)) { + g_string_append_printf(node, ",%x", PCI_FUNC(d->devfn)); + } + + qemu_fdt_add_subnode(fi->fdt, node->str); + if (device_map[i].dtf) { + FDTInfo cfi = { fi->fdt, node->str }; + device_map[i].dtf(bus, d, &cfi); + } + cells[0] = cpu_to_be32(d->devfn << 8); + cells[1] = 0; + cells[2] = 0; + cells[3] = 0; + cells[4] = 0; + j = 5; + for (i = 0; i < PCI_NUM_REGIONS; i++) { + if (!d->io_regions[i].size) { + continue; + } + cells[j] = cpu_to_be32(d->devfn << 8 | (PCI_BASE_ADDRESS_0 + i * 4)); + if (d->io_regions[i].type & PCI_BASE_ADDRESS_SPACE_IO) { + cells[j] |= cpu_to_be32(1 << 24); + } else { + cells[j] |= cpu_to_be32(2 << 24); + if (d->io_regions[i].type & PCI_BASE_ADDRESS_MEM_PREFETCH) { + cells[j] |= cpu_to_be32(4 << 28); + } + } + cells[j + 1] = 0; + cells[j + 2] = 0; + cells[j + 3] = cpu_to_be32(d->io_regions[i].size >> 32); + cells[j + 4] = cpu_to_be32(d->io_regions[i].size); + j += 5; + } + qemu_fdt_setprop(fi->fdt, node->str, "reg", cells, j * sizeof(cells[0])); + qemu_fdt_setprop_string(fi->fdt, node->str, "name", name ?: pn); + if (pci_get_byte(&d->config[PCI_INTERRUPT_PIN])) { + qemu_fdt_setprop_cell(fi->fdt, node->str, "interrupts", + pci_get_byte(&d->config[PCI_INTERRUPT_PIN])); + } + /* Pegasos2 firmware has subsystem-id amd subsystem-vendor-id swapped */ + qemu_fdt_setprop_cell(fi->fdt, node->str, "subsystem-vendor-id", + pci_get_word(&d->config[PCI_SUBSYSTEM_ID])); + qemu_fdt_setprop_cell(fi->fdt, node->str, "subsystem-id", + pci_get_word(&d->config[PCI_SUBSYSTEM_VENDOR_ID])); + cells[0] = pci_get_long(&d->config[PCI_CLASS_REVISION]); + qemu_fdt_setprop_cell(fi->fdt, node->str, "class-code", cells[0] >> 8); + qemu_fdt_setprop_cell(fi->fdt, node->str, "revision-id", cells[0] && 0xff); + qemu_fdt_setprop_cell(fi->fdt, node->str, "device-id", + pci_get_word(&d->config[PCI_DEVICE_ID])); + qemu_fdt_setprop_cell(fi->fdt, node->str, "vendor-id", + pci_get_word(&d->config[PCI_VENDOR_ID])); + + g_string_free(node, TRUE); +} + +static void *build_fdt(MachineState *machine, int *fdt_size) +{ + Pegasos2MachineState *pm = PEGASOS2_MACHINE(machine); + PowerPCCPU *cpu = pm->cpu; + PCIBus *pci_bus; + FDTInfo fi; + uint32_t cells[16]; + void *fdt = create_device_tree(fdt_size); + + fi.fdt = fdt; + + /* root node */ + qemu_fdt_setprop_string(fdt, "/", "CODEGEN,description", + "Pegasos CHRP PowerPC System"); + qemu_fdt_setprop_string(fdt, "/", "CODEGEN,board", "Pegasos2"); + qemu_fdt_setprop_string(fdt, "/", "CODEGEN,vendor", "bplan GmbH"); + qemu_fdt_setprop_string(fdt, "/", "revision", "2B"); + qemu_fdt_setprop_string(fdt, "/", "model", "Pegasos2"); + qemu_fdt_setprop_string(fdt, "/", "device_type", "chrp"); + qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 1); + qemu_fdt_setprop_string(fdt, "/", "name", "bplan,Pegasos2"); + + /* pci@c0000000 */ + qemu_fdt_add_subnode(fdt, "/pci@c0000000"); + cells[0] = 0; + cells[1] = 0; + qemu_fdt_setprop(fdt, "/pci@c0000000", "bus-range", + cells, 2 * sizeof(cells[0])); + qemu_fdt_setprop_cell(fdt, "/pci@c0000000", "pci-bridge-number", 1); + cells[0] = cpu_to_be32(PCI0_MEM_BASE); + cells[1] = cpu_to_be32(PCI0_MEM_SIZE); + qemu_fdt_setprop(fdt, "/pci@c0000000", "reg", cells, 2 * sizeof(cells[0])); + cells[0] = cpu_to_be32(0x01000000); + cells[1] = 0; + cells[2] = 0; + cells[3] = cpu_to_be32(PCI0_IO_BASE); + cells[4] = 0; + cells[5] = cpu_to_be32(PCI0_IO_SIZE); + cells[6] = cpu_to_be32(0x02000000); + cells[7] = 0; + cells[8] = cpu_to_be32(PCI0_MEM_BASE); + cells[9] = cpu_to_be32(PCI0_MEM_BASE); + cells[10] = 0; + cells[11] = cpu_to_be32(PCI0_MEM_SIZE); + qemu_fdt_setprop(fdt, "/pci@c0000000", "ranges", + cells, 12 * sizeof(cells[0])); + qemu_fdt_setprop_cell(fdt, "/pci@c0000000", "#size-cells", 2); + qemu_fdt_setprop_cell(fdt, "/pci@c0000000", "#address-cells", 3); + qemu_fdt_setprop_string(fdt, "/pci@c0000000", "device_type", "pci"); + qemu_fdt_setprop_string(fdt, "/pci@c0000000", "name", "pci"); + + fi.path = "/pci@c0000000"; + pci_bus = mv64361_get_pci_bus(pm->mv, 0); + pci_for_each_device_reverse(pci_bus, 0, add_pci_device, &fi); + + /* pci@80000000 */ + qemu_fdt_add_subnode(fdt, "/pci@80000000"); + cells[0] = 0; + cells[1] = 0; + qemu_fdt_setprop(fdt, "/pci@80000000", "bus-range", + cells, 2 * sizeof(cells[0])); + qemu_fdt_setprop_cell(fdt, "/pci@80000000", "pci-bridge-number", 0); + cells[0] = cpu_to_be32(PCI1_MEM_BASE); + cells[1] = cpu_to_be32(PCI1_MEM_SIZE); + qemu_fdt_setprop(fdt, "/pci@80000000", "reg", cells, 2 * sizeof(cells[0])); + qemu_fdt_setprop_cell(fdt, "/pci@80000000", "8259-interrupt-acknowledge", + 0xf1000cb4); + cells[0] = cpu_to_be32(0x01000000); + cells[1] = 0; + cells[2] = 0; + cells[3] = cpu_to_be32(PCI1_IO_BASE); + cells[4] = 0; + cells[5] = cpu_to_be32(PCI1_IO_SIZE); + cells[6] = cpu_to_be32(0x02000000); + cells[7] = 0; + cells[8] = cpu_to_be32(PCI1_MEM_BASE); + cells[9] = cpu_to_be32(PCI1_MEM_BASE); + cells[10] = 0; + cells[11] = cpu_to_be32(PCI1_MEM_SIZE); + qemu_fdt_setprop(fdt, "/pci@80000000", "ranges", + cells, 12 * sizeof(cells[0])); + qemu_fdt_setprop_cell(fdt, "/pci@80000000", "#size-cells", 2); + qemu_fdt_setprop_cell(fdt, "/pci@80000000", "#address-cells", 3); + qemu_fdt_setprop_string(fdt, "/pci@80000000", "device_type", "pci"); + qemu_fdt_setprop_string(fdt, "/pci@80000000", "name", "pci"); + + fi.path = "/pci@80000000"; + pci_bus = mv64361_get_pci_bus(pm->mv, 1); + pci_for_each_device_reverse(pci_bus, 0, add_pci_device, &fi); + + qemu_fdt_add_subnode(fdt, "/failsafe"); + qemu_fdt_setprop_string(fdt, "/failsafe", "device_type", "serial"); + qemu_fdt_setprop_string(fdt, "/failsafe", "name", "failsafe"); + + /* cpus */ + qemu_fdt_add_subnode(fdt, "/cpus"); + qemu_fdt_setprop_cell(fdt, "/cpus", "#cpus", 1); + qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 1); + qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0); + qemu_fdt_setprop_string(fdt, "/cpus", "name", "cpus"); + + /* FIXME Get CPU name from CPU object */ + const char *cp = "/cpus/PowerPC,G4"; + qemu_fdt_add_subnode(fdt, cp); + qemu_fdt_setprop_cell(fdt, cp, "l2cr", 0); + qemu_fdt_setprop_cell(fdt, cp, "d-cache-size", 0x8000); + qemu_fdt_setprop_cell(fdt, cp, "d-cache-block-size", + cpu->env.dcache_line_size); + qemu_fdt_setprop_cell(fdt, cp, "d-cache-line-size", + cpu->env.dcache_line_size); + qemu_fdt_setprop_cell(fdt, cp, "i-cache-size", 0x8000); + qemu_fdt_setprop_cell(fdt, cp, "i-cache-block-size", + cpu->env.icache_line_size); + qemu_fdt_setprop_cell(fdt, cp, "i-cache-line-size", + cpu->env.icache_line_size); + if (cpu->env.id_tlbs) { + qemu_fdt_setprop_cell(fdt, cp, "i-tlb-sets", cpu->env.nb_ways); + qemu_fdt_setprop_cell(fdt, cp, "i-tlb-size", cpu->env.tlb_per_way); + qemu_fdt_setprop_cell(fdt, cp, "d-tlb-sets", cpu->env.nb_ways); + qemu_fdt_setprop_cell(fdt, cp, "d-tlb-size", cpu->env.tlb_per_way); + qemu_fdt_setprop_string(fdt, cp, "tlb-split", ""); + } + qemu_fdt_setprop_cell(fdt, cp, "tlb-sets", cpu->env.nb_ways); + qemu_fdt_setprop_cell(fdt, cp, "tlb-size", cpu->env.nb_tlb); + qemu_fdt_setprop_string(fdt, cp, "state", "running"); + if (cpu->env.insns_flags & PPC_ALTIVEC) { + qemu_fdt_setprop_string(fdt, cp, "altivec", ""); + qemu_fdt_setprop_string(fdt, cp, "data-streams", ""); + } + /* + * FIXME What flags do data-streams, external-control and + * performance-monitor depend on? + */ + qemu_fdt_setprop_string(fdt, cp, "external-control", ""); + if (cpu->env.insns_flags & PPC_FLOAT_FSQRT) { + qemu_fdt_setprop_string(fdt, cp, "general-purpose", ""); + } + qemu_fdt_setprop_string(fdt, cp, "performance-monitor", ""); + if (cpu->env.insns_flags & PPC_FLOAT_FRES) { + qemu_fdt_setprop_string(fdt, cp, "graphics", ""); + } + qemu_fdt_setprop_cell(fdt, cp, "reservation-granule-size", 4); + qemu_fdt_setprop_cell(fdt, cp, "timebase-frequency", + cpu->env.tb_env->tb_freq); + qemu_fdt_setprop_cell(fdt, cp, "bus-frequency", BUS_FREQ_HZ); + qemu_fdt_setprop_cell(fdt, cp, "clock-frequency", BUS_FREQ_HZ * 7.5); + qemu_fdt_setprop_cell(fdt, cp, "cpu-version", cpu->env.spr[SPR_PVR]); + cells[0] = 0; + cells[1] = 0; + qemu_fdt_setprop(fdt, cp, "reg", cells, 2 * sizeof(cells[0])); + qemu_fdt_setprop_string(fdt, cp, "device_type", "cpu"); + qemu_fdt_setprop_string(fdt, cp, "name", strrchr(cp, '/') + 1); + + /* memory */ + qemu_fdt_add_subnode(fdt, "/memory@0"); + cells[0] = 0; + cells[1] = cpu_to_be32(machine->ram_size); + qemu_fdt_setprop(fdt, "/memory@0", "reg", cells, 2 * sizeof(cells[0])); + qemu_fdt_setprop_string(fdt, "/memory@0", "device_type", "memory"); + qemu_fdt_setprop_string(fdt, "/memory@0", "name", "memory"); + + qemu_fdt_add_subnode(fdt, "/chosen"); + qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", + machine->kernel_cmdline ?: ""); + qemu_fdt_setprop_string(fdt, "/chosen", "name", "chosen"); + + qemu_fdt_add_subnode(fdt, "/openprom"); + qemu_fdt_setprop_string(fdt, "/openprom", "model", "Pegasos2,1.1"); + + return fdt; +} From e7dfb29e5a757de09b890df42fbeb5b70c6f2a9f Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 8 Jul 2021 15:40:21 +1000 Subject: [PATCH 102/272] ppc/pegasos2: Fix use of && instead of & This is obviously intended to be a mask, not a logical operation. Signed-off-by: David Gibson --- hw/ppc/pegasos2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index f1741a4512..cf1dc53c83 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -584,7 +584,7 @@ static void add_pci_device(PCIBus *bus, PCIDevice *d, void *opaque) pci_get_word(&d->config[PCI_SUBSYSTEM_VENDOR_ID])); cells[0] = pci_get_long(&d->config[PCI_CLASS_REVISION]); qemu_fdt_setprop_cell(fi->fdt, node->str, "class-code", cells[0] >> 8); - qemu_fdt_setprop_cell(fi->fdt, node->str, "revision-id", cells[0] && 0xff); + qemu_fdt_setprop_cell(fi->fdt, node->str, "revision-id", cells[0] & 0xff); qemu_fdt_setprop_cell(fi->fdt, node->str, "device-id", pci_get_word(&d->config[PCI_DEVICE_ID])); qemu_fdt_setprop_cell(fi->fdt, node->str, "vendor-id", From 5f2eb04961011de0ed15160ee17c8f85c8c30b73 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Thu, 8 Jul 2021 23:46:14 +0200 Subject: [PATCH 103/272] ppc/pegasos2: Implement some RTAS functions with VOF Linux uses RTAS functions to access PCI devices so we need to provide these with VOF. Implement some of the most important functions to allow booting Linux with VOF. With this the board is now usable without a binary ROM image and we can enable it by default as other boards. Signed-off-by: BALATON Zoltan Message-Id: <20210708215113.B3F747456E3@zero.eik.bme.hu> Signed-off-by: David Gibson --- default-configs/devices/ppc-softmmu.mak | 2 +- hw/ppc/pegasos2.c | 137 ++++++++++++++++++++++++ 2 files changed, 138 insertions(+), 1 deletion(-) diff --git a/default-configs/devices/ppc-softmmu.mak b/default-configs/devices/ppc-softmmu.mak index c2d41198cd..4535993d8d 100644 --- a/default-configs/devices/ppc-softmmu.mak +++ b/default-configs/devices/ppc-softmmu.mak @@ -14,7 +14,7 @@ CONFIG_SAM460EX=y CONFIG_MAC_OLDWORLD=y CONFIG_MAC_NEWWORLD=y -CONFIG_PEGASOS2=n +CONFIG_PEGASOS2=y # For PReP CONFIG_PREP=y diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index cf1dc53c83..9a6ae867e4 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -43,6 +43,7 @@ #define PROM_SIZE 0x80000 #define KVMPPC_HCALL_BASE 0xf000 +#define KVMPPC_H_RTAS (KVMPPC_HCALL_BASE + 0x0) #define KVMPPC_H_VOF_CLIENT (KVMPPC_HCALL_BASE + 0x5) #define H_SUCCESS 0 @@ -195,6 +196,30 @@ static void pegasos2_init(MachineState *machine) } } +static uint32_t pegasos2_pci_config_read(AddressSpace *as, int bus, + uint32_t addr, uint32_t len) +{ + hwaddr pcicfg = (bus ? 0xf1000c78 : 0xf1000cf8); + uint32_t val = 0xffffffff; + + stl_le_phys(as, pcicfg, addr | BIT(31)); + switch (len) { + case 4: + val = ldl_le_phys(as, pcicfg + 4); + break; + case 2: + val = lduw_le_phys(as, pcicfg + 4); + break; + case 1: + val = ldub_phys(as, pcicfg + 4); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid length\n", __func__); + break; + } + return val; +} + static void pegasos2_pci_config_write(AddressSpace *as, int bus, uint32_t addr, uint32_t len, uint32_t val) { @@ -304,6 +329,87 @@ static void pegasos2_machine_reset(MachineState *machine) pm->cpu->vhyp = PPC_VIRTUAL_HYPERVISOR(machine); } +enum pegasos2_rtas_tokens { + RTAS_RESTART_RTAS = 0, + RTAS_NVRAM_FETCH = 1, + RTAS_NVRAM_STORE = 2, + RTAS_GET_TIME_OF_DAY = 3, + RTAS_SET_TIME_OF_DAY = 4, + RTAS_EVENT_SCAN = 6, + RTAS_CHECK_EXCEPTION = 7, + RTAS_READ_PCI_CONFIG = 8, + RTAS_WRITE_PCI_CONFIG = 9, + RTAS_DISPLAY_CHARACTER = 10, + RTAS_SET_INDICATOR = 11, + RTAS_POWER_OFF = 17, + RTAS_SUSPEND = 18, + RTAS_HIBERNATE = 19, + RTAS_SYSTEM_REBOOT = 20, +}; + +static target_ulong pegasos2_rtas(PowerPCCPU *cpu, Pegasos2MachineState *pm, + target_ulong args_real) +{ + AddressSpace *as = CPU(cpu)->as; + uint32_t token = ldl_be_phys(as, args_real); + uint32_t nargs = ldl_be_phys(as, args_real + 4); + uint32_t nrets = ldl_be_phys(as, args_real + 8); + uint32_t args = args_real + 12; + uint32_t rets = args_real + 12 + nargs * 4; + + if (nrets < 1) { + qemu_log_mask(LOG_GUEST_ERROR, "Too few return values in RTAS call\n"); + return H_PARAMETER; + } + switch (token) { + case RTAS_READ_PCI_CONFIG: + { + uint32_t addr, len, val; + + if (nargs != 2 || nrets != 2) { + stl_be_phys(as, rets, -1); + return H_PARAMETER; + } + addr = ldl_be_phys(as, args); + len = ldl_be_phys(as, args + 4); + val = pegasos2_pci_config_read(as, !(addr >> 24), + addr & 0x0fffffff, len); + stl_be_phys(as, rets, 0); + stl_be_phys(as, rets + 4, val); + return H_SUCCESS; + } + case RTAS_WRITE_PCI_CONFIG: + { + uint32_t addr, len, val; + + if (nargs != 3 || nrets != 1) { + stl_be_phys(as, rets, -1); + return H_PARAMETER; + } + addr = ldl_be_phys(as, args); + len = ldl_be_phys(as, args + 4); + val = ldl_be_phys(as, args + 8); + pegasos2_pci_config_write(as, !(addr >> 24), + addr & 0x0fffffff, len, val); + stl_be_phys(as, rets, 0); + return H_SUCCESS; + } + case RTAS_DISPLAY_CHARACTER: + if (nargs != 1 || nrets != 1) { + stl_be_phys(as, rets, -1); + return H_PARAMETER; + } + qemu_log_mask(LOG_UNIMP, "%c", ldl_be_phys(as, args)); + stl_be_phys(as, rets, 0); + return H_SUCCESS; + default: + qemu_log_mask(LOG_UNIMP, "Unknown RTAS token %u (args=%u, rets=%u)\n", + token, nargs, nrets); + stl_be_phys(as, rets, 0); + return H_SUCCESS; + } +} + static void pegasos2_hypercall(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu) { Pegasos2MachineState *pm = PEGASOS2_MACHINE(vhyp); @@ -315,6 +421,8 @@ static void pegasos2_hypercall(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu) if (msr_pr) { qemu_log_mask(LOG_GUEST_ERROR, "Hypercall made with MSR[PR]=1\n"); env->gpr[3] = H_PRIVILEGE; + } else if (env->gpr[3] == KVMPPC_H_RTAS) { + env->gpr[3] = pegasos2_rtas(cpu, pm, env->gpr[4]); } else if (env->gpr[3] == KVMPPC_H_VOF_CLIENT) { int ret = vof_client_call(MACHINE(pm), pm->vof, pm->fdt_blob, env->gpr[4]); @@ -687,6 +795,35 @@ static void *build_fdt(MachineState *machine, int *fdt_size) qemu_fdt_setprop_string(fdt, "/failsafe", "device_type", "serial"); qemu_fdt_setprop_string(fdt, "/failsafe", "name", "failsafe"); + qemu_fdt_add_subnode(fdt, "/rtas"); + qemu_fdt_setprop_cell(fdt, "/rtas", "system-reboot", RTAS_SYSTEM_REBOOT); + qemu_fdt_setprop_cell(fdt, "/rtas", "hibernate", RTAS_HIBERNATE); + qemu_fdt_setprop_cell(fdt, "/rtas", "suspend", RTAS_SUSPEND); + qemu_fdt_setprop_cell(fdt, "/rtas", "power-off", RTAS_POWER_OFF); + qemu_fdt_setprop_cell(fdt, "/rtas", "set-indicator", RTAS_SET_INDICATOR); + qemu_fdt_setprop_cell(fdt, "/rtas", "display-character", + RTAS_DISPLAY_CHARACTER); + qemu_fdt_setprop_cell(fdt, "/rtas", "write-pci-config", + RTAS_WRITE_PCI_CONFIG); + qemu_fdt_setprop_cell(fdt, "/rtas", "read-pci-config", + RTAS_READ_PCI_CONFIG); + /* Pegasos2 firmware misspells check-exception and guests use that */ + qemu_fdt_setprop_cell(fdt, "/rtas", "check-execption", + RTAS_CHECK_EXCEPTION); + qemu_fdt_setprop_cell(fdt, "/rtas", "event-scan", RTAS_EVENT_SCAN); + qemu_fdt_setprop_cell(fdt, "/rtas", "set-time-of-day", + RTAS_SET_TIME_OF_DAY); + qemu_fdt_setprop_cell(fdt, "/rtas", "get-time-of-day", + RTAS_GET_TIME_OF_DAY); + qemu_fdt_setprop_cell(fdt, "/rtas", "nvram-store", RTAS_NVRAM_STORE); + qemu_fdt_setprop_cell(fdt, "/rtas", "nvram-fetch", RTAS_NVRAM_FETCH); + qemu_fdt_setprop_cell(fdt, "/rtas", "restart-rtas", RTAS_RESTART_RTAS); + qemu_fdt_setprop_cell(fdt, "/rtas", "rtas-error-log-max", 0); + qemu_fdt_setprop_cell(fdt, "/rtas", "rtas-event-scan-rate", 0); + qemu_fdt_setprop_cell(fdt, "/rtas", "rtas-display-device", 0); + qemu_fdt_setprop_cell(fdt, "/rtas", "rtas-size", 20); + qemu_fdt_setprop_cell(fdt, "/rtas", "rtas-version", 1); + /* cpus */ qemu_fdt_add_subnode(fdt, "/cpus"); qemu_fdt_setprop_cell(fdt, "/cpus", "#cpus", 1); From 89bb5a4dfdef8316e840ab090ef04a5b7117731b Mon Sep 17 00:00:00 2001 From: "Lucas Mateus Castro (alqotel)" Date: Thu, 8 Jul 2021 13:49:54 -0300 Subject: [PATCH 104/272] target/ppc: Don't compile ppc_tlb_invalid_all without TCG The function ppc_tlb_invalid_all is not compiled anymore in a TCG-less environment, and the call to that function has been disabled in this situation Signed-off-by: Lucas Mateus Castro (alqotel) Message-Id: <20210708164957.28096-2-lucas.araujo@eldorado.org.br> Signed-off-by: David Gibson --- target/ppc/cpu_init.c | 2 ++ target/ppc/mmu_helper.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 6f8ce010ba..505a0ed6ac 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -8847,9 +8847,11 @@ static void ppc_cpu_reset(DeviceState *dev) #if !defined(CONFIG_USER_ONLY) env->nip = env->hreset_vector | env->excp_prefix; +#if defined(CONFIG_TCG) if (env->mmu_model != POWERPC_MMU_REAL) { ppc_tlb_invalidate_all(env); } +#endif /* CONFIG_TCG */ #endif hreg_compute_hflags(env); diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index 47e9f9529e..869d24d301 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -825,6 +825,7 @@ static int mmubooke_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, return ret; } +#ifdef CONFIG_TCG static void booke206_flush_tlb(CPUPPCState *env, int flags, const int check_iprot) { @@ -846,6 +847,7 @@ static void booke206_flush_tlb(CPUPPCState *env, int flags, tlb_flush(env_cpu(env)); } +#endif static hwaddr booke206_tlb_to_page_size(CPUPPCState *env, ppcmas_tlb_t *tlb) @@ -1901,6 +1903,7 @@ void helper_store_601_batl(CPUPPCState *env, uint32_t nr, target_ulong value) } #endif +#ifdef CONFIG_TCG /*****************************************************************************/ /* TLB management */ void ppc_tlb_invalidate_all(CPUPPCState *env) @@ -1944,6 +1947,7 @@ void ppc_tlb_invalidate_all(CPUPPCState *env) break; } } +#endif #ifdef CONFIG_TCG void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr) From 21bde1ecb6cecba1d2f0219a1b79c240bed78749 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Thu, 8 Jul 2021 16:56:25 +1000 Subject: [PATCH 105/272] spapr: Fix implementation of Open Firmware client interface This addresses the comments from v22. The functional changes are (the VOF ones need retesting with Pegasos2): (VOF) setprop will start failing if the machine class callback did not handle it; (VOF) unit addresses are lowered in path_offset(); (SPAPR) /chosen/bootargs is initialized from kernel_cmdline if the client did not change it. Fixes: 5c991e5d4378 ("spapr: Implement Open Firmware client interface") Cc: BALATON Zoltan Signed-off-by: Alexey Kardashevskiy Message-Id: <20210708065625.548396-1-aik@ozlabs.ru> Tested-by: BALATON Zoltan Signed-off-by: David Gibson --- MAINTAINERS | 4 ++-- hw/ppc/spapr.c | 10 +--------- hw/ppc/spapr_hcall.c | 5 ++--- hw/ppc/spapr_vof.c | 32 +++++++++++++++++++++++--------- hw/ppc/vof.c | 30 +++++++++++++++++------------- include/hw/ppc/spapr.h | 3 +-- pc-bios/vof.bin | Bin 3784 -> 3456 bytes pc-bios/vof/ci.c | 2 +- pc-bios/vof/libc.c | 26 -------------------------- pc-bios/vof/main.c | 2 +- pc-bios/vof/vof.h | 2 -- 11 files changed, 48 insertions(+), 68 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index ce122eeced..89d71b42b2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1362,8 +1362,8 @@ F: include/hw/pci-host/mv64361.h Virtual Open Firmware (VOF) M: Alexey Kardashevskiy -M: David Gibson -M: Greg Kurz +R: David Gibson +R: Greg Kurz L: qemu-ppc@nongnu.org S: Maintained F: hw/ppc/spapr_vof* diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index e9b6d0f587..3808d47053 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1645,15 +1645,7 @@ static void spapr_machine_reset(MachineState *machine) fdt = spapr_build_fdt(spapr, true, FDT_MAX_SIZE); if (spapr->vof) { - target_ulong stack_ptr = 0; - - spapr_vof_reset(spapr, fdt, &stack_ptr, &error_fatal); - - spapr_cpu_set_entry_state(first_ppc_cpu, SPAPR_ENTRY_POINT, - stack_ptr, spapr->initrd_base, - spapr->initrd_size); - /* VOF is 32bit BE so enforce MSR here */ - first_ppc_cpu->env.msr &= ~((1ULL << MSR_SF) | (1ULL << MSR_LE)); + spapr_vof_reset(spapr, fdt, &error_fatal); /* * Do not pack the FDT as the client may change properties. * VOF client does not expect the FDT so we do not load it to the VM. diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 80ae8eaadd..0e9a5b2e40 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -1080,7 +1080,7 @@ target_ulong do_client_architecture_support(PowerPCCPU *cpu, SpaprOptionVector *ov1_guest, *ov5_guest; bool guest_radix; bool raw_mode_supported = false; - bool guest_xive, reset_fdt = false; + bool guest_xive; CPUState *cs; void *fdt; uint32_t max_compat = spapr->max_compat_pvr; @@ -1233,8 +1233,7 @@ target_ulong do_client_architecture_support(PowerPCCPU *cpu, spapr_setup_hpt(spapr); } - reset_fdt = spapr->vof != NULL; - fdt = spapr_build_fdt(spapr, reset_fdt, fdt_bufsize); + fdt = spapr_build_fdt(spapr, spapr->vof != NULL, fdt_bufsize); g_free(spapr->fdt_blob); spapr->fdt_size = fdt_totalsize(fdt); spapr->fdt_initial_size = spapr->fdt_size; diff --git a/hw/ppc/spapr_vof.c b/hw/ppc/spapr_vof.c index 131a03fec0..40ce8fe003 100644 --- a/hw/ppc/spapr_vof.c +++ b/hw/ppc/spapr_vof.c @@ -8,6 +8,7 @@ #include "qapi/error.h" #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" +#include "hw/ppc/spapr_cpu_core.h" #include "hw/ppc/fdt.h" #include "hw/ppc/vof.h" #include "sysemu/sysemu.h" @@ -29,13 +30,19 @@ target_ulong spapr_h_vof_client(PowerPCCPU *cpu, SpaprMachineState *spapr, void spapr_vof_client_dt_finalize(SpaprMachineState *spapr, void *fdt) { char *stdout_path = spapr_vio_stdout_path(spapr->vio_bus); - int chosen; vof_build_dt(fdt, spapr->vof); - _FDT(chosen = fdt_path_offset(fdt, "/chosen")); - _FDT(fdt_setprop_string(fdt, chosen, "bootargs", - spapr->vof->bootargs ? : "")); + if (spapr->vof->bootargs) { + int chosen; + + _FDT(chosen = fdt_path_offset(fdt, "/chosen")); + /* + * If the client did not change "bootargs", spapr_dt_chosen() must have + * stored machine->kernel_cmdline in it before getting here. + */ + _FDT(fdt_setprop_string(fdt, chosen, "bootargs", spapr->vof->bootargs)); + } /* * SLOF-less setup requires an open instance of stdout for early @@ -48,20 +55,21 @@ void spapr_vof_client_dt_finalize(SpaprMachineState *spapr, void *fdt) } } -void spapr_vof_reset(SpaprMachineState *spapr, void *fdt, - target_ulong *stack_ptr, Error **errp) +void spapr_vof_reset(SpaprMachineState *spapr, void *fdt, Error **errp) { + target_ulong stack_ptr; Vof *vof = spapr->vof; + PowerPCCPU *first_ppc_cpu = POWERPC_CPU(first_cpu); vof_init(vof, spapr->rma_size, errp); - *stack_ptr = vof_claim(vof, 0, VOF_STACK_SIZE, VOF_STACK_SIZE); - if (*stack_ptr == -1) { + stack_ptr = vof_claim(vof, 0, VOF_STACK_SIZE, VOF_STACK_SIZE); + if (stack_ptr == -1) { error_setg(errp, "Memory allocation for stack failed"); return; } /* Stack grows downwards plus reserve space for the minimum stack frame */ - *stack_ptr += VOF_STACK_SIZE - 0x20; + stack_ptr += VOF_STACK_SIZE - 0x20; if (spapr->kernel_size && vof_claim(vof, spapr->kernel_addr, spapr->kernel_size, 0) == -1) { @@ -77,6 +85,12 @@ void spapr_vof_reset(SpaprMachineState *spapr, void *fdt, spapr_vof_client_dt_finalize(spapr, fdt); + spapr_cpu_set_entry_state(first_ppc_cpu, SPAPR_ENTRY_POINT, + stack_ptr, spapr->initrd_base, + spapr->initrd_size); + /* VOF is 32bit BE so enforce MSR here */ + first_ppc_cpu->env.msr &= ~((1ULL << MSR_SF) | (1ULL << MSR_LE)); + /* * At this point the expected allocation map is: * diff --git a/hw/ppc/vof.c b/hw/ppc/vof.c index 47c86e394e..81f6596215 100644 --- a/hw/ppc/vof.c +++ b/hw/ppc/vof.c @@ -144,15 +144,16 @@ static int path_offset(const void *fdt, const char *path) * the lower case forms of the hexadecimal digits in the range a..f, * suppressing leading zeros". */ - at = strchr(path, '@'); - if (!at) { - return fdt_path_offset(fdt, path); + p = g_strdup(path); + for (at = strchr(p, '@'); at && *at; ) { + if (*at == '/') { + at = strchr(at, '@'); + } else { + *at = tolower(*at); + ++at; + } } - p = g_strdup(path); - for (at = at - path + p + 1; *at; ++at) { - *at = tolower(*at); - } return fdt_path_offset(fdt, p); } @@ -300,6 +301,7 @@ static uint32_t vof_setprop(MachineState *ms, void *fdt, Vof *vof, char trval[64] = ""; char nodepath[VOF_MAX_PATH] = ""; Object *vmo = object_dynamic_cast(OBJECT(ms), TYPE_VOF_MACHINE_IF); + VofMachineIfClass *vmc; g_autofree char *val = NULL; if (vallen > VOF_MAX_SETPROPLEN) { @@ -322,13 +324,13 @@ static uint32_t vof_setprop(MachineState *ms, void *fdt, Vof *vof, goto trace_exit; } - if (vmo) { - VofMachineIfClass *vmc = VOF_MACHINE_GET_CLASS(vmo); + if (!vmo) { + goto trace_exit; + } - if (vmc->setprop && - !vmc->setprop(ms, nodepath, propname, val, vallen)) { - goto trace_exit; - } + vmc = VOF_MACHINE_GET_CLASS(vmo); + if (!vmc->setprop || !vmc->setprop(ms, nodepath, propname, val, vallen)) { + goto trace_exit; } ret = fdt_setprop(fdt, offset, propname, val, vallen); @@ -919,6 +921,8 @@ static uint32_t vof_client_handle(MachineState *ms, void *fdt, Vof *vof, ret = -1; } +#undef cmpserv + return ret; } diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index a25e69fe4c..779f707fb8 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -964,8 +964,7 @@ void spapr_set_all_lpcrs(target_ulong value, target_ulong mask); hwaddr spapr_get_rtas_addr(void); bool spapr_memory_hot_unplug_supported(SpaprMachineState *spapr); -void spapr_vof_reset(SpaprMachineState *spapr, void *fdt, - target_ulong *stack_ptr, Error **errp); +void spapr_vof_reset(SpaprMachineState *spapr, void *fdt, Error **errp); void spapr_vof_quiesce(MachineState *ms); bool spapr_vof_setprop(MachineState *ms, const char *path, const char *propname, void *val, int vallen); diff --git a/pc-bios/vof.bin b/pc-bios/vof.bin index 1ec670be82134adcb5ae128732aff6e371281360..300cb7c7f9d9d77ffa7cbb7f0f26919246ef2d14 100755 GIT binary patch delta 151 zcmX>h+aSGxjghy9!GnR}jEw^WLxNM!WMRf~rtUM7dl>VWx??8)VQgaRda${HDTtA& zvuE-Z<}9X8h0P8uhZvdKVH uz%*HauS<{T1Oo%l10a6Gz`)A@#2gF^j0r&O17wQ;u?!Gv0I>lOTL1tL)F-q6 delta 369 zcmZpWJ|Vk-jghyH!GnR}jEw^WLxNM^WMRf~re2ZBJ&buwJxeD4VQgaR(b(L;6vW8X zb!GAu<}9YJjLi-#hZvbUmP}@0i(~3=nViCw#?*di@)@=|ruK%-KI~hW>f;$?8toY* zYPdWc92yuV0NDzSK(SgaFA*RO7I$o9r~rvuYX1KZ5(CLiv}fQz5(BFTit$(~FfagV z0iZ(-fNFUxwf_GHi38PgSaJf{@(diEUJMK~JsB8)VgeSHnhcB}4M4>LAOnF8VQ_5t ze*$Pg43IAYlml5L12P2J@X0?olqCgl>7J~^X|b7u>j2Y40hP%oc)IlX1Q;0jG=SIy dh=FGF1u!r$CIGPykR1cWDL`BR#1%l?005cvU&jCd diff --git a/pc-bios/vof/ci.c b/pc-bios/vof/ci.c index 2b56050238..fc4821b3e9 100644 --- a/pc-bios/vof/ci.c +++ b/pc-bios/vof/ci.c @@ -69,7 +69,7 @@ static int call_ci(const char *service, int nargs, int nret, ...) } if (ci_entry((uint32_t)(&args)) < 0) { - return PROM_ERROR; + return -1; } return (nret > 0) ? args.args[nargs] : 0; diff --git a/pc-bios/vof/libc.c b/pc-bios/vof/libc.c index 00c10e6e7d..fdbc30f777 100644 --- a/pc-bios/vof/libc.c +++ b/pc-bios/vof/libc.c @@ -54,32 +54,6 @@ int memcmp(const void *ptr1, const void *ptr2, size_t n) return 0; } -void *memmove(void *dest, const void *src, size_t n) -{ - char *cdest; - const char *csrc; - int i; - - /* Do the buffers overlap in a bad way? */ - if (src < dest && src + n >= dest) { - /* Copy from end to start */ - cdest = dest + n - 1; - csrc = src + n - 1; - for (i = 0; i < n; i++) { - *cdest-- = *csrc--; - } - } else { - /* Normal copy is possible */ - cdest = dest; - csrc = src; - for (i = 0; i < n; i++) { - *cdest++ = *csrc++; - } - } - - return dest; -} - void *memset(void *dest, int c, size_t size) { unsigned char *d = (unsigned char *)dest; diff --git a/pc-bios/vof/main.c b/pc-bios/vof/main.c index 9fc30d2d09..0f0f6b4cb1 100644 --- a/pc-bios/vof/main.c +++ b/pc-bios/vof/main.c @@ -6,7 +6,7 @@ void do_boot(unsigned long addr, unsigned long _r3, unsigned long _r4) register unsigned long r4 __asm__("r4") = _r4; register unsigned long r5 __asm__("r5") = (unsigned long) _prom_entry; - ((client *)(uint32_t)addr)(); + ((void (*)(void))(uint32_t)addr)(); } void entry_c(void) diff --git a/pc-bios/vof/vof.h b/pc-bios/vof/vof.h index 2d89580769..5f12c077f5 100644 --- a/pc-bios/vof/vof.h +++ b/pc-bios/vof/vof.h @@ -10,11 +10,9 @@ typedef unsigned short uint16_t; typedef unsigned long uint32_t; typedef unsigned long long uint64_t; #define NULL (0) -#define PROM_ERROR (-1u) typedef unsigned long ihandle; typedef unsigned long phandle; typedef int size_t; -typedef void client(void); /* globals */ extern void _prom_entry(void); /* OF CI entry point (i.e. this firmware) */ From 327d4b7f3f26eb19b8bc2b1b54afa6874612efdd Mon Sep 17 00:00:00 2001 From: Bharata B Rao Date: Tue, 6 Jul 2021 16:54:39 +0530 Subject: [PATCH 106/272] linux-headers: Update Update to mainline commit: 79160a603bdb ("Merge tag 'usb-5.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb" Signed-off-by: Bharata B Rao Message-Id: <20210706112440.1449562-2-bharata@linux.ibm.com> Signed-off-by: David Gibson --- include/standard-headers/asm-x86/kvm_para.h | 13 +++ include/standard-headers/drm/drm_fourcc.h | 7 ++ include/standard-headers/linux/ethtool.h | 4 +- .../linux/input-event-codes.h | 1 + include/standard-headers/linux/virtio_ids.h | 2 +- include/standard-headers/linux/virtio_vsock.h | 9 ++ linux-headers/asm-arm64/kvm.h | 11 ++ linux-headers/asm-generic/mman-common.h | 3 + linux-headers/asm-generic/unistd.h | 4 +- linux-headers/asm-mips/mman.h | 3 + linux-headers/asm-mips/unistd_n32.h | 1 + linux-headers/asm-mips/unistd_n64.h | 1 + linux-headers/asm-mips/unistd_o32.h | 1 + linux-headers/asm-powerpc/unistd_32.h | 1 + linux-headers/asm-powerpc/unistd_64.h | 1 + linux-headers/asm-s390/unistd_32.h | 1 + linux-headers/asm-s390/unistd_64.h | 1 + linux-headers/asm-x86/kvm.h | 13 +++ linux-headers/asm-x86/unistd_32.h | 7 +- linux-headers/asm-x86/unistd_64.h | 7 +- linux-headers/asm-x86/unistd_x32.h | 7 +- linux-headers/linux/kvm.h | 105 ++++++++++++++++++ linux-headers/linux/userfaultfd.h | 11 +- 23 files changed, 197 insertions(+), 17 deletions(-) diff --git a/include/standard-headers/asm-x86/kvm_para.h b/include/standard-headers/asm-x86/kvm_para.h index 215d01b4ec..204cfb8640 100644 --- a/include/standard-headers/asm-x86/kvm_para.h +++ b/include/standard-headers/asm-x86/kvm_para.h @@ -33,6 +33,8 @@ #define KVM_FEATURE_PV_SCHED_YIELD 13 #define KVM_FEATURE_ASYNC_PF_INT 14 #define KVM_FEATURE_MSI_EXT_DEST_ID 15 +#define KVM_FEATURE_HC_MAP_GPA_RANGE 16 +#define KVM_FEATURE_MIGRATION_CONTROL 17 #define KVM_HINTS_REALTIME 0 @@ -54,6 +56,7 @@ #define MSR_KVM_POLL_CONTROL 0x4b564d05 #define MSR_KVM_ASYNC_PF_INT 0x4b564d06 #define MSR_KVM_ASYNC_PF_ACK 0x4b564d07 +#define MSR_KVM_MIGRATION_CONTROL 0x4b564d08 struct kvm_steal_time { uint64_t steal; @@ -90,6 +93,16 @@ struct kvm_clock_pairing { /* MSR_KVM_ASYNC_PF_INT */ #define KVM_ASYNC_PF_VEC_MASK GENMASK(7, 0) +/* MSR_KVM_MIGRATION_CONTROL */ +#define KVM_MIGRATION_READY (1 << 0) + +/* KVM_HC_MAP_GPA_RANGE */ +#define KVM_MAP_GPA_RANGE_PAGE_SZ_4K 0 +#define KVM_MAP_GPA_RANGE_PAGE_SZ_2M (1 << 0) +#define KVM_MAP_GPA_RANGE_PAGE_SZ_1G (1 << 1) +#define KVM_MAP_GPA_RANGE_ENC_STAT(n) (n << 4) +#define KVM_MAP_GPA_RANGE_ENCRYPTED KVM_MAP_GPA_RANGE_ENC_STAT(1) +#define KVM_MAP_GPA_RANGE_DECRYPTED KVM_MAP_GPA_RANGE_ENC_STAT(0) /* Operations for KVM_HC_MMU_OP */ #define KVM_MMU_OP_WRITE_PTE 1 diff --git a/include/standard-headers/drm/drm_fourcc.h b/include/standard-headers/drm/drm_fourcc.h index a61ae520c2..352b51fd0a 100644 --- a/include/standard-headers/drm/drm_fourcc.h +++ b/include/standard-headers/drm/drm_fourcc.h @@ -167,6 +167,13 @@ extern "C" { #define DRM_FORMAT_RGBA1010102 fourcc_code('R', 'A', '3', '0') /* [31:0] R:G:B:A 10:10:10:2 little endian */ #define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0') /* [31:0] B:G:R:A 10:10:10:2 little endian */ +/* 64 bpp RGB */ +#define DRM_FORMAT_XRGB16161616 fourcc_code('X', 'R', '4', '8') /* [63:0] x:R:G:B 16:16:16:16 little endian */ +#define DRM_FORMAT_XBGR16161616 fourcc_code('X', 'B', '4', '8') /* [63:0] x:B:G:R 16:16:16:16 little endian */ + +#define DRM_FORMAT_ARGB16161616 fourcc_code('A', 'R', '4', '8') /* [63:0] A:R:G:B 16:16:16:16 little endian */ +#define DRM_FORMAT_ABGR16161616 fourcc_code('A', 'B', '4', '8') /* [63:0] A:B:G:R 16:16:16:16 little endian */ + /* * Floating point 64bpp RGB * IEEE 754-2008 binary16 half-precision float diff --git a/include/standard-headers/linux/ethtool.h b/include/standard-headers/linux/ethtool.h index 218d944a17..053d3fafdf 100644 --- a/include/standard-headers/linux/ethtool.h +++ b/include/standard-headers/linux/ethtool.h @@ -233,7 +233,7 @@ enum tunable_id { ETHTOOL_PFC_PREVENTION_TOUT, /* timeout in msecs */ /* * Add your fresh new tunable attribute above and remember to update - * tunable_strings[] in net/core/ethtool.c + * tunable_strings[] in net/ethtool/common.c */ __ETHTOOL_TUNABLE_COUNT, }; @@ -297,7 +297,7 @@ enum phy_tunable_id { ETHTOOL_PHY_EDPD, /* * Add your fresh new phy tunable attribute above and remember to update - * phy_tunable_strings[] in net/core/ethtool.c + * phy_tunable_strings[] in net/ethtool/common.c */ __ETHTOOL_PHY_TUNABLE_COUNT, }; diff --git a/include/standard-headers/linux/input-event-codes.h b/include/standard-headers/linux/input-event-codes.h index c403b9cb0d..b5e86b40ab 100644 --- a/include/standard-headers/linux/input-event-codes.h +++ b/include/standard-headers/linux/input-event-codes.h @@ -611,6 +611,7 @@ #define KEY_VOICECOMMAND 0x246 /* Listening Voice Command */ #define KEY_ASSISTANT 0x247 /* AL Context-aware desktop assistant */ #define KEY_KBD_LAYOUT_NEXT 0x248 /* AC Next Keyboard Layout Select */ +#define KEY_EMOJI_PICKER 0x249 /* Show/hide emoji picker (HUTRR101) */ #define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */ #define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */ diff --git a/include/standard-headers/linux/virtio_ids.h b/include/standard-headers/linux/virtio_ids.h index f0c35ce862..4fe842c3a3 100644 --- a/include/standard-headers/linux/virtio_ids.h +++ b/include/standard-headers/linux/virtio_ids.h @@ -54,7 +54,7 @@ #define VIRTIO_ID_SOUND 25 /* virtio sound */ #define VIRTIO_ID_FS 26 /* virtio filesystem */ #define VIRTIO_ID_PMEM 27 /* virtio pmem */ -#define VIRTIO_ID_BT 28 /* virtio bluetooth */ #define VIRTIO_ID_MAC80211_HWSIM 29 /* virtio mac80211-hwsim */ +#define VIRTIO_ID_BT 40 /* virtio bluetooth */ #endif /* _LINUX_VIRTIO_IDS_H */ diff --git a/include/standard-headers/linux/virtio_vsock.h b/include/standard-headers/linux/virtio_vsock.h index be443211ce..3a23488e42 100644 --- a/include/standard-headers/linux/virtio_vsock.h +++ b/include/standard-headers/linux/virtio_vsock.h @@ -38,6 +38,9 @@ #include "standard-headers/linux/virtio_ids.h" #include "standard-headers/linux/virtio_config.h" +/* The feature bitmap for virtio vsock */ +#define VIRTIO_VSOCK_F_SEQPACKET 1 /* SOCK_SEQPACKET supported */ + struct virtio_vsock_config { uint64_t guest_cid; } QEMU_PACKED; @@ -65,6 +68,7 @@ struct virtio_vsock_hdr { enum virtio_vsock_type { VIRTIO_VSOCK_TYPE_STREAM = 1, + VIRTIO_VSOCK_TYPE_SEQPACKET = 2, }; enum virtio_vsock_op { @@ -91,4 +95,9 @@ enum virtio_vsock_shutdown { VIRTIO_VSOCK_SHUTDOWN_SEND = 2, }; +/* VIRTIO_VSOCK_OP_RW flags values */ +enum virtio_vsock_rw { + VIRTIO_VSOCK_SEQ_EOR = 1, +}; + #endif /* _LINUX_VIRTIO_VSOCK_H */ diff --git a/linux-headers/asm-arm64/kvm.h b/linux-headers/asm-arm64/kvm.h index b6a0eaa32a..3d2ce9912d 100644 --- a/linux-headers/asm-arm64/kvm.h +++ b/linux-headers/asm-arm64/kvm.h @@ -184,6 +184,17 @@ struct kvm_vcpu_events { __u32 reserved[12]; }; +struct kvm_arm_copy_mte_tags { + __u64 guest_ipa; + __u64 length; + void *addr; + __u64 flags; + __u64 reserved[2]; +}; + +#define KVM_ARM_TAGS_TO_GUEST 0 +#define KVM_ARM_TAGS_FROM_GUEST 1 + /* 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 diff --git a/linux-headers/asm-generic/mman-common.h b/linux-headers/asm-generic/mman-common.h index f94f65d429..1567a3294c 100644 --- a/linux-headers/asm-generic/mman-common.h +++ b/linux-headers/asm-generic/mman-common.h @@ -72,6 +72,9 @@ #define MADV_COLD 20 /* deactivate these pages */ #define MADV_PAGEOUT 21 /* reclaim these pages */ +#define MADV_POPULATE_READ 22 /* populate (prefault) page tables readable */ +#define MADV_POPULATE_WRITE 23 /* populate (prefault) page tables writable */ + /* compatibility flags */ #define MAP_FILE 0 diff --git a/linux-headers/asm-generic/unistd.h b/linux-headers/asm-generic/unistd.h index 6de5a7fc06..f211961ce1 100644 --- a/linux-headers/asm-generic/unistd.h +++ b/linux-headers/asm-generic/unistd.h @@ -863,8 +863,8 @@ __SYSCALL(__NR_process_madvise, sys_process_madvise) __SC_COMP(__NR_epoll_pwait2, sys_epoll_pwait2, compat_sys_epoll_pwait2) #define __NR_mount_setattr 442 __SYSCALL(__NR_mount_setattr, sys_mount_setattr) -#define __NR_quotactl_path 443 -__SYSCALL(__NR_quotactl_path, sys_quotactl_path) +#define __NR_quotactl_fd 443 +__SYSCALL(__NR_quotactl_fd, sys_quotactl_fd) #define __NR_landlock_create_ruleset 444 __SYSCALL(__NR_landlock_create_ruleset, sys_landlock_create_ruleset) diff --git a/linux-headers/asm-mips/mman.h b/linux-headers/asm-mips/mman.h index 57dc2ac4f8..40b210c65a 100644 --- a/linux-headers/asm-mips/mman.h +++ b/linux-headers/asm-mips/mman.h @@ -98,6 +98,9 @@ #define MADV_COLD 20 /* deactivate these pages */ #define MADV_PAGEOUT 21 /* reclaim these pages */ +#define MADV_POPULATE_READ 22 /* populate (prefault) page tables readable */ +#define MADV_POPULATE_WRITE 23 /* populate (prefault) page tables writable */ + /* compatibility flags */ #define MAP_FILE 0 diff --git a/linux-headers/asm-mips/unistd_n32.h b/linux-headers/asm-mips/unistd_n32.h index fce51fee09..09cd297698 100644 --- a/linux-headers/asm-mips/unistd_n32.h +++ b/linux-headers/asm-mips/unistd_n32.h @@ -372,6 +372,7 @@ #define __NR_process_madvise (__NR_Linux + 440) #define __NR_epoll_pwait2 (__NR_Linux + 441) #define __NR_mount_setattr (__NR_Linux + 442) +#define __NR_quotactl_fd (__NR_Linux + 443) #define __NR_landlock_create_ruleset (__NR_Linux + 444) #define __NR_landlock_add_rule (__NR_Linux + 445) #define __NR_landlock_restrict_self (__NR_Linux + 446) diff --git a/linux-headers/asm-mips/unistd_n64.h b/linux-headers/asm-mips/unistd_n64.h index 0996001802..780e0cead6 100644 --- a/linux-headers/asm-mips/unistd_n64.h +++ b/linux-headers/asm-mips/unistd_n64.h @@ -348,6 +348,7 @@ #define __NR_process_madvise (__NR_Linux + 440) #define __NR_epoll_pwait2 (__NR_Linux + 441) #define __NR_mount_setattr (__NR_Linux + 442) +#define __NR_quotactl_fd (__NR_Linux + 443) #define __NR_landlock_create_ruleset (__NR_Linux + 444) #define __NR_landlock_add_rule (__NR_Linux + 445) #define __NR_landlock_restrict_self (__NR_Linux + 446) diff --git a/linux-headers/asm-mips/unistd_o32.h b/linux-headers/asm-mips/unistd_o32.h index 954303ad69..06a2b3b55e 100644 --- a/linux-headers/asm-mips/unistd_o32.h +++ b/linux-headers/asm-mips/unistd_o32.h @@ -418,6 +418,7 @@ #define __NR_process_madvise (__NR_Linux + 440) #define __NR_epoll_pwait2 (__NR_Linux + 441) #define __NR_mount_setattr (__NR_Linux + 442) +#define __NR_quotactl_fd (__NR_Linux + 443) #define __NR_landlock_create_ruleset (__NR_Linux + 444) #define __NR_landlock_add_rule (__NR_Linux + 445) #define __NR_landlock_restrict_self (__NR_Linux + 446) diff --git a/linux-headers/asm-powerpc/unistd_32.h b/linux-headers/asm-powerpc/unistd_32.h index 9155778c19..cd5a8a41b2 100644 --- a/linux-headers/asm-powerpc/unistd_32.h +++ b/linux-headers/asm-powerpc/unistd_32.h @@ -425,6 +425,7 @@ #define __NR_process_madvise 440 #define __NR_epoll_pwait2 441 #define __NR_mount_setattr 442 +#define __NR_quotactl_fd 443 #define __NR_landlock_create_ruleset 444 #define __NR_landlock_add_rule 445 #define __NR_landlock_restrict_self 446 diff --git a/linux-headers/asm-powerpc/unistd_64.h b/linux-headers/asm-powerpc/unistd_64.h index 3cefa88932..8458effa8d 100644 --- a/linux-headers/asm-powerpc/unistd_64.h +++ b/linux-headers/asm-powerpc/unistd_64.h @@ -397,6 +397,7 @@ #define __NR_process_madvise 440 #define __NR_epoll_pwait2 441 #define __NR_mount_setattr 442 +#define __NR_quotactl_fd 443 #define __NR_landlock_create_ruleset 444 #define __NR_landlock_add_rule 445 #define __NR_landlock_restrict_self 446 diff --git a/linux-headers/asm-s390/unistd_32.h b/linux-headers/asm-s390/unistd_32.h index e8cd34334f..0c3cd299e4 100644 --- a/linux-headers/asm-s390/unistd_32.h +++ b/linux-headers/asm-s390/unistd_32.h @@ -415,6 +415,7 @@ #define __NR_process_madvise 440 #define __NR_epoll_pwait2 441 #define __NR_mount_setattr 442 +#define __NR_quotactl_fd 443 #define __NR_landlock_create_ruleset 444 #define __NR_landlock_add_rule 445 #define __NR_landlock_restrict_self 446 diff --git a/linux-headers/asm-s390/unistd_64.h b/linux-headers/asm-s390/unistd_64.h index 86830e1e83..8dfc08b5e6 100644 --- a/linux-headers/asm-s390/unistd_64.h +++ b/linux-headers/asm-s390/unistd_64.h @@ -363,6 +363,7 @@ #define __NR_process_madvise 440 #define __NR_epoll_pwait2 441 #define __NR_mount_setattr 442 +#define __NR_quotactl_fd 443 #define __NR_landlock_create_ruleset 444 #define __NR_landlock_add_rule 445 #define __NR_landlock_restrict_self 446 diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h index 0662f644aa..a6c327f8ad 100644 --- a/linux-headers/asm-x86/kvm.h +++ b/linux-headers/asm-x86/kvm.h @@ -159,6 +159,19 @@ struct kvm_sregs { __u64 interrupt_bitmap[(KVM_NR_INTERRUPTS + 63) / 64]; }; +struct kvm_sregs2 { + /* out (KVM_GET_SREGS2) / in (KVM_SET_SREGS2) */ + struct kvm_segment cs, ds, es, fs, gs, ss; + struct kvm_segment tr, ldt; + struct kvm_dtable gdt, idt; + __u64 cr0, cr2, cr3, cr4, cr8; + __u64 efer; + __u64 apic_base; + __u64 flags; + __u64 pdptrs[4]; +}; +#define KVM_SREGS2_FLAGS_PDPTRS_VALID 1 + /* for KVM_GET_FPU and KVM_SET_FPU */ struct kvm_fpu { __u8 fpr[8][16]; diff --git a/linux-headers/asm-x86/unistd_32.h b/linux-headers/asm-x86/unistd_32.h index 8f6ac8c19f..66e96c0c68 100644 --- a/linux-headers/asm-x86/unistd_32.h +++ b/linux-headers/asm-x86/unistd_32.h @@ -1,5 +1,5 @@ -#ifndef _ASM_X86_UNISTD_32_H -#define _ASM_X86_UNISTD_32_H 1 +#ifndef _ASM_UNISTD_32_H +#define _ASM_UNISTD_32_H #define __NR_restart_syscall 0 #define __NR_exit 1 @@ -433,9 +433,10 @@ #define __NR_process_madvise 440 #define __NR_epoll_pwait2 441 #define __NR_mount_setattr 442 +#define __NR_quotactl_fd 443 #define __NR_landlock_create_ruleset 444 #define __NR_landlock_add_rule 445 #define __NR_landlock_restrict_self 446 -#endif /* _ASM_X86_UNISTD_32_H */ +#endif /* _ASM_UNISTD_32_H */ diff --git a/linux-headers/asm-x86/unistd_64.h b/linux-headers/asm-x86/unistd_64.h index bb187a9268..b8ff6f14ee 100644 --- a/linux-headers/asm-x86/unistd_64.h +++ b/linux-headers/asm-x86/unistd_64.h @@ -1,5 +1,5 @@ -#ifndef _ASM_X86_UNISTD_64_H -#define _ASM_X86_UNISTD_64_H 1 +#ifndef _ASM_UNISTD_64_H +#define _ASM_UNISTD_64_H #define __NR_read 0 #define __NR_write 1 @@ -355,9 +355,10 @@ #define __NR_process_madvise 440 #define __NR_epoll_pwait2 441 #define __NR_mount_setattr 442 +#define __NR_quotactl_fd 443 #define __NR_landlock_create_ruleset 444 #define __NR_landlock_add_rule 445 #define __NR_landlock_restrict_self 446 -#endif /* _ASM_X86_UNISTD_64_H */ +#endif /* _ASM_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/unistd_x32.h b/linux-headers/asm-x86/unistd_x32.h index 4edd0103ac..06a1097c15 100644 --- a/linux-headers/asm-x86/unistd_x32.h +++ b/linux-headers/asm-x86/unistd_x32.h @@ -1,5 +1,5 @@ -#ifndef _ASM_X86_UNISTD_X32_H -#define _ASM_X86_UNISTD_X32_H 1 +#ifndef _ASM_UNISTD_X32_H +#define _ASM_UNISTD_X32_H #define __NR_read (__X32_SYSCALL_BIT + 0) #define __NR_write (__X32_SYSCALL_BIT + 1) @@ -308,6 +308,7 @@ #define __NR_process_madvise (__X32_SYSCALL_BIT + 440) #define __NR_epoll_pwait2 (__X32_SYSCALL_BIT + 441) #define __NR_mount_setattr (__X32_SYSCALL_BIT + 442) +#define __NR_quotactl_fd (__X32_SYSCALL_BIT + 443) #define __NR_landlock_create_ruleset (__X32_SYSCALL_BIT + 444) #define __NR_landlock_add_rule (__X32_SYSCALL_BIT + 445) #define __NR_landlock_restrict_self (__X32_SYSCALL_BIT + 446) @@ -349,4 +350,4 @@ #define __NR_pwritev2 (__X32_SYSCALL_BIT + 547) -#endif /* _ASM_X86_UNISTD_X32_H */ +#endif /* _ASM_UNISTD_X32_H */ diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 20d6a263bb..bcaf66cc4d 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -280,6 +280,9 @@ struct kvm_xen_exit { /* Encounter unexpected vm-exit reason */ #define KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASON 4 +/* Flags that describe what fields in emulation_failure hold valid data. */ +#define KVM_INTERNAL_ERROR_EMULATION_FLAG_INSTRUCTION_BYTES (1ULL << 0) + /* for KVM_RUN, returned by mmap(vcpu_fd, offset=0) */ struct kvm_run { /* in */ @@ -383,6 +386,25 @@ struct kvm_run { __u32 ndata; __u64 data[16]; } internal; + /* + * KVM_INTERNAL_ERROR_EMULATION + * + * "struct emulation_failure" is an overlay of "struct internal" + * that is used for the KVM_INTERNAL_ERROR_EMULATION sub-type of + * KVM_EXIT_INTERNAL_ERROR. Note, unlike other internal error + * sub-types, this struct is ABI! It also needs to be backwards + * compatible with "struct internal". Take special care that + * "ndata" is correct, that new fields are enumerated in "flags", + * and that each flag enumerates fields that are 64-bit aligned + * and sized (so that ndata+internal.data[] is valid/accurate). + */ + struct { + __u32 suberror; + __u32 ndata; + __u64 flags; + __u8 insn_size; + __u8 insn_bytes[15]; + } emulation_failure; /* KVM_EXIT_OSI */ struct { __u64 gprs[32]; @@ -1083,6 +1105,13 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_SGX_ATTRIBUTE 196 #define KVM_CAP_VM_COPY_ENC_CONTEXT_FROM 197 #define KVM_CAP_PTP_KVM 198 +#define KVM_CAP_HYPERV_ENFORCE_CPUID 199 +#define KVM_CAP_SREGS2 200 +#define KVM_CAP_EXIT_HYPERCALL 201 +#define KVM_CAP_PPC_RPT_INVALIDATE 202 +#define KVM_CAP_BINARY_STATS_FD 203 +#define KVM_CAP_EXIT_ON_EMULATION_FAILURE 204 +#define KVM_CAP_ARM_MTE 205 #ifdef KVM_CAP_IRQ_ROUTING @@ -1428,6 +1457,7 @@ struct kvm_s390_ucas_mapping { /* Available with KVM_CAP_PMU_EVENT_FILTER */ #define KVM_SET_PMU_EVENT_FILTER _IOW(KVMIO, 0xb2, struct kvm_pmu_event_filter) #define KVM_PPC_SVM_OFF _IO(KVMIO, 0xb3) +#define KVM_ARM_MTE_COPY_TAGS _IOR(KVMIO, 0xb4, struct kvm_arm_copy_mte_tags) /* ioctl for vm fd */ #define KVM_CREATE_DEVICE _IOWR(KVMIO, 0xe0, struct kvm_create_device) @@ -1621,6 +1651,9 @@ struct kvm_xen_hvm_attr { #define KVM_XEN_VCPU_GET_ATTR _IOWR(KVMIO, 0xca, struct kvm_xen_vcpu_attr) #define KVM_XEN_VCPU_SET_ATTR _IOW(KVMIO, 0xcb, struct kvm_xen_vcpu_attr) +#define KVM_GET_SREGS2 _IOR(KVMIO, 0xcc, struct kvm_sregs2) +#define KVM_SET_SREGS2 _IOW(KVMIO, 0xcd, struct kvm_sregs2) + struct kvm_xen_vcpu_attr { __u16 type; __u16 pad[3]; @@ -1899,4 +1932,76 @@ struct kvm_dirty_gfn { #define KVM_BUS_LOCK_DETECTION_OFF (1 << 0) #define KVM_BUS_LOCK_DETECTION_EXIT (1 << 1) +/** + * struct kvm_stats_header - Header of per vm/vcpu binary statistics data. + * @flags: Some extra information for header, always 0 for now. + * @name_size: The size in bytes of the memory which contains statistics + * name string including trailing '\0'. The memory is allocated + * at the send of statistics descriptor. + * @num_desc: The number of statistics the vm or vcpu has. + * @id_offset: The offset of the vm/vcpu stats' id string in the file pointed + * by vm/vcpu stats fd. + * @desc_offset: The offset of the vm/vcpu stats' descriptor block in the file + * pointd by vm/vcpu stats fd. + * @data_offset: The offset of the vm/vcpu stats' data block in the file + * pointed by vm/vcpu stats fd. + * + * This is the header userspace needs to read from stats fd before any other + * readings. It is used by userspace to discover all the information about the + * vm/vcpu's binary statistics. + * Userspace reads this header from the start of the vm/vcpu's stats fd. + */ +struct kvm_stats_header { + __u32 flags; + __u32 name_size; + __u32 num_desc; + __u32 id_offset; + __u32 desc_offset; + __u32 data_offset; +}; + +#define KVM_STATS_TYPE_SHIFT 0 +#define KVM_STATS_TYPE_MASK (0xF << KVM_STATS_TYPE_SHIFT) +#define KVM_STATS_TYPE_CUMULATIVE (0x0 << KVM_STATS_TYPE_SHIFT) +#define KVM_STATS_TYPE_INSTANT (0x1 << KVM_STATS_TYPE_SHIFT) +#define KVM_STATS_TYPE_PEAK (0x2 << KVM_STATS_TYPE_SHIFT) +#define KVM_STATS_TYPE_MAX KVM_STATS_TYPE_PEAK + +#define KVM_STATS_UNIT_SHIFT 4 +#define KVM_STATS_UNIT_MASK (0xF << KVM_STATS_UNIT_SHIFT) +#define KVM_STATS_UNIT_NONE (0x0 << KVM_STATS_UNIT_SHIFT) +#define KVM_STATS_UNIT_BYTES (0x1 << KVM_STATS_UNIT_SHIFT) +#define KVM_STATS_UNIT_SECONDS (0x2 << KVM_STATS_UNIT_SHIFT) +#define KVM_STATS_UNIT_CYCLES (0x3 << KVM_STATS_UNIT_SHIFT) +#define KVM_STATS_UNIT_MAX KVM_STATS_UNIT_CYCLES + +#define KVM_STATS_BASE_SHIFT 8 +#define KVM_STATS_BASE_MASK (0xF << KVM_STATS_BASE_SHIFT) +#define KVM_STATS_BASE_POW10 (0x0 << KVM_STATS_BASE_SHIFT) +#define KVM_STATS_BASE_POW2 (0x1 << KVM_STATS_BASE_SHIFT) +#define KVM_STATS_BASE_MAX KVM_STATS_BASE_POW2 + +/** + * struct kvm_stats_desc - Descriptor of a KVM statistics. + * @flags: Annotations of the stats, like type, unit, etc. + * @exponent: Used together with @flags to determine the unit. + * @size: The number of data items for this stats. + * Every data item is of type __u64. + * @offset: The offset of the stats to the start of stat structure in + * struture kvm or kvm_vcpu. + * @unused: Unused field for future usage. Always 0 for now. + * @name: The name string for the stats. Its size is indicated by the + * &kvm_stats_header->name_size. + */ +struct kvm_stats_desc { + __u32 flags; + __s16 exponent; + __u16 size; + __u32 offset; + __u32 unused; + char name[]; +}; + +#define KVM_GET_STATS_FD _IO(KVMIO, 0xce) + #endif /* __LINUX_KVM_H */ diff --git a/linux-headers/linux/userfaultfd.h b/linux-headers/linux/userfaultfd.h index b9ac97b70f..8479af5f4c 100644 --- a/linux-headers/linux/userfaultfd.h +++ b/linux-headers/linux/userfaultfd.h @@ -31,7 +31,8 @@ UFFD_FEATURE_MISSING_SHMEM | \ UFFD_FEATURE_SIGBUS | \ UFFD_FEATURE_THREAD_ID | \ - UFFD_FEATURE_MINOR_HUGETLBFS) + UFFD_FEATURE_MINOR_HUGETLBFS | \ + UFFD_FEATURE_MINOR_SHMEM) #define UFFD_API_IOCTLS \ ((__u64)1 << _UFFDIO_REGISTER | \ (__u64)1 << _UFFDIO_UNREGISTER | \ @@ -80,8 +81,8 @@ struct uffdio_zeropage) #define UFFDIO_WRITEPROTECT _IOWR(UFFDIO, _UFFDIO_WRITEPROTECT, \ struct uffdio_writeprotect) -#define UFFDIO_CONTINUE _IOR(UFFDIO, _UFFDIO_CONTINUE, \ - struct uffdio_continue) +#define UFFDIO_CONTINUE _IOWR(UFFDIO, _UFFDIO_CONTINUE, \ + struct uffdio_continue) /* read() structure */ struct uffd_msg { @@ -185,6 +186,9 @@ struct uffdio_api { * UFFD_FEATURE_MINOR_HUGETLBFS indicates that minor faults * can be intercepted (via REGISTER_MODE_MINOR) for * hugetlbfs-backed pages. + * + * UFFD_FEATURE_MINOR_SHMEM indicates the same support as + * UFFD_FEATURE_MINOR_HUGETLBFS, but for shmem-backed pages instead. */ #define UFFD_FEATURE_PAGEFAULT_FLAG_WP (1<<0) #define UFFD_FEATURE_EVENT_FORK (1<<1) @@ -196,6 +200,7 @@ struct uffdio_api { #define UFFD_FEATURE_SIGBUS (1<<7) #define UFFD_FEATURE_THREAD_ID (1<<8) #define UFFD_FEATURE_MINOR_HUGETLBFS (1<<9) +#define UFFD_FEATURE_MINOR_SHMEM (1<<10) __u64 features; __u64 ioctls; From 82123b756a1a2f1965350e5794aaa7b5c6a15282 Mon Sep 17 00:00:00 2001 From: Bharata B Rao Date: Tue, 6 Jul 2021 16:54:40 +0530 Subject: [PATCH 107/272] target/ppc: Support for H_RPT_INVALIDATE hcall If KVM_CAP_RPT_INVALIDATE KVM capability is enabled, then - indicate the availability of H_RPT_INVALIDATE hcall to the guest via ibm,hypertas-functions property. - Enable the hcall Both the above are done only if the new sPAPR machine capability cap-rpt-invalidate is set. Signed-off-by: Bharata B Rao Message-Id: <20210706112440.1449562-3-bharata@linux.ibm.com> Signed-off-by: David Gibson --- hw/ppc/spapr.c | 6 ++++++ hw/ppc/spapr_caps.c | 41 +++++++++++++++++++++++++++++++++++++++++ include/hw/ppc/spapr.h | 8 ++++++-- target/ppc/kvm.c | 12 ++++++++++++ target/ppc/kvm_ppc.h | 12 ++++++++++++ 5 files changed, 77 insertions(+), 2 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 3808d47053..a007be471e 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -881,6 +881,10 @@ static void spapr_dt_rtas(SpaprMachineState *spapr, void *fdt) add_str(hypertas, "hcall-copy"); add_str(hypertas, "hcall-debug"); add_str(hypertas, "hcall-vphn"); + if (spapr_get_cap(spapr, SPAPR_CAP_RPT_INVALIDATE) == SPAPR_CAP_ON) { + add_str(hypertas, "hcall-rpt-invalidate"); + } + add_str(qemu_hypertas, "hcall-memop1"); if (!kvm_enabled() || kvmppc_spapr_use_multitce()) { @@ -2030,6 +2034,7 @@ static const VMStateDescription vmstate_spapr = { &vmstate_spapr_cap_ccf_assist, &vmstate_spapr_cap_fwnmi, &vmstate_spapr_fwnmi, + &vmstate_spapr_cap_rpt_invalidate, NULL } }; @@ -4618,6 +4623,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) smc->default_caps.caps[SPAPR_CAP_LARGE_DECREMENTER] = SPAPR_CAP_ON; smc->default_caps.caps[SPAPR_CAP_CCF_ASSIST] = SPAPR_CAP_ON; smc->default_caps.caps[SPAPR_CAP_FWNMI] = SPAPR_CAP_ON; + smc->default_caps.caps[SPAPR_CAP_RPT_INVALIDATE] = SPAPR_CAP_OFF; spapr_caps_add_properties(smc); smc->irq = &spapr_irq_dual; smc->dr_phb_enabled = true; diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index d0c419b392..ed7c077a0d 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -582,6 +582,37 @@ static void cap_fwnmi_apply(SpaprMachineState *spapr, uint8_t val, } } +static void cap_rpt_invalidate_apply(SpaprMachineState *spapr, + uint8_t val, Error **errp) +{ + ERRP_GUARD(); + + if (!val) { + /* capability disabled by default */ + return; + } + + if (tcg_enabled()) { + error_setg(errp, "No H_RPT_INVALIDATE support in TCG"); + error_append_hint(errp, + "Try appending -machine cap-rpt-invalidate=off\n"); + } else if (kvm_enabled()) { + if (!kvmppc_has_cap_mmu_radix()) { + error_setg(errp, "H_RPT_INVALIDATE only supported on Radix"); + return; + } + + if (!kvmppc_has_cap_rpt_invalidate()) { + error_setg(errp, + "KVM implementation does not support H_RPT_INVALIDATE"); + error_append_hint(errp, + "Try appending -machine cap-rpt-invalidate=off\n"); + } else { + kvmppc_enable_h_rpt_invalidate(); + } + } +} + SpaprCapabilityInfo capability_table[SPAPR_CAP_NUM] = { [SPAPR_CAP_HTM] = { .name = "htm", @@ -690,6 +721,15 @@ SpaprCapabilityInfo capability_table[SPAPR_CAP_NUM] = { .type = "bool", .apply = cap_fwnmi_apply, }, + [SPAPR_CAP_RPT_INVALIDATE] = { + .name = "rpt-invalidate", + .description = "Allow H_RPT_INVALIDATE", + .index = SPAPR_CAP_RPT_INVALIDATE, + .get = spapr_cap_get_bool, + .set = spapr_cap_set_bool, + .type = "bool", + .apply = cap_rpt_invalidate_apply, + }, }; static SpaprCapabilities default_caps_with_cpu(SpaprMachineState *spapr, @@ -830,6 +870,7 @@ SPAPR_CAP_MIG_STATE(nested_kvm_hv, SPAPR_CAP_NESTED_KVM_HV); SPAPR_CAP_MIG_STATE(large_decr, SPAPR_CAP_LARGE_DECREMENTER); SPAPR_CAP_MIG_STATE(ccf_assist, SPAPR_CAP_CCF_ASSIST); SPAPR_CAP_MIG_STATE(fwnmi, SPAPR_CAP_FWNMI); +SPAPR_CAP_MIG_STATE(rpt_invalidate, SPAPR_CAP_RPT_INVALIDATE); void spapr_caps_init(SpaprMachineState *spapr) { diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 779f707fb8..637652ad16 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -75,8 +75,10 @@ typedef enum { #define SPAPR_CAP_CCF_ASSIST 0x09 /* Implements PAPR FWNMI option */ #define SPAPR_CAP_FWNMI 0x0A +/* Support H_RPT_INVALIDATE */ +#define SPAPR_CAP_RPT_INVALIDATE 0x0B /* Num Caps */ -#define SPAPR_CAP_NUM (SPAPR_CAP_FWNMI + 1) +#define SPAPR_CAP_NUM (SPAPR_CAP_RPT_INVALIDATE + 1) /* * Capability Values @@ -547,8 +549,9 @@ struct SpaprMachineState { #define H_SCM_UNBIND_MEM 0x3F0 #define H_SCM_UNBIND_ALL 0x3FC #define H_SCM_HEALTH 0x400 +#define H_RPT_INVALIDATE 0x448 -#define MAX_HCALL_OPCODE H_SCM_HEALTH +#define MAX_HCALL_OPCODE H_RPT_INVALIDATE /* The hcalls above are standardized in PAPR and implemented by pHyp * as well. @@ -939,6 +942,7 @@ extern const VMStateDescription vmstate_spapr_cap_nested_kvm_hv; extern const VMStateDescription vmstate_spapr_cap_large_decr; extern const VMStateDescription vmstate_spapr_cap_ccf_assist; extern const VMStateDescription vmstate_spapr_cap_fwnmi; +extern const VMStateDescription vmstate_spapr_cap_rpt_invalidate; static inline uint8_t spapr_get_cap(SpaprMachineState *spapr, int cap) { diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 104a308abb..dc93b99189 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -89,6 +89,7 @@ static int cap_ppc_count_cache_flush_assist; static int cap_ppc_nested_kvm_hv; static int cap_large_decr; static int cap_fwnmi; +static int cap_rpt_invalidate; static uint32_t debug_inst_opcode; @@ -152,6 +153,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s) exit(1); } + cap_rpt_invalidate = kvm_vm_check_extension(s, KVM_CAP_PPC_RPT_INVALIDATE); kvm_ppc_register_host_cpu_type(); return 0; @@ -2040,6 +2042,11 @@ void kvmppc_enable_h_page_init(void) kvmppc_enable_hcall(kvm_state, H_PAGE_INIT); } +void kvmppc_enable_h_rpt_invalidate(void) +{ + kvmppc_enable_hcall(kvm_state, H_RPT_INVALIDATE); +} + void kvmppc_set_papr(PowerPCCPU *cpu) { CPUState *cs = CPU(cpu); @@ -2551,6 +2558,11 @@ int kvmppc_enable_cap_large_decr(PowerPCCPU *cpu, int enable) return 0; } +int kvmppc_has_cap_rpt_invalidate(void) +{ + return cap_rpt_invalidate; +} + PowerPCCPUClass *kvm_ppc_get_host_cpu_class(void) { uint32_t host_pvr = mfpvr(); diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h index 989f61ace0..ee9325bf9a 100644 --- a/target/ppc/kvm_ppc.h +++ b/target/ppc/kvm_ppc.h @@ -24,6 +24,7 @@ void kvmppc_enable_logical_ci_hcalls(void); void kvmppc_enable_set_mode_hcall(void); void kvmppc_enable_clear_ref_mod_hcalls(void); void kvmppc_enable_h_page_init(void); +void kvmppc_enable_h_rpt_invalidate(void); void kvmppc_set_papr(PowerPCCPU *cpu); int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr); void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy); @@ -71,6 +72,7 @@ bool kvmppc_has_cap_nested_kvm_hv(void); int kvmppc_set_cap_nested_kvm_hv(int enable); int kvmppc_get_cap_large_decr(void); int kvmppc_enable_cap_large_decr(PowerPCCPU *cpu, int enable); +int kvmppc_has_cap_rpt_invalidate(void); int kvmppc_enable_hwrng(void); int kvmppc_put_books_sregs(PowerPCCPU *cpu); PowerPCCPUClass *kvm_ppc_get_host_cpu_class(void); @@ -150,6 +152,11 @@ static inline void kvmppc_enable_h_page_init(void) { } +static inline void kvmppc_enable_h_rpt_invalidate(void) +{ + g_assert_not_reached(); +} + static inline void kvmppc_set_papr(PowerPCCPU *cpu) { } @@ -381,6 +388,11 @@ static inline int kvmppc_enable_cap_large_decr(PowerPCCPU *cpu, int enable) return -1; } +static inline int kvmppc_has_cap_rpt_invalidate(void) +{ + return false; +} + static inline int kvmppc_enable_hwrng(void) { return -1; From 0725570b2dcaeedff3031fc271b0d731a7382bfd Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Wed, 19 May 2021 13:25:13 +0200 Subject: [PATCH 108/272] MAINTAINERS: update block/rbd.c maintainer Jason has moved on from working on RBD and Ceph. I'm taking over his role upstream. Signed-off-by: Ilya Dryomov Message-Id: <20210519112513.19694-1-idryomov@gmail.com> Acked-by: Stefano Garzarella Signed-off-by: Kevin Wolf --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 684142e12e..86ec36a062 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3060,7 +3060,7 @@ S: Supported F: block/vmdk.c RBD -M: Jason Dillaman +M: Ilya Dryomov L: qemu-block@nongnu.org S: Supported F: block/rbd.c From 42e4ac9ef5a65f1714dd6f332de160eb63e0d6a4 Mon Sep 17 00:00:00 2001 From: Or Ozeri Date: Sun, 27 Jun 2021 14:46:35 +0300 Subject: [PATCH 109/272] block/rbd: Add support for rbd image encryption Starting from ceph Pacific, RBD has built-in support for image-level encryption. Currently supported formats are LUKS version 1 and 2. There are 2 new relevant librbd APIs for controlling encryption, both expect an open image context: rbd_encryption_format: formats an image (i.e. writes the LUKS header) rbd_encryption_load: loads encryptor/decryptor to the image IO stack This commit extends the qemu rbd driver API to support the above. Signed-off-by: Or Ozeri Message-Id: <20210627114635.39326-1-oro@il.ibm.com> Reviewed-by: Ilya Dryomov Signed-off-by: Kevin Wolf --- block/rbd.c | 361 ++++++++++++++++++++++++++++++++++++++++++- qapi/block-core.json | 110 ++++++++++++- 2 files changed, 465 insertions(+), 6 deletions(-) diff --git a/block/rbd.c b/block/rbd.c index 26f64cce7c..f51adb3646 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -73,6 +73,18 @@ #define LIBRBD_USE_IOVEC 0 #endif +#define RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN 8 + +static const char rbd_luks_header_verification[ + RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN] = { + 'L', 'U', 'K', 'S', 0xBA, 0xBE, 0, 1 +}; + +static const char rbd_luks2_header_verification[ + RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN] = { + 'L', 'U', 'K', 'S', 0xBA, 0xBE, 0, 2 +}; + typedef enum { RBD_AIO_READ, RBD_AIO_WRITE, @@ -351,6 +363,203 @@ static void qemu_rbd_memset(RADOSCB *rcb, int64_t offs) } } +#ifdef LIBRBD_SUPPORTS_ENCRYPTION +static int qemu_rbd_convert_luks_options( + RbdEncryptionOptionsLUKSBase *luks_opts, + char **passphrase, + size_t *passphrase_len, + Error **errp) +{ + return qcrypto_secret_lookup(luks_opts->key_secret, (uint8_t **)passphrase, + passphrase_len, errp); +} + +static int qemu_rbd_convert_luks_create_options( + RbdEncryptionCreateOptionsLUKSBase *luks_opts, + rbd_encryption_algorithm_t *alg, + char **passphrase, + size_t *passphrase_len, + Error **errp) +{ + int r = 0; + + r = qemu_rbd_convert_luks_options( + qapi_RbdEncryptionCreateOptionsLUKSBase_base(luks_opts), + passphrase, passphrase_len, errp); + if (r < 0) { + return r; + } + + if (luks_opts->has_cipher_alg) { + switch (luks_opts->cipher_alg) { + case QCRYPTO_CIPHER_ALG_AES_128: { + *alg = RBD_ENCRYPTION_ALGORITHM_AES128; + break; + } + case QCRYPTO_CIPHER_ALG_AES_256: { + *alg = RBD_ENCRYPTION_ALGORITHM_AES256; + break; + } + default: { + r = -ENOTSUP; + error_setg_errno(errp, -r, "unknown encryption algorithm: %u", + luks_opts->cipher_alg); + return r; + } + } + } else { + /* default alg */ + *alg = RBD_ENCRYPTION_ALGORITHM_AES256; + } + + return 0; +} + +static int qemu_rbd_encryption_format(rbd_image_t image, + RbdEncryptionCreateOptions *encrypt, + Error **errp) +{ + int r = 0; + g_autofree char *passphrase = NULL; + size_t passphrase_len; + rbd_encryption_format_t format; + rbd_encryption_options_t opts; + rbd_encryption_luks1_format_options_t luks_opts; + rbd_encryption_luks2_format_options_t luks2_opts; + size_t opts_size; + uint64_t raw_size, effective_size; + + r = rbd_get_size(image, &raw_size); + if (r < 0) { + error_setg_errno(errp, -r, "cannot get raw image size"); + return r; + } + + switch (encrypt->format) { + case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS: { + memset(&luks_opts, 0, sizeof(luks_opts)); + format = RBD_ENCRYPTION_FORMAT_LUKS1; + opts = &luks_opts; + opts_size = sizeof(luks_opts); + r = qemu_rbd_convert_luks_create_options( + qapi_RbdEncryptionCreateOptionsLUKS_base(&encrypt->u.luks), + &luks_opts.alg, &passphrase, &passphrase_len, errp); + if (r < 0) { + return r; + } + luks_opts.passphrase = passphrase; + luks_opts.passphrase_size = passphrase_len; + break; + } + case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2: { + memset(&luks2_opts, 0, sizeof(luks2_opts)); + format = RBD_ENCRYPTION_FORMAT_LUKS2; + opts = &luks2_opts; + opts_size = sizeof(luks2_opts); + r = qemu_rbd_convert_luks_create_options( + qapi_RbdEncryptionCreateOptionsLUKS2_base( + &encrypt->u.luks2), + &luks2_opts.alg, &passphrase, &passphrase_len, errp); + if (r < 0) { + return r; + } + luks2_opts.passphrase = passphrase; + luks2_opts.passphrase_size = passphrase_len; + break; + } + default: { + r = -ENOTSUP; + error_setg_errno( + errp, -r, "unknown image encryption format: %u", + encrypt->format); + return r; + } + } + + r = rbd_encryption_format(image, format, opts, opts_size); + if (r < 0) { + error_setg_errno(errp, -r, "encryption format fail"); + return r; + } + + r = rbd_get_size(image, &effective_size); + if (r < 0) { + error_setg_errno(errp, -r, "cannot get effective image size"); + return r; + } + + r = rbd_resize(image, raw_size + (raw_size - effective_size)); + if (r < 0) { + error_setg_errno(errp, -r, "cannot resize image after format"); + return r; + } + + return 0; +} + +static int qemu_rbd_encryption_load(rbd_image_t image, + RbdEncryptionOptions *encrypt, + Error **errp) +{ + int r = 0; + g_autofree char *passphrase = NULL; + size_t passphrase_len; + rbd_encryption_luks1_format_options_t luks_opts; + rbd_encryption_luks2_format_options_t luks2_opts; + rbd_encryption_format_t format; + rbd_encryption_options_t opts; + size_t opts_size; + + switch (encrypt->format) { + case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS: { + memset(&luks_opts, 0, sizeof(luks_opts)); + format = RBD_ENCRYPTION_FORMAT_LUKS1; + opts = &luks_opts; + opts_size = sizeof(luks_opts); + r = qemu_rbd_convert_luks_options( + qapi_RbdEncryptionOptionsLUKS_base(&encrypt->u.luks), + &passphrase, &passphrase_len, errp); + if (r < 0) { + return r; + } + luks_opts.passphrase = passphrase; + luks_opts.passphrase_size = passphrase_len; + break; + } + case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2: { + memset(&luks2_opts, 0, sizeof(luks2_opts)); + format = RBD_ENCRYPTION_FORMAT_LUKS2; + opts = &luks2_opts; + opts_size = sizeof(luks2_opts); + r = qemu_rbd_convert_luks_options( + qapi_RbdEncryptionOptionsLUKS2_base(&encrypt->u.luks2), + &passphrase, &passphrase_len, errp); + if (r < 0) { + return r; + } + luks2_opts.passphrase = passphrase; + luks2_opts.passphrase_size = passphrase_len; + break; + } + default: { + r = -ENOTSUP; + error_setg_errno( + errp, -r, "unknown image encryption format: %u", + encrypt->format); + return r; + } + } + + r = rbd_encryption_load(image, format, opts, opts_size); + if (r < 0) { + error_setg_errno(errp, -r, "encryption load fail"); + return r; + } + + return 0; +} +#endif + /* 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, @@ -368,6 +577,13 @@ static int qemu_rbd_do_create(BlockdevCreateOptions *options, return -EINVAL; } +#ifndef LIBRBD_SUPPORTS_ENCRYPTION + if (opts->has_encrypt) { + error_setg(errp, "RBD library does not support image encryption"); + return -ENOTSUP; + } +#endif + if (opts->has_cluster_size) { int64_t objsize = opts->cluster_size; if ((objsize - 1) & objsize) { /* not a power of 2? */ @@ -393,6 +609,28 @@ static int qemu_rbd_do_create(BlockdevCreateOptions *options, goto out; } +#ifdef LIBRBD_SUPPORTS_ENCRYPTION + if (opts->has_encrypt) { + rbd_image_t image; + + ret = rbd_open(io_ctx, opts->location->image, &image, NULL); + if (ret < 0) { + error_setg_errno(errp, -ret, + "error opening image '%s' for encryption format", + opts->location->image); + goto out; + } + + ret = qemu_rbd_encryption_format(image, opts->encrypt, errp); + rbd_close(image); + if (ret < 0) { + /* encryption format fail, try removing the image */ + rbd_remove(io_ctx, opts->location->image); + goto out; + } + } +#endif + ret = 0; out: rados_ioctx_destroy(io_ctx); @@ -405,6 +643,43 @@ static int qemu_rbd_co_create(BlockdevCreateOptions *options, Error **errp) return qemu_rbd_do_create(options, NULL, NULL, errp); } +static int qemu_rbd_extract_encryption_create_options( + QemuOpts *opts, + RbdEncryptionCreateOptions **spec, + Error **errp) +{ + QDict *opts_qdict; + QDict *encrypt_qdict; + Visitor *v; + int ret = 0; + + opts_qdict = qemu_opts_to_qdict(opts, NULL); + qdict_extract_subqdict(opts_qdict, &encrypt_qdict, "encrypt."); + qobject_unref(opts_qdict); + if (!qdict_size(encrypt_qdict)) { + *spec = NULL; + goto exit; + } + + /* Convert options into a QAPI object */ + v = qobject_input_visitor_new_flat_confused(encrypt_qdict, errp); + if (!v) { + ret = -EINVAL; + goto exit; + } + + visit_type_RbdEncryptionCreateOptions(v, NULL, spec, errp); + visit_free(v); + if (!*spec) { + ret = -EINVAL; + goto exit; + } + +exit: + qobject_unref(encrypt_qdict); + return ret; +} + static int coroutine_fn qemu_rbd_co_create_opts(BlockDriver *drv, const char *filename, QemuOpts *opts, @@ -413,6 +688,7 @@ static int coroutine_fn qemu_rbd_co_create_opts(BlockDriver *drv, BlockdevCreateOptions *create_options; BlockdevCreateOptionsRbd *rbd_opts; BlockdevOptionsRbd *loc; + RbdEncryptionCreateOptions *encrypt = NULL; Error *local_err = NULL; const char *keypairs, *password_secret; QDict *options = NULL; @@ -441,6 +717,13 @@ static int coroutine_fn qemu_rbd_co_create_opts(BlockDriver *drv, goto exit; } + ret = qemu_rbd_extract_encryption_create_options(opts, &encrypt, errp); + if (ret < 0) { + goto exit; + } + rbd_opts->encrypt = encrypt; + rbd_opts->has_encrypt = !!encrypt; + /* * Caution: while qdict_get_try_str() is fine, getting non-string * types would require more care. When @options come from -blockdev @@ -766,12 +1049,24 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, goto failed_open; } + if (opts->has_encrypt) { +#ifdef LIBRBD_SUPPORTS_ENCRYPTION + r = qemu_rbd_encryption_load(s->image, opts->encrypt, errp); + if (r < 0) { + goto failed_post_open; + } +#else + r = -ENOTSUP; + error_setg(errp, "RBD library does not support image encryption"); + goto failed_post_open; +#endif + } + r = rbd_get_size(s->image, &s->image_size); if (r < 0) { error_setg_errno(errp, -r, "error getting image size from %s", s->image_name); - rbd_close(s->image); - goto failed_open; + goto failed_post_open; } /* If we are using an rbd snapshot, we must be r/o, otherwise @@ -779,8 +1074,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, if (s->snap != NULL) { r = bdrv_apply_auto_read_only(bs, "rbd snapshots are read-only", errp); if (r < 0) { - rbd_close(s->image); - goto failed_open; + goto failed_post_open; } } @@ -790,6 +1084,8 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, r = 0; goto out; +failed_post_open: + rbd_close(s->image); failed_open: rados_ioctx_destroy(s->io_ctx); g_free(s->snap); @@ -1060,6 +1356,46 @@ static int qemu_rbd_getinfo(BlockDriverState *bs, BlockDriverInfo *bdi) return 0; } +static ImageInfoSpecific *qemu_rbd_get_specific_info(BlockDriverState *bs, + Error **errp) +{ + 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){ + .type = IMAGE_INFO_SPECIFIC_KIND_RBD, + .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 { + spec_info->u.rbd.data->has_encryption_format = false; + } + + return spec_info; +} + static int64_t qemu_rbd_getlength(BlockDriverState *bs) { BDRVRBDState *s = bs->opaque; @@ -1253,6 +1589,22 @@ static QemuOptsList qemu_rbd_create_opts = { .type = QEMU_OPT_STRING, .help = "ID of secret providing the password", }, + { + .name = "encrypt.format", + .type = QEMU_OPT_STRING, + .help = "Encrypt the image, format choices: 'luks', 'luks2'", + }, + { + .name = "encrypt.cipher-alg", + .type = QEMU_OPT_STRING, + .help = "Name of encryption cipher algorithm" + " (allowed values: aes-128, aes-256)", + }, + { + .name = "encrypt.key-secret", + .type = QEMU_OPT_STRING, + .help = "ID of secret providing LUKS passphrase", + }, { /* end of list */ } } }; @@ -1282,6 +1634,7 @@ static BlockDriver bdrv_rbd = { .bdrv_co_create_opts = qemu_rbd_co_create_opts, .bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_get_info = qemu_rbd_getinfo, + .bdrv_get_specific_info = qemu_rbd_get_specific_info, .create_opts = &qemu_rbd_create_opts, .bdrv_getlength = qemu_rbd_getlength, .bdrv_co_truncate = qemu_rbd_co_truncate, diff --git a/qapi/block-core.json b/qapi/block-core.json index 3114ba69bb..4a46552816 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -127,6 +127,18 @@ 'extents': ['ImageInfo'] } } +## +# @ImageInfoSpecificRbd: +# +# @encryption-format: Image encryption format +# +# Since: 6.1 +## +{ 'struct': 'ImageInfoSpecificRbd', + 'data': { + '*encryption-format': 'RbdImageEncryptionFormat' + } } + ## # @ImageInfoSpecific: # @@ -141,7 +153,8 @@ # If we need to add block driver specific parameters for # LUKS in future, then we'll subclass QCryptoBlockInfoLUKS # to define a ImageInfoSpecificLUKS - 'luks': 'QCryptoBlockInfoLUKS' + 'luks': 'QCryptoBlockInfoLUKS', + 'rbd': 'ImageInfoSpecificRbd' } } ## @@ -3613,6 +3626,94 @@ { 'enum': 'RbdAuthMode', 'data': [ 'cephx', 'none' ] } +## +# @RbdImageEncryptionFormat: +# +# Since: 6.1 +## +{ 'enum': 'RbdImageEncryptionFormat', + 'data': [ 'luks', 'luks2' ] } + +## +# @RbdEncryptionOptionsLUKSBase: +# +# @key-secret: ID of a QCryptoSecret object providing a passphrase +# for unlocking the encryption +# +# Since: 6.1 +## +{ 'struct': 'RbdEncryptionOptionsLUKSBase', + 'data': { 'key-secret': 'str' } } + +## +# @RbdEncryptionCreateOptionsLUKSBase: +# +# @cipher-alg: The encryption algorithm +# +# Since: 6.1 +## +{ 'struct': 'RbdEncryptionCreateOptionsLUKSBase', + 'base': 'RbdEncryptionOptionsLUKSBase', + 'data': { '*cipher-alg': 'QCryptoCipherAlgorithm' } } + +## +# @RbdEncryptionOptionsLUKS: +# +# Since: 6.1 +## +{ 'struct': 'RbdEncryptionOptionsLUKS', + 'base': 'RbdEncryptionOptionsLUKSBase', + 'data': { } } + +## +# @RbdEncryptionOptionsLUKS2: +# +# Since: 6.1 +## +{ 'struct': 'RbdEncryptionOptionsLUKS2', + 'base': 'RbdEncryptionOptionsLUKSBase', + 'data': { } } + +## +# @RbdEncryptionCreateOptionsLUKS: +# +# Since: 6.1 +## +{ 'struct': 'RbdEncryptionCreateOptionsLUKS', + 'base': 'RbdEncryptionCreateOptionsLUKSBase', + 'data': { } } + +## +# @RbdEncryptionCreateOptionsLUKS2: +# +# Since: 6.1 +## +{ 'struct': 'RbdEncryptionCreateOptionsLUKS2', + 'base': 'RbdEncryptionCreateOptionsLUKSBase', + 'data': { } } + +## +# @RbdEncryptionOptions: +# +# Since: 6.1 +## +{ 'union': 'RbdEncryptionOptions', + 'base': { 'format': 'RbdImageEncryptionFormat' }, + 'discriminator': 'format', + 'data': { 'luks': 'RbdEncryptionOptionsLUKS', + 'luks2': 'RbdEncryptionOptionsLUKS2' } } + +## +# @RbdEncryptionCreateOptions: +# +# Since: 6.1 +## +{ 'union': 'RbdEncryptionCreateOptions', + 'base': { 'format': 'RbdImageEncryptionFormat' }, + 'discriminator': 'format', + 'data': { 'luks': 'RbdEncryptionCreateOptionsLUKS', + 'luks2': 'RbdEncryptionCreateOptionsLUKS2' } } + ## # @BlockdevOptionsRbd: # @@ -3628,6 +3729,8 @@ # # @snapshot: Ceph snapshot name. # +# @encrypt: Image encryption options. (Since 6.1) +# # @user: Ceph id name. # # @auth-client-required: Acceptable authentication modes. @@ -3650,6 +3753,7 @@ 'image': 'str', '*conf': 'str', '*snapshot': 'str', + '*encrypt': 'RbdEncryptionOptions', '*user': 'str', '*auth-client-required': ['RbdAuthMode'], '*key-secret': 'str', @@ -4403,13 +4507,15 @@ # point to a snapshot. # @size: Size of the virtual disk in bytes # @cluster-size: RBD object size +# @encrypt: Image encryption options. (Since 6.1) # # Since: 2.12 ## { 'struct': 'BlockdevCreateOptionsRbd', 'data': { 'location': 'BlockdevOptionsRbd', 'size': 'size', - '*cluster-size' : 'size' } } + '*cluster-size' : 'size', + '*encrypt' : 'RbdEncryptionCreateOptions' } } ## # @BlockdevVmdkSubformat: From 48672ac058419a2162ea4579d507278e091c1e3e Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Fri, 2 Jul 2021 19:23:51 +0200 Subject: [PATCH 110/272] block/rbd: bump librbd requirement to luminous release Ceph Luminous (version 12.2.z) is almost 4 years old at this point. Bump the requirement to get rid of the ifdef'ry in the code. Qemu 6.1 dropped the support for RHEL-7 which was the last supported OS that required an older librbd. Signed-off-by: Peter Lieven Reviewed-by: Ilya Dryomov Message-Id: <20210702172356.11574-2-idryomov@gmail.com> Signed-off-by: Kevin Wolf --- block/rbd.c | 120 ++++------------------------------------------------ meson.build | 7 ++- 2 files changed, 13 insertions(+), 114 deletions(-) diff --git a/block/rbd.c b/block/rbd.c index f51adb3646..b4b928bbb9 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -55,24 +55,10 @@ * leading "\". */ -/* rbd_aio_discard added in 0.1.2 */ -#if LIBRBD_VERSION_CODE >= LIBRBD_VERSION(0, 1, 2) -#define LIBRBD_SUPPORTS_DISCARD -#else -#undef LIBRBD_SUPPORTS_DISCARD -#endif - #define OBJ_MAX_SIZE (1UL << OBJ_DEFAULT_OBJ_ORDER) #define RBD_MAX_SNAPS 100 -/* The LIBRBD_SUPPORTS_IOVEC is defined in librbd.h */ -#ifdef LIBRBD_SUPPORTS_IOVEC -#define LIBRBD_USE_IOVEC 1 -#else -#define LIBRBD_USE_IOVEC 0 -#endif - #define RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN 8 static const char rbd_luks_header_verification[ @@ -96,7 +82,6 @@ typedef struct RBDAIOCB { BlockAIOCB common; int64_t ret; QEMUIOVector *qiov; - char *bounce; RBDAIOCmd cmd; int error; struct BDRVRBDState *s; @@ -106,7 +91,6 @@ typedef struct RADOSCB { RBDAIOCB *acb; struct BDRVRBDState *s; int64_t size; - char *buf; int64_t ret; } RADOSCB; @@ -354,13 +338,9 @@ static int qemu_rbd_set_keypairs(rados_t cluster, const char *keypairs_json, static void qemu_rbd_memset(RADOSCB *rcb, int64_t offs) { - if (LIBRBD_USE_IOVEC) { - RBDAIOCB *acb = rcb->acb; - iov_memset(acb->qiov->iov, acb->qiov->niov, offs, 0, - acb->qiov->size - offs); - } else { - memset(rcb->buf + offs, 0, rcb->size - offs); - } + RBDAIOCB *acb = rcb->acb; + iov_memset(acb->qiov->iov, acb->qiov->niov, offs, 0, + acb->qiov->size - offs); } #ifdef LIBRBD_SUPPORTS_ENCRYPTION @@ -787,13 +767,6 @@ static void qemu_rbd_complete_aio(RADOSCB *rcb) g_free(rcb); - if (!LIBRBD_USE_IOVEC) { - if (acb->cmd == RBD_AIO_READ) { - qemu_iovec_from_buf(acb->qiov, 0, acb->bounce, acb->qiov->size); - } - qemu_vfree(acb->bounce); - } - acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret)); qemu_aio_unref(acb); @@ -1174,28 +1147,6 @@ static void rbd_finish_aiocb(rbd_completion_t c, RADOSCB *rcb) rbd_finish_bh, rcb); } -static int rbd_aio_discard_wrapper(rbd_image_t image, - uint64_t off, - uint64_t len, - rbd_completion_t comp) -{ -#ifdef LIBRBD_SUPPORTS_DISCARD - return rbd_aio_discard(image, off, len, comp); -#else - return -ENOTSUP; -#endif -} - -static int rbd_aio_flush_wrapper(rbd_image_t image, - rbd_completion_t comp) -{ -#ifdef LIBRBD_SUPPORTS_AIO_FLUSH - return rbd_aio_flush(image, comp); -#else - return -ENOTSUP; -#endif -} - static BlockAIOCB *rbd_start_aio(BlockDriverState *bs, int64_t off, QEMUIOVector *qiov, @@ -1218,21 +1169,6 @@ static BlockAIOCB *rbd_start_aio(BlockDriverState *bs, rcb = g_new(RADOSCB, 1); - if (!LIBRBD_USE_IOVEC) { - if (cmd == RBD_AIO_DISCARD || cmd == RBD_AIO_FLUSH) { - acb->bounce = NULL; - } else { - acb->bounce = qemu_try_blockalign(bs, qiov->size); - if (acb->bounce == NULL) { - goto failed; - } - } - if (cmd == RBD_AIO_WRITE) { - qemu_iovec_to_buf(acb->qiov, 0, acb->bounce, qiov->size); - } - rcb->buf = acb->bounce; - } - acb->ret = 0; acb->error = 0; acb->s = s; @@ -1246,7 +1182,7 @@ static BlockAIOCB *rbd_start_aio(BlockDriverState *bs, } switch (cmd) { - case RBD_AIO_WRITE: { + case RBD_AIO_WRITE: /* * RBD APIs don't allow us to write more than actual size, so in order * to support growing images, we resize the image before write @@ -1258,25 +1194,16 @@ static BlockAIOCB *rbd_start_aio(BlockDriverState *bs, goto failed_completion; } } -#ifdef LIBRBD_SUPPORTS_IOVEC - r = rbd_aio_writev(s->image, qiov->iov, qiov->niov, off, c); -#else - r = rbd_aio_write(s->image, off, size, rcb->buf, c); -#endif + r = rbd_aio_writev(s->image, qiov->iov, qiov->niov, off, c); break; - } case RBD_AIO_READ: -#ifdef LIBRBD_SUPPORTS_IOVEC - r = rbd_aio_readv(s->image, qiov->iov, qiov->niov, off, c); -#else - r = rbd_aio_read(s->image, off, size, rcb->buf, c); -#endif + r = rbd_aio_readv(s->image, qiov->iov, qiov->niov, off, c); break; case RBD_AIO_DISCARD: - r = rbd_aio_discard_wrapper(s->image, off, size, c); + r = rbd_aio_discard(s->image, off, size, c); break; case RBD_AIO_FLUSH: - r = rbd_aio_flush_wrapper(s->image, c); + r = rbd_aio_flush(s->image, c); break; default: r = -EINVAL; @@ -1291,9 +1218,6 @@ failed_completion: rbd_aio_release(c); failed: g_free(rcb); - if (!LIBRBD_USE_IOVEC) { - qemu_vfree(acb->bounce); - } qemu_aio_unref(acb); return NULL; @@ -1319,7 +1243,6 @@ static BlockAIOCB *qemu_rbd_aio_pwritev(BlockDriverState *bs, RBD_AIO_WRITE); } -#ifdef LIBRBD_SUPPORTS_AIO_FLUSH static BlockAIOCB *qemu_rbd_aio_flush(BlockDriverState *bs, BlockCompletionFunc *cb, void *opaque) @@ -1327,20 +1250,6 @@ static BlockAIOCB *qemu_rbd_aio_flush(BlockDriverState *bs, return rbd_start_aio(bs, 0, NULL, 0, cb, opaque, RBD_AIO_FLUSH); } -#else - -static int qemu_rbd_co_flush(BlockDriverState *bs) -{ -#if LIBRBD_VERSION_CODE >= LIBRBD_VERSION(0, 1, 1) - /* rbd_flush added in 0.1.1 */ - BDRVRBDState *s = bs->opaque; - return rbd_flush(s->image); -#else - return 0; -#endif -} -#endif - static int qemu_rbd_getinfo(BlockDriverState *bs, BlockDriverInfo *bdi) { BDRVRBDState *s = bs->opaque; @@ -1546,7 +1455,6 @@ static int qemu_rbd_snap_list(BlockDriverState *bs, return snap_count; } -#ifdef LIBRBD_SUPPORTS_DISCARD static BlockAIOCB *qemu_rbd_aio_pdiscard(BlockDriverState *bs, int64_t offset, int bytes, @@ -1556,9 +1464,7 @@ static BlockAIOCB *qemu_rbd_aio_pdiscard(BlockDriverState *bs, return rbd_start_aio(bs, offset, NULL, bytes, cb, opaque, RBD_AIO_DISCARD); } -#endif -#ifdef LIBRBD_SUPPORTS_INVALIDATE static void coroutine_fn qemu_rbd_co_invalidate_cache(BlockDriverState *bs, Error **errp) { @@ -1568,7 +1474,6 @@ static void coroutine_fn qemu_rbd_co_invalidate_cache(BlockDriverState *bs, error_setg_errno(errp, -r, "Failed to invalidate the cache"); } } -#endif static QemuOptsList qemu_rbd_create_opts = { .name = "rbd-create-opts", @@ -1643,23 +1548,14 @@ static BlockDriver bdrv_rbd = { .bdrv_aio_preadv = qemu_rbd_aio_preadv, .bdrv_aio_pwritev = qemu_rbd_aio_pwritev, -#ifdef LIBRBD_SUPPORTS_AIO_FLUSH .bdrv_aio_flush = qemu_rbd_aio_flush, -#else - .bdrv_co_flush_to_disk = qemu_rbd_co_flush, -#endif - -#ifdef LIBRBD_SUPPORTS_DISCARD .bdrv_aio_pdiscard = qemu_rbd_aio_pdiscard, -#endif .bdrv_snapshot_create = qemu_rbd_snap_create, .bdrv_snapshot_delete = qemu_rbd_snap_remove, .bdrv_snapshot_list = qemu_rbd_snap_list, .bdrv_snapshot_goto = qemu_rbd_snap_rollback, -#ifdef LIBRBD_SUPPORTS_INVALIDATE .bdrv_co_invalidate_cache = qemu_rbd_co_invalidate_cache, -#endif .strong_runtime_opts = qemu_rbd_strong_runtime_opts, }; diff --git a/meson.build b/meson.build index 7e12de01be..eb362ee5eb 100644 --- a/meson.build +++ b/meson.build @@ -710,13 +710,16 @@ if not get_option('rbd').auto() or have_block int main(void) { rados_t cluster; rados_create(&cluster, NULL); + #if LIBRBD_VERSION_CODE < LIBRBD_VERSION(1, 12, 0) + #error + #endif return 0; }''', dependencies: [librbd, librados]) rbd = declare_dependency(dependencies: [librbd, librados]) elif get_option('rbd').enabled() - error('could not link librados') + error('librbd >= 1.12.0 required') else - warning('could not link librados, disabling') + warning('librbd >= 1.12.0 not found, disabling') endif endif endif From 832a93dcb8a870f9debcc2513a040bb3dea858f2 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Fri, 2 Jul 2021 19:23:52 +0200 Subject: [PATCH 111/272] block/rbd: store object_size in BDRVRBDState Signed-off-by: Peter Lieven Reviewed-by: Ilya Dryomov Message-Id: <20210702172356.11574-3-idryomov@gmail.com> Signed-off-by: Kevin Wolf --- block/rbd.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/block/rbd.c b/block/rbd.c index b4b928bbb9..1ebf8f7e48 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -102,6 +102,7 @@ typedef struct BDRVRBDState { char *snap; char *namespace; uint64_t image_size; + uint64_t object_size; } BDRVRBDState; static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, @@ -958,6 +959,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, const QDictEntry *e; Error *local_err = NULL; char *keypairs, *secretid; + rbd_image_info_t info; int r; keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs")); @@ -1035,12 +1037,14 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, #endif } - r = rbd_get_size(s->image, &s->image_size); + r = rbd_stat(s->image, &info, sizeof(info)); if (r < 0) { - error_setg_errno(errp, -r, "error getting image size from %s", + error_setg_errno(errp, -r, "error getting image info from %s", s->image_name); goto failed_post_open; } + s->image_size = info.size; + s->object_size = info.obj_size; /* If we are using an rbd snapshot, we must be r/o, otherwise * leave as-is */ @@ -1253,15 +1257,7 @@ static BlockAIOCB *qemu_rbd_aio_flush(BlockDriverState *bs, static int qemu_rbd_getinfo(BlockDriverState *bs, BlockDriverInfo *bdi) { BDRVRBDState *s = bs->opaque; - rbd_image_info_t info; - int r; - - r = rbd_stat(s->image, &info, sizeof(info)); - if (r < 0) { - return r; - } - - bdi->cluster_size = info.obj_size; + bdi->cluster_size = s->object_size; return 0; } From 6d9214189e22205d42c4ad1fae5af265f0a94dd3 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Fri, 2 Jul 2021 19:23:53 +0200 Subject: [PATCH 112/272] block/rbd: update s->image_size in qemu_rbd_getlength While at it just call rbd_get_size and avoid rbd_image_info_t. Signed-off-by: Peter Lieven Reviewed-by: Ilya Dryomov Message-Id: <20210702172356.11574-4-idryomov@gmail.com> Signed-off-by: Kevin Wolf --- block/rbd.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/block/rbd.c b/block/rbd.c index 1ebf8f7e48..e2028d3db5 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -1304,15 +1304,14 @@ static ImageInfoSpecific *qemu_rbd_get_specific_info(BlockDriverState *bs, static int64_t qemu_rbd_getlength(BlockDriverState *bs) { BDRVRBDState *s = bs->opaque; - rbd_image_info_t info; int r; - r = rbd_stat(s->image, &info, sizeof(info)); + r = rbd_get_size(s->image, &s->image_size); if (r < 0) { return r; } - return info.size; + return s->image_size; } static int coroutine_fn qemu_rbd_co_truncate(BlockDriverState *bs, From c3e5fac534c6effc329b962162f79c799398f013 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Fri, 2 Jul 2021 19:23:54 +0200 Subject: [PATCH 113/272] block/rbd: migrate from aio to coroutines Signed-off-by: Peter Lieven Reviewed-by: Ilya Dryomov Message-Id: <20210702172356.11574-5-idryomov@gmail.com> Signed-off-by: Kevin Wolf --- block/rbd.c | 252 +++++++++++++++++++--------------------------------- 1 file changed, 90 insertions(+), 162 deletions(-) diff --git a/block/rbd.c b/block/rbd.c index e2028d3db5..380ad28861 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -78,22 +78,6 @@ typedef enum { RBD_AIO_FLUSH } RBDAIOCmd; -typedef struct RBDAIOCB { - BlockAIOCB common; - int64_t ret; - QEMUIOVector *qiov; - RBDAIOCmd cmd; - int error; - struct BDRVRBDState *s; -} RBDAIOCB; - -typedef struct RADOSCB { - RBDAIOCB *acb; - struct BDRVRBDState *s; - int64_t size; - int64_t ret; -} RADOSCB; - typedef struct BDRVRBDState { rados_t cluster; rados_ioctx_t io_ctx; @@ -105,6 +89,13 @@ typedef struct BDRVRBDState { uint64_t object_size; } BDRVRBDState; +typedef struct RBDTask { + BlockDriverState *bs; + Coroutine *co; + bool complete; + int64_t ret; +} RBDTask; + static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, BlockdevOptionsRbd *opts, bool cache, const char *keypairs, const char *secretid, @@ -337,13 +328,6 @@ static int qemu_rbd_set_keypairs(rados_t cluster, const char *keypairs_json, return ret; } -static void qemu_rbd_memset(RADOSCB *rcb, int64_t offs) -{ - RBDAIOCB *acb = rcb->acb; - iov_memset(acb->qiov->iov, acb->qiov->niov, offs, 0, - acb->qiov->size - offs); -} - #ifdef LIBRBD_SUPPORTS_ENCRYPTION static int qemu_rbd_convert_luks_options( RbdEncryptionOptionsLUKSBase *luks_opts, @@ -733,46 +717,6 @@ exit: return ret; } -/* - * This aio completion is being called from rbd_finish_bh() and runs in qemu - * BH context. - */ -static void qemu_rbd_complete_aio(RADOSCB *rcb) -{ - RBDAIOCB *acb = rcb->acb; - int64_t r; - - r = rcb->ret; - - if (acb->cmd != RBD_AIO_READ) { - if (r < 0) { - acb->ret = r; - acb->error = 1; - } else if (!acb->error) { - acb->ret = rcb->size; - } - } else { - if (r < 0) { - qemu_rbd_memset(rcb, 0); - acb->ret = r; - acb->error = 1; - } else if (r < rcb->size) { - qemu_rbd_memset(rcb, r); - if (!acb->error) { - acb->ret = rcb->size; - } - } else if (!acb->error) { - acb->ret = r; - } - } - - g_free(rcb); - - acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret)); - - qemu_aio_unref(acb); -} - static char *qemu_rbd_mon_host(BlockdevOptionsRbd *opts, Error **errp) { const char **vals; @@ -1122,89 +1066,59 @@ static int qemu_rbd_resize(BlockDriverState *bs, uint64_t size) return 0; } -static const AIOCBInfo rbd_aiocb_info = { - .aiocb_size = sizeof(RBDAIOCB), -}; - -static void rbd_finish_bh(void *opaque) +static void qemu_rbd_finish_bh(void *opaque) { - RADOSCB *rcb = opaque; - qemu_rbd_complete_aio(rcb); + RBDTask *task = opaque; + task->complete = 1; + aio_co_wake(task->co); } /* - * This is the callback function for rbd_aio_read and _write + * This is the completion callback function for all rbd aio calls + * started from qemu_rbd_start_co(). * * Note: this function is being called from a non qemu thread so * we need to be careful about what we do here. Generally we only * schedule a BH, and do the rest of the io completion handling - * from rbd_finish_bh() which runs in a qemu context. + * from qemu_rbd_finish_bh() which runs in a qemu context. */ -static void rbd_finish_aiocb(rbd_completion_t c, RADOSCB *rcb) +static void qemu_rbd_completion_cb(rbd_completion_t c, RBDTask *task) { - RBDAIOCB *acb = rcb->acb; - - rcb->ret = rbd_aio_get_return_value(c); + task->ret = rbd_aio_get_return_value(c); rbd_aio_release(c); - - replay_bh_schedule_oneshot_event(bdrv_get_aio_context(acb->common.bs), - rbd_finish_bh, rcb); + aio_bh_schedule_oneshot(bdrv_get_aio_context(task->bs), + qemu_rbd_finish_bh, task); } -static BlockAIOCB *rbd_start_aio(BlockDriverState *bs, - int64_t off, - QEMUIOVector *qiov, - int64_t size, - BlockCompletionFunc *cb, - void *opaque, - RBDAIOCmd cmd) +static int coroutine_fn qemu_rbd_start_co(BlockDriverState *bs, + uint64_t offset, + uint64_t bytes, + QEMUIOVector *qiov, + int flags, + RBDAIOCmd cmd) { - RBDAIOCB *acb; - RADOSCB *rcb = NULL; + BDRVRBDState *s = bs->opaque; + RBDTask task = { .bs = bs, .co = qemu_coroutine_self() }; rbd_completion_t c; int r; - BDRVRBDState *s = bs->opaque; + assert(!qiov || qiov->size == bytes); - acb = qemu_aio_get(&rbd_aiocb_info, bs, cb, opaque); - acb->cmd = cmd; - acb->qiov = qiov; - assert(!qiov || qiov->size == size); - - rcb = g_new(RADOSCB, 1); - - acb->ret = 0; - acb->error = 0; - acb->s = s; - - rcb->acb = acb; - rcb->s = acb->s; - rcb->size = size; - r = rbd_aio_create_completion(rcb, (rbd_callback_t) rbd_finish_aiocb, &c); + r = rbd_aio_create_completion(&task, + (rbd_callback_t) qemu_rbd_completion_cb, &c); if (r < 0) { - goto failed; + return r; } switch (cmd) { - case RBD_AIO_WRITE: - /* - * RBD APIs don't allow us to write more than actual size, so in order - * to support growing images, we resize the image before write - * operations that exceed the current size. - */ - if (off + size > s->image_size) { - r = qemu_rbd_resize(bs, off + size); - if (r < 0) { - goto failed_completion; - } - } - r = rbd_aio_writev(s->image, qiov->iov, qiov->niov, off, c); - break; case RBD_AIO_READ: - r = rbd_aio_readv(s->image, qiov->iov, qiov->niov, off, c); + r = rbd_aio_readv(s->image, qiov->iov, qiov->niov, offset, c); + break; + case RBD_AIO_WRITE: + r = rbd_aio_writev(s->image, qiov->iov, qiov->niov, offset, c); break; case RBD_AIO_DISCARD: - r = rbd_aio_discard(s->image, off, size, c); + r = rbd_aio_discard(s->image, offset, bytes, c); break; case RBD_AIO_FLUSH: r = rbd_aio_flush(s->image, c); @@ -1214,44 +1128,69 @@ static BlockAIOCB *rbd_start_aio(BlockDriverState *bs, } if (r < 0) { - goto failed_completion; + error_report("rbd request failed early: cmd %d offset %" PRIu64 + " bytes %" PRIu64 " flags %d r %d (%s)", cmd, offset, + bytes, flags, r, strerror(-r)); + rbd_aio_release(c); + return r; } - return &acb->common; -failed_completion: - rbd_aio_release(c); -failed: - g_free(rcb); + while (!task.complete) { + qemu_coroutine_yield(); + } - qemu_aio_unref(acb); - return NULL; + if (task.ret < 0) { + error_report("rbd request failed: cmd %d offset %" PRIu64 " bytes %" + PRIu64 " flags %d task.ret %" PRIi64 " (%s)", cmd, offset, + bytes, flags, task.ret, strerror(-task.ret)); + return task.ret; + } + + /* zero pad short reads */ + if (cmd == RBD_AIO_READ && task.ret < qiov->size) { + qemu_iovec_memset(qiov, task.ret, 0, qiov->size - task.ret); + } + + return 0; } -static BlockAIOCB *qemu_rbd_aio_preadv(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags, - BlockCompletionFunc *cb, - void *opaque) +static int +coroutine_fn qemu_rbd_co_preadv(BlockDriverState *bs, uint64_t offset, + uint64_t bytes, QEMUIOVector *qiov, + int flags) { - return rbd_start_aio(bs, offset, qiov, bytes, cb, opaque, - RBD_AIO_READ); + return qemu_rbd_start_co(bs, offset, bytes, qiov, flags, RBD_AIO_READ); } -static BlockAIOCB *qemu_rbd_aio_pwritev(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags, - BlockCompletionFunc *cb, - void *opaque) +static int +coroutine_fn qemu_rbd_co_pwritev(BlockDriverState *bs, uint64_t offset, + uint64_t bytes, QEMUIOVector *qiov, + int flags) { - return rbd_start_aio(bs, offset, qiov, bytes, cb, opaque, - RBD_AIO_WRITE); + BDRVRBDState *s = bs->opaque; + /* + * RBD APIs don't allow us to write more than actual size, so in order + * to support growing images, we resize the image before write + * operations that exceed the current size. + */ + if (offset + bytes > s->image_size) { + int r = qemu_rbd_resize(bs, offset + bytes); + if (r < 0) { + return r; + } + } + return qemu_rbd_start_co(bs, offset, bytes, qiov, flags, RBD_AIO_WRITE); } -static BlockAIOCB *qemu_rbd_aio_flush(BlockDriverState *bs, - BlockCompletionFunc *cb, - void *opaque) +static int coroutine_fn qemu_rbd_co_flush(BlockDriverState *bs) { - return rbd_start_aio(bs, 0, NULL, 0, cb, opaque, RBD_AIO_FLUSH); + return qemu_rbd_start_co(bs, 0, 0, NULL, 0, RBD_AIO_FLUSH); +} + +static int coroutine_fn qemu_rbd_co_pdiscard(BlockDriverState *bs, + int64_t offset, int count) +{ + return qemu_rbd_start_co(bs, offset, count, NULL, 0, RBD_AIO_DISCARD); } static int qemu_rbd_getinfo(BlockDriverState *bs, BlockDriverInfo *bdi) @@ -1450,16 +1389,6 @@ static int qemu_rbd_snap_list(BlockDriverState *bs, return snap_count; } -static BlockAIOCB *qemu_rbd_aio_pdiscard(BlockDriverState *bs, - int64_t offset, - int bytes, - BlockCompletionFunc *cb, - void *opaque) -{ - return rbd_start_aio(bs, offset, NULL, bytes, cb, opaque, - RBD_AIO_DISCARD); -} - static void coroutine_fn qemu_rbd_co_invalidate_cache(BlockDriverState *bs, Error **errp) { @@ -1540,11 +1469,10 @@ static BlockDriver bdrv_rbd = { .bdrv_co_truncate = qemu_rbd_co_truncate, .protocol_name = "rbd", - .bdrv_aio_preadv = qemu_rbd_aio_preadv, - .bdrv_aio_pwritev = qemu_rbd_aio_pwritev, - - .bdrv_aio_flush = qemu_rbd_aio_flush, - .bdrv_aio_pdiscard = qemu_rbd_aio_pdiscard, + .bdrv_co_preadv = qemu_rbd_co_preadv, + .bdrv_co_pwritev = qemu_rbd_co_pwritev, + .bdrv_co_flush_to_disk = qemu_rbd_co_flush, + .bdrv_co_pdiscard = qemu_rbd_co_pdiscard, .bdrv_snapshot_create = qemu_rbd_snap_create, .bdrv_snapshot_delete = qemu_rbd_snap_remove, From c56ac27d2ad583aa2db5382bb510a33b638a4ab5 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Fri, 2 Jul 2021 19:23:55 +0200 Subject: [PATCH 114/272] block/rbd: add write zeroes support This patch wittingly sets BDRV_REQ_NO_FALLBACK and silently ignores BDRV_REQ_MAY_UNMAP for older librbd versions. The rationale for this is as follows (citing Ilya Dryomov current RBD maintainer): ---8<--- a) remove the BDRV_REQ_MAY_UNMAP check in qemu_rbd_co_pwrite_zeroes() and as a consequence always unmap if librbd is too old It's not clear what qemu's expectation is but in general Write Zeroes is allowed to unmap. The only guarantee is that subsequent reads return zeroes, everything else is a hint. This is how it is specified in the kernel and in the NVMe spec. In particular, block/nvme.c implements it as follows: if (flags & BDRV_REQ_MAY_UNMAP) { cdw12 |= (1 << 25); } This sets the Deallocate bit. But if it's not set, the device may still deallocate: """ If the Deallocate bit (CDW12.DEAC) is set to '1' in a Write Zeroes command, and the namespace supports clearing all bytes to 0h in the values read (e.g., bits 2:0 in the DLFEAT field are set to 001b) from a deallocated logical block and its metadata (excluding protection information), then for each specified logical block, the controller: - should deallocate that logical block; ... If the Deallocate bit is cleared to '0' in a Write Zeroes command, and the namespace supports clearing all bytes to 0h in the values read (e.g., bits 2:0 in the DLFEAT field are set to 001b) from a deallocated logical block and its metadata (excluding protection information), then, for each specified logical block, the controller: - may deallocate that logical block; """ https://nvmexpress.org/wp-content/uploads/NVM-Express-NVM-Command-Set-Specification-2021.06.02-Ratified-1.pdf b) set BDRV_REQ_NO_FALLBACK in supported_zero_flags Again, it's not clear what qemu expects here, but without it we end up in a ridiculous situation where specifying the "don't allow slow fallback" switch immediately fails all efficient zeroing requests on a device where Write Zeroes is always efficient: $ qemu-io -c 'help write' | grep -- '-[zun]' -n, -- with -z, don't allow slow fallback -u, -- with -z, allow unmapping -z, -- write zeroes using blk_co_pwrite_zeroes $ qemu-io -f rbd -c 'write -z -u -n 0 1M' rbd:foo/bar write failed: Operation not supported --->8--- Signed-off-by: Peter Lieven Reviewed-by: Ilya Dryomov Message-Id: <20210702172356.11574-6-idryomov@gmail.com> Signed-off-by: Kevin Wolf --- block/rbd.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/block/rbd.c b/block/rbd.c index 380ad28861..3152bc8ba0 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -75,7 +75,8 @@ typedef enum { RBD_AIO_READ, RBD_AIO_WRITE, RBD_AIO_DISCARD, - RBD_AIO_FLUSH + RBD_AIO_FLUSH, + RBD_AIO_WRITE_ZEROES } RBDAIOCmd; typedef struct BDRVRBDState { @@ -999,6 +1000,10 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, } } +#ifdef LIBRBD_SUPPORTS_WRITE_ZEROES + bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK; +#endif + /* When extending regular files, we get zeros from the OS */ bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE; @@ -1123,6 +1128,18 @@ static int coroutine_fn qemu_rbd_start_co(BlockDriverState *bs, case RBD_AIO_FLUSH: r = rbd_aio_flush(s->image, c); break; +#ifdef LIBRBD_SUPPORTS_WRITE_ZEROES + case RBD_AIO_WRITE_ZEROES: { + int zero_flags = 0; +#ifdef RBD_WRITE_ZEROES_FLAG_THICK_PROVISION + if (!(flags & BDRV_REQ_MAY_UNMAP)) { + zero_flags = RBD_WRITE_ZEROES_FLAG_THICK_PROVISION; + } +#endif + r = rbd_aio_write_zeroes(s->image, offset, bytes, c, zero_flags, 0); + break; + } +#endif default: r = -EINVAL; } @@ -1193,6 +1210,16 @@ static int coroutine_fn qemu_rbd_co_pdiscard(BlockDriverState *bs, return qemu_rbd_start_co(bs, offset, count, NULL, 0, RBD_AIO_DISCARD); } +#ifdef LIBRBD_SUPPORTS_WRITE_ZEROES +static int +coroutine_fn qemu_rbd_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, + int count, BdrvRequestFlags flags) +{ + return qemu_rbd_start_co(bs, offset, count, NULL, flags, + RBD_AIO_WRITE_ZEROES); +} +#endif + static int qemu_rbd_getinfo(BlockDriverState *bs, BlockDriverInfo *bdi) { BDRVRBDState *s = bs->opaque; @@ -1473,6 +1500,9 @@ static BlockDriver bdrv_rbd = { .bdrv_co_pwritev = qemu_rbd_co_pwritev, .bdrv_co_flush_to_disk = qemu_rbd_co_flush, .bdrv_co_pdiscard = qemu_rbd_co_pdiscard, +#ifdef LIBRBD_SUPPORTS_WRITE_ZEROES + .bdrv_co_pwrite_zeroes = qemu_rbd_co_pwrite_zeroes, +#endif .bdrv_snapshot_create = qemu_rbd_snap_create, .bdrv_snapshot_delete = qemu_rbd_snap_remove, From eb06cbab7e92caf15033c91dfcacd2ba5d7bc88a Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Fri, 2 Jul 2021 19:23:56 +0200 Subject: [PATCH 115/272] block/rbd: drop qemu_rbd_refresh_limits librbd supports 1 byte alignment for all aio operations. Currently, there is no API call to query limits from the Ceph ObjectStore backend. So drop the bdrv_refresh_limits completely until there is such an API call. Signed-off-by: Peter Lieven Reviewed-by: Ilya Dryomov Message-Id: <20210702172356.11574-7-idryomov@gmail.com> Signed-off-by: Kevin Wolf --- block/rbd.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/block/rbd.c b/block/rbd.c index 3152bc8ba0..01a7b94d62 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -240,14 +240,6 @@ done: return; } - -static void qemu_rbd_refresh_limits(BlockDriverState *bs, Error **errp) -{ - /* XXX Does RBD support AIO on less than 512-byte alignment? */ - bs->bl.request_alignment = 512; -} - - static int qemu_rbd_set_auth(rados_t cluster, BlockdevOptionsRbd *opts, Error **errp) { @@ -1482,7 +1474,6 @@ static BlockDriver bdrv_rbd = { .format_name = "rbd", .instance_size = sizeof(BDRVRBDState), .bdrv_parse_filename = qemu_rbd_parse_filename, - .bdrv_refresh_limits = qemu_rbd_refresh_limits, .bdrv_file_open = qemu_rbd_open, .bdrv_close = qemu_rbd_close, .bdrv_reopen_prepare = qemu_rbd_reopen_prepare, From c2615bdfbd6d5a1a48438102f8ab328fe30b8e27 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 29 Jun 2021 08:36:02 +0200 Subject: [PATCH 116/272] util/uri: do not check argument of uri_free() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit uri_free() checks if its argument is NULL in uri_clean() and g_free(). There is no need to check the argument before the call. Signed-off-by: Heinrich Schuchardt Message-Id: <20210629063602.4239-1-xypron.glpk@gmx.de> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard W.M. Jones Signed-off-by: Kevin Wolf --- block/nfs.c | 4 +--- block/ssh.c | 4 +--- util/uri.c | 22 ++++++---------------- 3 files changed, 8 insertions(+), 22 deletions(-) diff --git a/block/nfs.c b/block/nfs.c index 7dff64f489..9aeaefb364 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -147,9 +147,7 @@ out: if (qp) { query_params_free(qp); } - if (uri) { - uri_free(uri); - } + uri_free(uri); return ret; } diff --git a/block/ssh.c b/block/ssh.c index d008caf059..e0fbb4934b 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -237,9 +237,7 @@ static int parse_uri(const char *filename, QDict *options, Error **errp) return 0; err: - if (uri) { - uri_free(uri); - } + uri_free(uri); return -EINVAL; } diff --git a/util/uri.c b/util/uri.c index 8bdef84120..ff72c6005f 100644 --- a/util/uri.c +++ b/util/uri.c @@ -1340,7 +1340,7 @@ static void uri_clean(URI *uri) /** * uri_free: - * @uri: pointer to an URI + * @uri: pointer to an URI, NULL is ignored * * Free up the URI struct */ @@ -1939,15 +1939,9 @@ step_7: val = uri_to_string(res); done: - if (ref != NULL) { - uri_free(ref); - } - if (bas != NULL) { - uri_free(bas); - } - if (res != NULL) { - uri_free(res); - } + uri_free(ref); + uri_free(bas); + uri_free(res); return val; } @@ -2190,12 +2184,8 @@ done: if (remove_path != 0) { ref->path = NULL; } - if (ref != NULL) { - uri_free(ref); - } - if (bas != NULL) { - uri_free(bas); - } + uri_free(ref); + uri_free(bas); return val; } From 2c7dd057aa7bd7a875e9b1a53975c220d6380bc4 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Fri, 25 Jun 2021 16:23:12 +0200 Subject: [PATCH 117/272] export/fuse: Pass default_permissions for mount We do not do any permission checks in fuse_open(), so let the kernel do them. We already let fuse_getattr() report the proper UNIX permissions, so this should work the way we want. This causes a change in 308's reference output, because now opening a non-writable export with O_RDWR fails already, instead of only actually attempting to write to it. (That is an improvement.) Signed-off-by: Max Reitz Message-Id: <20210625142317.271673-2-mreitz@redhat.com> Signed-off-by: Kevin Wolf --- block/export/fuse.c | 8 ++++++-- tests/qemu-iotests/308 | 3 ++- tests/qemu-iotests/308.out | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/block/export/fuse.c b/block/export/fuse.c index 38f74c94da..d0b88e8f80 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -153,8 +153,12 @@ static int setup_fuse_export(FuseExport *exp, const char *mountpoint, struct fuse_args fuse_args; int ret; - /* Needs to match what fuse_init() sets. Only max_read must be supplied. */ - mount_opts = g_strdup_printf("max_read=%zu", FUSE_MAX_BOUNCE_BYTES); + /* + * max_read needs to match what fuse_init() sets. + * max_write need not be supplied. + */ + mount_opts = g_strdup_printf("max_read=%zu,default_permissions", + FUSE_MAX_BOUNCE_BYTES); fuse_argv[0] = ""; /* Dummy program name */ fuse_argv[1] = "-o"; diff --git a/tests/qemu-iotests/308 b/tests/qemu-iotests/308 index f122065d0f..11c28a75f2 100755 --- a/tests/qemu-iotests/308 +++ b/tests/qemu-iotests/308 @@ -215,7 +215,8 @@ echo '=== Writable export ===' fuse_export_add 'export-mp' "'mountpoint': '$EXT_MP', 'writable': true" # Check that writing to the read-only export fails -$QEMU_IO -f raw -c 'write -P 42 1M 64k' "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -f raw -c 'write -P 42 1M 64k' "$TEST_IMG" 2>&1 \ + | _filter_qemu_io | _filter_testdir | _filter_imgfmt # But here it should work $QEMU_IO -f raw -c 'write -P 42 1M 64k' "$EXT_MP" | _filter_qemu_io diff --git a/tests/qemu-iotests/308.out b/tests/qemu-iotests/308.out index 466e7e0267..0e9420645f 100644 --- a/tests/qemu-iotests/308.out +++ b/tests/qemu-iotests/308.out @@ -91,7 +91,7 @@ virtual size: 0 B (0 bytes) 'mountpoint': 'TEST_DIR/t.IMGFMT.fuse', 'writable': true } } {"return": {}} -write failed: Permission denied +qemu-io: can't open device TEST_DIR/t.IMGFMT: Could not open 'TEST_DIR/t.IMGFMT': Permission denied wrote 65536/65536 bytes at offset 1048576 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset 1048576 From 8fc54f9428b9763f8003bd5f5dd440946210fc80 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Fri, 25 Jun 2021 16:23:13 +0200 Subject: [PATCH 118/272] export/fuse: Add allow-other option Without the allow_other mount option, no user (not even root) but the one who started qemu/the storage daemon can access the export. Allow users to configure the export such that such accesses are possible. While allow_other is probably what users want, we cannot make it an unconditional default, because passing it is only possible (for non-root users) if the global fuse.conf configuration file allows it. Thus, the default is an 'auto' mode, in which we first try with allow_other, and then fall back to without. FuseExport.allow_other reports whether allow_other was actually used as a mount option or not. Currently, this information is not used, but a future patch will let this field decide whether e.g. an export's UID and GID can be changed through chmod. One notable thing about 'auto' mode is that libfuse may print error messages directly to stderr, and so may fusermount (which it executes). Our export code cannot really filter or hide them. Therefore, if 'auto' fails its first attempt and has to fall back, fusermount will print an error message that mounting with allow_other failed. This behavior necessitates a change to iotest 308, namely we need to filter out this error message (because if the first attempt at mounting with allow_other succeeds, there will be no such message). Furthermore, common.rc's _make_test_img should use allow-other=off for FUSE exports, because iotests generally do not need to access images from other users, so allow-other=on or allow-other=auto have no advantage. OTOH, allow-other=on will not work on systems where user_allow_other is disabled, and with allow-other=auto, we get said error message that we would need to filter out again. Just disabling allow-other is simplest. Signed-off-by: Max Reitz Message-Id: <20210625142317.271673-3-mreitz@redhat.com> Signed-off-by: Kevin Wolf --- block/export/fuse.c | 28 +++++++++++++++++++++++----- qapi/block-export.json | 33 ++++++++++++++++++++++++++++++++- tests/qemu-iotests/308 | 6 +++++- tests/qemu-iotests/common.rc | 6 +++++- 4 files changed, 65 insertions(+), 8 deletions(-) diff --git a/block/export/fuse.c b/block/export/fuse.c index d0b88e8f80..4068250241 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -46,6 +46,8 @@ typedef struct FuseExport { char *mountpoint; bool writable; bool growable; + /* Whether allow_other was used as a mount option or not */ + bool allow_other; } FuseExport; static GHashTable *exports; @@ -57,7 +59,7 @@ static void fuse_export_delete(BlockExport *exp); static void init_exports_table(void); static int setup_fuse_export(FuseExport *exp, const char *mountpoint, - Error **errp); + bool allow_other, Error **errp); static void read_from_fuse_export(void *opaque); static bool is_regular_file(const char *path, Error **errp); @@ -118,7 +120,22 @@ static int fuse_export_create(BlockExport *blk_exp, exp->writable = blk_exp_args->writable; exp->growable = args->growable; - ret = setup_fuse_export(exp, args->mountpoint, errp); + /* set default */ + if (!args->has_allow_other) { + args->allow_other = FUSE_EXPORT_ALLOW_OTHER_AUTO; + } + + if (args->allow_other == FUSE_EXPORT_ALLOW_OTHER_AUTO) { + /* Ignore errors on our first attempt */ + ret = setup_fuse_export(exp, args->mountpoint, true, NULL); + exp->allow_other = ret == 0; + if (ret < 0) { + ret = setup_fuse_export(exp, args->mountpoint, false, errp); + } + } else { + exp->allow_other = args->allow_other == FUSE_EXPORT_ALLOW_OTHER_ON; + ret = setup_fuse_export(exp, args->mountpoint, exp->allow_other, errp); + } if (ret < 0) { goto fail; } @@ -146,7 +163,7 @@ static void init_exports_table(void) * Create exp->fuse_session and mount it. */ static int setup_fuse_export(FuseExport *exp, const char *mountpoint, - Error **errp) + bool allow_other, Error **errp) { const char *fuse_argv[4]; char *mount_opts; @@ -157,8 +174,9 @@ static int setup_fuse_export(FuseExport *exp, const char *mountpoint, * max_read needs to match what fuse_init() sets. * max_write need not be supplied. */ - mount_opts = g_strdup_printf("max_read=%zu,default_permissions", - FUSE_MAX_BOUNCE_BYTES); + mount_opts = g_strdup_printf("max_read=%zu,default_permissions%s", + FUSE_MAX_BOUNCE_BYTES, + allow_other ? ",allow_other" : ""); fuse_argv[0] = ""; /* Dummy program name */ fuse_argv[1] = "-o"; diff --git a/qapi/block-export.json b/qapi/block-export.json index e819e70cac..0ed63442a8 100644 --- a/qapi/block-export.json +++ b/qapi/block-export.json @@ -120,6 +120,23 @@ '*logical-block-size': 'size', '*num-queues': 'uint16'} } +## +# @FuseExportAllowOther: +# +# Possible allow_other modes for FUSE exports. +# +# @off: Do not pass allow_other as a mount option. +# +# @on: Pass allow_other as a mount option. +# +# @auto: Try mounting with allow_other first, and if that fails, retry +# without allow_other. +# +# Since: 6.1 +## +{ 'enum': 'FuseExportAllowOther', + 'data': ['off', 'on', 'auto'] } + ## # @BlockExportOptionsFuse: # @@ -132,11 +149,25 @@ # @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 +# 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 allow_other as a non-root user requires +# user_allow_other to be enabled in the global fuse.conf +# configuration file. +# In auto mode (the default), the FUSE export driver will +# first attempt to mount the export with allow_other, and +# if that fails, try again without. +# (since 6.1; default: auto) +# # Since: 6.0 ## { 'struct': 'BlockExportOptionsFuse', 'data': { 'mountpoint': 'str', - '*growable': 'bool' }, + '*growable': 'bool', + '*allow-other': 'FuseExportAllowOther' }, 'if': 'defined(CONFIG_FUSE)' } ## diff --git a/tests/qemu-iotests/308 b/tests/qemu-iotests/308 index 11c28a75f2..d13a9a969c 100755 --- a/tests/qemu-iotests/308 +++ b/tests/qemu-iotests/308 @@ -58,6 +58,9 @@ _supported_os Linux # We need /dev/urandom # $4: Node to export (defaults to 'node-format') fuse_export_add() { + # The grep -v is a filter for errors when /etc/fuse.conf does not contain + # user_allow_other. (The error is benign, but it is printed by fusermount + # on the first mount attempt, so our export code cannot hide it.) _send_qemu_cmd $QEMU_HANDLE \ "{'execute': 'block-export-add', 'arguments': { @@ -67,7 +70,8 @@ fuse_export_add() $2 } }" \ "${3:-return}" \ - | _filter_imgfmt + | _filter_imgfmt \ + | grep -v 'option allow_other only allowed if' } # $1: Export ID diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index cbbf6d7c7f..609d82de89 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -512,9 +512,13 @@ _make_test_img() # Usually, users would export formatted nodes. But we present fuse as a # protocol-level driver here, so we have to leave the format to the # client. + # Switch off allow-other, because in general we do not need it for + # iotests. The default allow-other=auto has the downside of printing a + # fusermount error on its first attempt if allow_other is not + # permissible, which we would need to filter. QSD_NEED_PID=y $QSD \ --blockdev file,node-name=export-node,filename=$img_name,discard=unmap \ - --export fuse,id=fuse-export,node-name=export-node,mountpoint="$export_mp",writable=on,growable=on \ + --export fuse,id=fuse-export,node-name=export-node,mountpoint="$export_mp",writable=on,growable=on,allow-other=off \ & pidfile="$QEMU_TEST_DIR/qemu-storage-daemon.pid" From 9bad96a8cc669a3b399b9d739b505fdc592acaa4 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Fri, 25 Jun 2021 16:23:14 +0200 Subject: [PATCH 119/272] export/fuse: Give SET_ATTR_SIZE its own branch In order to support changing other attributes than the file size in fuse_setattr(), we have to give each its own independent branch. This also applies to the only attribute we do support right now. Signed-off-by: Max Reitz Reviewed-by: Kevin Wolf Message-Id: <20210625142317.271673-4-mreitz@redhat.com> Signed-off-by: Kevin Wolf --- block/export/fuse.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/block/export/fuse.c b/block/export/fuse.c index 4068250241..26ad644cd7 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -417,20 +417,22 @@ static void fuse_setattr(fuse_req_t req, fuse_ino_t inode, struct stat *statbuf, FuseExport *exp = fuse_req_userdata(req); int ret; - if (!exp->writable) { - fuse_reply_err(req, EACCES); - return; - } - if (to_set & ~FUSE_SET_ATTR_SIZE) { fuse_reply_err(req, ENOTSUP); return; } - ret = fuse_do_truncate(exp, statbuf->st_size, true, PREALLOC_MODE_OFF); - if (ret < 0) { - fuse_reply_err(req, -ret); - return; + if (to_set & FUSE_SET_ATTR_SIZE) { + if (!exp->writable) { + fuse_reply_err(req, EACCES); + return; + } + + ret = fuse_do_truncate(exp, statbuf->st_size, true, PREALLOC_MODE_OFF); + if (ret < 0) { + fuse_reply_err(req, -ret); + return; + } } fuse_getattr(req, inode, fi); From 6aeeaed29ced7ef03e4211193f7e3f537eace871 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Fri, 25 Jun 2021 16:23:15 +0200 Subject: [PATCH 120/272] export/fuse: Let permissions be adjustable Allow changing the file mode, UID, and GID through SETATTR. Without allow_other, UID and GID are not allowed to be changed, because it would not make sense. Also, changing group or others' permissions is not allowed either. For read-only exports, +w cannot be set. Signed-off-by: Max Reitz Message-Id: <20210625142317.271673-5-mreitz@redhat.com> Signed-off-by: Kevin Wolf --- block/export/fuse.c | 73 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 11 deletions(-) diff --git a/block/export/fuse.c b/block/export/fuse.c index 26ad644cd7..ada9e263eb 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -48,6 +48,10 @@ typedef struct FuseExport { bool growable; /* Whether allow_other was used as a mount option or not */ bool allow_other; + + mode_t st_mode; + uid_t st_uid; + gid_t st_gid; } FuseExport; static GHashTable *exports; @@ -125,6 +129,13 @@ static int fuse_export_create(BlockExport *blk_exp, args->allow_other = FUSE_EXPORT_ALLOW_OTHER_AUTO; } + exp->st_mode = S_IFREG | S_IRUSR; + if (exp->writable) { + exp->st_mode |= S_IWUSR; + } + exp->st_uid = getuid(); + exp->st_gid = getgid(); + if (args->allow_other == FUSE_EXPORT_ALLOW_OTHER_AUTO) { /* Ignore errors on our first attempt */ ret = setup_fuse_export(exp, args->mountpoint, true, NULL); @@ -338,7 +349,6 @@ static void fuse_getattr(fuse_req_t req, fuse_ino_t inode, int64_t length, allocated_blocks; time_t now = time(NULL); FuseExport *exp = fuse_req_userdata(req); - mode_t mode; length = blk_getlength(exp->common.blk); if (length < 0) { @@ -353,17 +363,12 @@ static void fuse_getattr(fuse_req_t req, fuse_ino_t inode, allocated_blocks = DIV_ROUND_UP(allocated_blocks, 512); } - mode = S_IFREG | S_IRUSR; - if (exp->writable) { - mode |= S_IWUSR; - } - statbuf = (struct stat) { .st_ino = inode, - .st_mode = mode, + .st_mode = exp->st_mode, .st_nlink = 1, - .st_uid = getuid(), - .st_gid = getgid(), + .st_uid = exp->st_uid, + .st_gid = exp->st_gid, .st_size = length, .st_blksize = blk_bs(exp->common.blk)->bl.request_alignment, .st_blocks = allocated_blocks, @@ -409,19 +414,52 @@ static int fuse_do_truncate(const FuseExport *exp, int64_t size, } /** - * Let clients set file attributes. Only resizing is supported. + * Let clients set file attributes. Only resizing and changing + * permissions (st_mode, st_uid, st_gid) is allowed. + * Changing permissions is only allowed as far as it will actually + * permit access: Read-only exports cannot be given +w, and exports + * without allow_other cannot be given a different UID or GID, and + * they cannot be given non-owner access. */ static void fuse_setattr(fuse_req_t req, fuse_ino_t inode, struct stat *statbuf, int to_set, struct fuse_file_info *fi) { FuseExport *exp = fuse_req_userdata(req); + int supported_attrs; int ret; - if (to_set & ~FUSE_SET_ATTR_SIZE) { + supported_attrs = FUSE_SET_ATTR_SIZE | FUSE_SET_ATTR_MODE; + if (exp->allow_other) { + supported_attrs |= FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID; + } + + if (to_set & ~supported_attrs) { fuse_reply_err(req, ENOTSUP); return; } + /* Do some argument checks first before committing to anything */ + if (to_set & FUSE_SET_ATTR_MODE) { + /* + * Without allow_other, non-owners can never access the export, so do + * not allow setting permissions for them + */ + if (!exp->allow_other && + (statbuf->st_mode & (S_IRWXG | S_IRWXO)) != 0) + { + fuse_reply_err(req, EPERM); + return; + } + + /* +w for read-only exports makes no sense, disallow it */ + if (!exp->writable && + (statbuf->st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) + { + fuse_reply_err(req, EROFS); + return; + } + } + if (to_set & FUSE_SET_ATTR_SIZE) { if (!exp->writable) { fuse_reply_err(req, EACCES); @@ -435,6 +473,19 @@ static void fuse_setattr(fuse_req_t req, fuse_ino_t inode, struct stat *statbuf, } } + if (to_set & FUSE_SET_ATTR_MODE) { + /* Ignore FUSE-supplied file type, only change the mode */ + exp->st_mode = (statbuf->st_mode & 07777) | S_IFREG; + } + + if (to_set & FUSE_SET_ATTR_UID) { + exp->st_uid = statbuf->st_uid; + } + + if (to_set & FUSE_SET_ATTR_GID) { + exp->st_gid = statbuf->st_gid; + } + fuse_getattr(req, inode, fi); } From f29add26d412311926e8095952316d360bd51cbf Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Fri, 25 Jun 2021 16:23:16 +0200 Subject: [PATCH 121/272] iotests/308: Test +w on read-only FUSE exports Test that +w on read-only FUSE exports returns an EROFS error. u+x on the other hand should work. (There is no special reason to choose u+x here, it simply is like +w another flag that is not set by default.) Signed-off-by: Max Reitz Message-Id: <20210625142317.271673-6-mreitz@redhat.com> Signed-off-by: Kevin Wolf --- tests/qemu-iotests/308 | 11 +++++++++++ tests/qemu-iotests/308.out | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/tests/qemu-iotests/308 b/tests/qemu-iotests/308 index d13a9a969c..6b386bd523 100755 --- a/tests/qemu-iotests/308 +++ b/tests/qemu-iotests/308 @@ -170,6 +170,17 @@ fuse_export_add 'export-mp' "'mountpoint': '$EXT_MP'" # Check that the export presents the same data as the original image $QEMU_IMG compare -f raw -F $IMGFMT -U "$EXT_MP" "$TEST_IMG" +# Some quick chmod tests +stat -c 'Permissions pre-chmod: %a' "$EXT_MP" + +# Verify that we cannot set +w +chmod u+w "$EXT_MP" 2>&1 | _filter_testdir | _filter_imgfmt +stat -c 'Permissions post-+w: %a' "$EXT_MP" + +# But that we can set, say, +x (if we are so inclined) +chmod u+x "$EXT_MP" 2>&1 | _filter_testdir | _filter_imgfmt +stat -c 'Permissions post-+x: %a' "$EXT_MP" + echo echo '=== Mount over existing file ===' diff --git a/tests/qemu-iotests/308.out b/tests/qemu-iotests/308.out index 0e9420645f..fc47bb11a2 100644 --- a/tests/qemu-iotests/308.out +++ b/tests/qemu-iotests/308.out @@ -50,6 +50,10 @@ wrote 67108864/67108864 bytes at offset 0 } } {"return": {}} Images are identical. +Permissions pre-chmod: 400 +chmod: changing permissions of 'TEST_DIR/t.IMGFMT.fuse': Read-only file system +Permissions post-+w: 400 +Permissions post-+x: 500 === Mount over existing file === {'execute': 'block-export-add', From d9f008e6235b50bc81d3c2c80eaec3065b7f04c6 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Fri, 25 Jun 2021 16:23:17 +0200 Subject: [PATCH 122/272] iotests/fuse-allow-other: Test allow-other Signed-off-by: Max Reitz Message-Id: <20210625142317.271673-7-mreitz@redhat.com> Signed-off-by: Kevin Wolf --- tests/qemu-iotests/tests/fuse-allow-other | 168 ++++++++++++++++++ tests/qemu-iotests/tests/fuse-allow-other.out | 88 +++++++++ 2 files changed, 256 insertions(+) create mode 100755 tests/qemu-iotests/tests/fuse-allow-other create mode 100644 tests/qemu-iotests/tests/fuse-allow-other.out diff --git a/tests/qemu-iotests/tests/fuse-allow-other b/tests/qemu-iotests/tests/fuse-allow-other new file mode 100755 index 0000000000..19f494aefb --- /dev/null +++ b/tests/qemu-iotests/tests/fuse-allow-other @@ -0,0 +1,168 @@ +#!/usr/bin/env bash +# group: rw +# +# Test FUSE exports' allow-other option +# +# Copyright (C) 2021 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_qemu + _cleanup_test_img + rm -f "$EXT_MP" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ../common.rc +. ../common.filter +. ../common.qemu + +_supported_fmt generic + +_supported_proto file # We create the FUSE export manually + +sudo -n -u nobody true || \ + _notrun 'Password-less sudo as nobody required to test allow_other' + +# $1: Export ID +# $2: Options (beyond the node-name and ID) +# $3: Expected return value (defaults to 'return') +# $4: Node to export (defaults to 'node-format') +fuse_export_add() +{ + allow_other_not_supported='option allow_other only allowed if' + + output=$( + success_or_failure=yes _send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'block-export-add', + 'arguments': { + 'type': 'fuse', + 'id': '$1', + 'node-name': '${4:-node-format}', + $2 + } }" \ + "${3:-return}" \ + "$allow_other_not_supported" \ + | _filter_imgfmt + ) + + if echo "$output" | grep -q "$allow_other_not_supported"; then + # Shut down qemu gracefully so it can unmount the export + _send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'quit'}" \ + 'return' + + wait=yes _cleanup_qemu + + _notrun "allow_other not supported" + fi + + echo "$output" +} + +EXT_MP="$TEST_DIR/fuse-export" + +_make_test_img 64k +touch "$EXT_MP" + +echo +echo '=== Test permissions ===' + +# $1: allow-other value ('on'/'off'/'auto') +run_permission_test() +{ + _launch_qemu \ + -blockdev \ + "$IMGFMT,node-name=node-format,file.driver=file,file.filename=$TEST_IMG" + + _send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'qmp_capabilities'}" \ + 'return' + + fuse_export_add 'export' \ + "'mountpoint': '$EXT_MP', + 'allow-other': '$1'" + + # Should always work + echo '(Removing all permissions)' + chmod 000 "$EXT_MP" 2>&1 | _filter_testdir | _filter_imgfmt + stat -c 'Permissions post-chmod: %a' "$EXT_MP" + + # Should always work + echo '(Granting u+r)' + chmod u+r "$EXT_MP" 2>&1 | _filter_testdir | _filter_imgfmt + stat -c 'Permissions post-chmod: %a' "$EXT_MP" + + # Should only work with allow-other: Otherwise, no permissions can be + # granted to the group or others + echo '(Granting read permissions for everyone)' + chmod 444 "$EXT_MP" 2>&1 | _filter_testdir | _filter_imgfmt + stat -c 'Permissions post-chmod: %a' "$EXT_MP" + + echo 'Doing operations as nobody:' + # Change to TEST_DIR, so nobody will not have to attempt a lookup + pushd "$TEST_DIR" >/dev/null + + # This is already prevented by the permissions (without allow-other, FUSE + # exports always have o-r), but test it anyway + sudo -n -u nobody cat fuse-export >/dev/null + + # If the only problem were the lack of permissions, we should still be able + # to stat the export as nobody; it should not work without allow-other, + # though + sudo -n -u nobody \ + stat -c 'Permissions seen by nobody: %a' fuse-export 2>&1 \ + | _filter_imgfmt + + # To prove the point, revoke read permissions for others and try again + chmod o-r fuse-export 2>&1 | _filter_testdir | _filter_imgfmt + + # Should fail + sudo -n -u nobody cat fuse-export >/dev/null + # Should work with allow_other + sudo -n -u nobody \ + stat -c 'Permissions seen by nobody: %a' fuse-export 2>&1 \ + | _filter_imgfmt + + popd >/dev/null + + _send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'quit'}" \ + 'return' + + wait=yes _cleanup_qemu +} + +# 'auto' should behave exactly like 'on', because 'on' tests that +# allow_other works (otherwise, this test is skipped) +for ao in off on auto; do + echo + echo "--- allow-other=$ao ---" + + run_permission_test "$ao" +done + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/tests/fuse-allow-other.out b/tests/qemu-iotests/tests/fuse-allow-other.out new file mode 100644 index 0000000000..543fa52a06 --- /dev/null +++ b/tests/qemu-iotests/tests/fuse-allow-other.out @@ -0,0 +1,88 @@ +QA output created by fuse-allow-other +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536 + +=== Test permissions === + +--- allow-other=off --- +{'execute': 'qmp_capabilities'} +{"return": {}} +{'execute': 'block-export-add', + 'arguments': { + 'type': 'fuse', + 'id': 'export', + 'node-name': 'node-format', + 'mountpoint': 'TEST_DIR/fuse-export', + 'allow-other': 'off' + } } +{"return": {}} +(Removing all permissions) +Permissions post-chmod: 0 +(Granting u+r) +Permissions post-chmod: 400 +(Granting read permissions for everyone) +chmod: changing permissions of 'TEST_DIR/fuse-export': Operation not permitted +Permissions post-chmod: 400 +Doing operations as nobody: +cat: fuse-export: Permission denied +stat: cannot statx 'fuse-export': Permission denied +cat: fuse-export: Permission denied +stat: cannot statx 'fuse-export': Permission denied +{'execute': 'quit'} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "export"}} + +--- allow-other=on --- +{'execute': 'qmp_capabilities'} +{"return": {}} +{'execute': 'block-export-add', + 'arguments': { + 'type': 'fuse', + 'id': 'export', + 'node-name': 'node-format', + 'mountpoint': 'TEST_DIR/fuse-export', + 'allow-other': 'on' + } } +{"return": {}} +(Removing all permissions) +Permissions post-chmod: 0 +(Granting u+r) +Permissions post-chmod: 400 +(Granting read permissions for everyone) +Permissions post-chmod: 444 +Doing operations as nobody: +Permissions seen by nobody: 444 +cat: fuse-export: Permission denied +Permissions seen by nobody: 440 +{'execute': 'quit'} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "export"}} + +--- allow-other=auto --- +{'execute': 'qmp_capabilities'} +{"return": {}} +{'execute': 'block-export-add', + 'arguments': { + 'type': 'fuse', + 'id': 'export', + 'node-name': 'node-format', + 'mountpoint': 'TEST_DIR/fuse-export', + 'allow-other': 'auto' + } } +{"return": {}} +(Removing all permissions) +Permissions post-chmod: 0 +(Granting u+r) +Permissions post-chmod: 400 +(Granting read permissions for everyone) +Permissions post-chmod: 444 +Doing operations as nobody: +Permissions seen by nobody: 444 +cat: fuse-export: Permission denied +Permissions seen by nobody: 440 +{'execute': 'quit'} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "export"}} +*** done From 64cc845bdb0908df247757ea864c1c72bf82b30c Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Wed, 7 Jul 2021 20:04:48 +0200 Subject: [PATCH 123/272] block/rbd: fix type of task->complete task->complete is a bool not an integer. Signed-off-by: Peter Lieven Message-Id: <20210707180449.32665-1-pl@kamp.de> Reviewed-by: Ilya Dryomov Signed-off-by: Kevin Wolf --- block/rbd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/rbd.c b/block/rbd.c index 01a7b94d62..dcf82b15b8 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -1066,7 +1066,7 @@ static int qemu_rbd_resize(BlockDriverState *bs, uint64_t size) static void qemu_rbd_finish_bh(void *opaque) { RBDTask *task = opaque; - task->complete = 1; + task->complete = true; aio_co_wake(task->co); } From 6d2f38eb0e2be86af6904f1b768575cc64be37c2 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Wed, 7 Jul 2021 20:04:49 +0200 Subject: [PATCH 124/272] MAINTAINERS: add block/rbd.c reviewer adding myself as a designated reviewer. Signed-off-by: Peter Lieven Message-Id: <20210707180449.32665-2-pl@kamp.de> Acked-by: Ilya Dryomov Signed-off-by: Kevin Wolf --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 86ec36a062..0e9f338e32 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3061,6 +3061,7 @@ F: block/vmdk.c RBD M: Ilya Dryomov +R: Peter Lieven L: qemu-block@nongnu.org S: Supported F: block/rbd.c From 84affad1fd4c5251d7cccf4df43b29e9157983a9 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 5 Jul 2021 19:14:29 +0200 Subject: [PATCH 125/272] vhost-user: Fix backends without multiqueue support dev->max_queues was never initialised for backends that don't support VHOST_USER_PROTOCOL_F_MQ, so it would use 0 as the maximum number of queues to check against and consequently fail for any such backend. Set it to 1 if the backend doesn't have multiqueue support. Fixes: c90bd505a3e8210c23d69fecab9ee6f56ec4a161 Signed-off-by: Kevin Wolf Message-Id: <20210705171429.29286-1-kwolf@redhat.com> Reviewed-by: Cornelia Huck Reviewed-by: Raphael Norwitz Signed-off-by: Kevin Wolf --- hw/virtio/vhost-user.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 1ac4a2ebec..29ea2b4fce 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -1913,7 +1913,10 @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque, if (err < 0) { return -EPROTO; } + } else { + dev->max_queues = 1; } + if (dev->num_queues && dev->max_queues < dev->num_queues) { error_setg(errp, "The maximum number of queues supported by the " "backend is %" PRIu64, dev->max_queues); From 2842ff2d81ddd86b1d0dd3b98c46b72bfa5cb4cb Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 8 Jun 2021 10:18:52 -0700 Subject: [PATCH 126/272] blockdev: fix drive-backup transaction endless drained section drive_backup_prepare() does bdrv_drained_begin() in hope that bdrv_drained_end() will be called in drive_backup_clean(). Still we need to set state->bs for this to work. That's done too late: a lot of failure paths in drive_backup_prepare() miss setting state->bs. Fix that. Fixes: 2288ccfac96281c316db942d10e3f921c1373064 Fixes: https://gitlab.com/qemu-project/qemu/-/issues/399 Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20210608171852.250775-1-vsementsov@virtuozzo.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- blockdev.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/blockdev.c b/blockdev.c index f08192deda..094c085962 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1714,6 +1714,7 @@ static void drive_backup_prepare(BlkActionState *common, Error **errp) aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); + state->bs = bs; /* Paired with .clean() */ bdrv_drained_begin(bs); @@ -1813,8 +1814,6 @@ static void drive_backup_prepare(BlkActionState *common, Error **errp) } } - state->bs = bs; - state->job = do_backup_common(qapi_DriveBackup_base(backup), bs, target_bs, aio_context, common->block_job_txn, errp); From 5a385bf5c5cb3069fab17c014cf4b4f629509f1e Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 3 May 2021 14:35:59 -0700 Subject: [PATCH 127/272] qcow2: Prohibit backing file changes in 'qemu-img amend' This was deprecated back in bc5ee6da7 (qcow2: Deprecate use of qemu-img amend to change backing file), and no one in the meantime has given any reasons why it should be supported. Time to make change attempts a hard error (but for convenience, specifying the _same_ backing chain is not forbidden). Update a couple of iotests to match. Signed-off-by: Eric Blake Message-Id: <20210503213600.569128-2-eblake@redhat.com> Reviewed-by: Connor Kuehl Signed-off-by: Kevin Wolf --- block/qcow2.c | 13 ++++--------- docs/system/deprecated.rst | 12 ------------ docs/system/removed-features.rst | 12 ++++++++++++ tests/qemu-iotests/061 | 3 +++ tests/qemu-iotests/061.out | 3 ++- tests/qemu-iotests/082.out | 6 ++++-- 6 files changed, 25 insertions(+), 24 deletions(-) diff --git a/block/qcow2.c b/block/qcow2.c index ee4530cdbd..0cac2eda36 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -5620,15 +5620,10 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, if (backing_file || backing_format) { if (g_strcmp0(backing_file, s->image_backing_file) || g_strcmp0(backing_format, s->image_backing_format)) { - warn_report("Deprecated use of amend to alter the backing file; " - "use qemu-img rebase instead"); - } - ret = qcow2_change_backing_file(bs, - backing_file ?: s->image_backing_file, - backing_format ?: s->image_backing_format); - if (ret < 0) { - error_setg_errno(errp, -ret, "Failed to change the backing file"); - return ret; + error_setg(errp, "Cannot amend the backing file"); + error_append_hint(errp, + "You can use 'qemu-img rebase' instead.\n"); + return -EINVAL; } } diff --git a/docs/system/deprecated.rst b/docs/system/deprecated.rst index 70e08baff6..9626a1fb57 100644 --- a/docs/system/deprecated.rst +++ b/docs/system/deprecated.rst @@ -282,18 +282,6 @@ this CPU is also deprecated. Related binaries ---------------- -qemu-img amend to adjust backing file (since 5.1) -''''''''''''''''''''''''''''''''''''''''''''''''' - -The use of ``qemu-img amend`` to modify the name or format of a qcow2 -backing image is deprecated; this functionality was never fully -documented or tested, and interferes with other amend operations that -need access to the original backing image (such as deciding whether a -v3 zero cluster may be left unallocated when converting to a v2 -image). Rather, any changes to the backing chain should be performed -with ``qemu-img rebase -u`` either before or after the remaining -changes being performed by amend, as appropriate. - qemu-img backing file without format (since 5.1) '''''''''''''''''''''''''''''''''''''''''''''''' diff --git a/docs/system/removed-features.rst b/docs/system/removed-features.rst index 2b21bd39ab..b64ea55194 100644 --- a/docs/system/removed-features.rst +++ b/docs/system/removed-features.rst @@ -491,6 +491,18 @@ topologies described with -smp include all possible cpus, i.e. The ``enforce-config-section`` property was replaced by the ``-global migration.send-configuration={on|off}`` option. +qemu-img amend to adjust backing file (removed in 6.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The use of ``qemu-img amend`` to modify the name or format of a qcow2 +backing image was never fully documented or tested, and interferes +with other amend operations that need access to the original backing +image (such as deciding whether a v3 zero cluster may be left +unallocated when converting to a v2 image). Any changes to the +backing chain should be performed with ``qemu-img rebase -u`` either +before or after the remaining changes being performed by amend, as +appropriate. + Block devices ------------- diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061 index e26d94a0df..9507c223bd 100755 --- a/tests/qemu-iotests/061 +++ b/tests/qemu-iotests/061 @@ -167,6 +167,9 @@ _make_test_img -o "compat=1.1" 64M TEST_IMG="$TEST_IMG.base" _make_test_img -o "compat=1.1" 64M $QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io $QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IMG amend -o "backing_file=$TEST_IMG.base,backing_fmt=qcow2" \ + "$TEST_IMG" && echo "unexpected pass" +$QEMU_IMG rebase -u -b "$TEST_IMG.base" -F qcow2 "$TEST_IMG" $QEMU_IMG amend -o "backing_file=$TEST_IMG.base,backing_fmt=qcow2" "$TEST_IMG" $QEMU_IO -c "read -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io _check_test_img diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out index ee30da2665..7ecbd4dea8 100644 --- a/tests/qemu-iotests/061.out +++ b/tests/qemu-iotests/061.out @@ -370,7 +370,8 @@ wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -qemu-img: warning: Deprecated use of amend to alter the backing file; use qemu-img rebase instead +qemu-img: Cannot amend the backing file +You can use 'qemu-img rebase' instead. read 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) No errors were found on the image. diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out index b70c12c139..077ed0f2c7 100644 --- a/tests/qemu-iotests/082.out +++ b/tests/qemu-iotests/082.out @@ -808,12 +808,14 @@ Amend options for 'qcow2': size= - Virtual disk size Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 -qemu-img: warning: Deprecated use of amend to alter the backing file; use qemu-img rebase instead +qemu-img: Cannot amend the backing file +You can use 'qemu-img rebase' instead. Testing: rebase -u -b -f qcow2 TEST_DIR/t.qcow2 Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,? TEST_DIR/t.qcow2 -qemu-img: warning: Deprecated use of amend to alter the backing file; use qemu-img rebase instead +qemu-img: Cannot amend the backing file +You can use 'qemu-img rebase' instead. Testing: rebase -u -b -f qcow2 TEST_DIR/t.qcow2 From 497a30dbb065937d67f6c43af6dd78492e1d6f6d Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 3 May 2021 14:36:00 -0700 Subject: [PATCH 128/272] qemu-img: Require -F with -b backing image Back in commit d9f059aa6c (qemu-img: Deprecate use of -b without -F), we deprecated the ability to create a file with a backing image that requires qemu to perform format probing. Qemu can still probe older files for backwards compatibility, but it is time to finish off the ability to create such images, due to the potential security risk they present. Update a couple of iotests affected by the change. Signed-off-by: Eric Blake Message-Id: <20210503213600.569128-3-eblake@redhat.com> Reviewed-by: Connor Kuehl Signed-off-by: Kevin Wolf --- block.c | 37 ++++++++++---------------------- docs/system/deprecated.rst | 20 ----------------- docs/system/removed-features.rst | 19 ++++++++++++++++ qemu-img.c | 6 ++++-- tests/qemu-iotests/040 | 4 ++-- tests/qemu-iotests/041 | 6 ++++-- tests/qemu-iotests/114 | 18 ++++++++-------- tests/qemu-iotests/114.out | 11 ++++------ tests/qemu-iotests/301 | 4 +--- tests/qemu-iotests/301.out | 16 ++------------ 10 files changed, 56 insertions(+), 85 deletions(-) diff --git a/block.c b/block.c index acd35cb0cb..ce96585575 100644 --- a/block.c +++ b/block.c @@ -5074,7 +5074,7 @@ int coroutine_fn bdrv_co_check(BlockDriverState *bs, * -ENOTSUP - format driver doesn't support changing the backing file */ int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, - const char *backing_fmt, bool warn) + const char *backing_fmt, bool require) { BlockDriver *drv = bs->drv; int ret; @@ -5088,10 +5088,8 @@ int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, return -EINVAL; } - if (warn && backing_file && !backing_fmt) { - warn_report("Deprecated use of backing file without explicit " - "backing format, use of this image requires " - "potentially unsafe format probing"); + if (require && backing_file && !backing_fmt) { + return -EINVAL; } if (drv->bdrv_change_backing_file != NULL) { @@ -6601,24 +6599,11 @@ void bdrv_img_create(const char *filename, const char *fmt, goto out; } else { if (!backing_fmt) { - warn_report("Deprecated use of backing file without explicit " - "backing format (detected format of %s)", - bs->drv->format_name); - if (bs->drv != &bdrv_raw) { - /* - * A probe of raw deserves the most attention: - * leaving the backing format out of the image - * will ensure bs->probed is set (ensuring we - * don't accidentally commit into the backing - * file), and allow more spots to warn the users - * to fix their toolchain when opening this image - * later. For other images, we can safely record - * the format that we probed. - */ - backing_fmt = bs->drv->format_name; - qemu_opt_set(opts, BLOCK_OPT_BACKING_FMT, backing_fmt, - NULL); - } + error_setg(&local_err, + "Backing file specified without backing format"); + error_append_hint(&local_err, "Detected format of %s.", + bs->drv->format_name); + goto out; } if (size == -1) { /* Opened BS, have no size */ @@ -6635,9 +6620,9 @@ void bdrv_img_create(const char *filename, const char *fmt, } /* (backing_file && !(flags & BDRV_O_NO_BACKING)) */ } else if (backing_file && !backing_fmt) { - warn_report("Deprecated use of unopened backing file without " - "explicit backing format, use of this image requires " - "potentially unsafe format probing"); + error_setg(&local_err, + "Backing file specified without backing format"); + goto out; } if (size == -1) { diff --git a/docs/system/deprecated.rst b/docs/system/deprecated.rst index 9626a1fb57..25d6c4c9a0 100644 --- a/docs/system/deprecated.rst +++ b/docs/system/deprecated.rst @@ -282,26 +282,6 @@ this CPU is also deprecated. Related binaries ---------------- -qemu-img backing file without format (since 5.1) -'''''''''''''''''''''''''''''''''''''''''''''''' - -The use of ``qemu-img create``, ``qemu-img rebase``, or ``qemu-img -convert`` to create or modify an image that depends on a backing file -now recommends that an explicit backing format be provided. This is -for safety: if QEMU probes a different format than what you thought, -the data presented to the guest will be corrupt; similarly, presenting -a raw image to a guest allows a potential security exploit if a future -probe sees a non-raw image based on guest writes. - -To avoid the warning message, or even future refusal to create an -unsafe image, you must pass ``-o backing_fmt=`` (or the shorthand -``-F`` during create) to specify the intended backing format. You may -use ``qemu-img rebase -u`` to retroactively add a backing format to an -existing image. However, be aware that there are already potential -security risks to blindly using ``qemu-img info`` to probe the format -of an untrusted backing image, when deciding what format to add into -an existing image. - Backwards compatibility ----------------------- diff --git a/docs/system/removed-features.rst b/docs/system/removed-features.rst index b64ea55194..28bb035043 100644 --- a/docs/system/removed-features.rst +++ b/docs/system/removed-features.rst @@ -503,6 +503,25 @@ backing chain should be performed with ``qemu-img rebase -u`` either before or after the remaining changes being performed by amend, as appropriate. +qemu-img backing file without format (removed in 6.1) +''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The use of ``qemu-img create``, ``qemu-img rebase``, or ``qemu-img +convert`` to create or modify an image that depends on a backing file +now requires that an explicit backing format be provided. This is +for safety: if QEMU probes a different format than what you thought, +the data presented to the guest will be corrupt; similarly, presenting +a raw image to a guest allows a potential security exploit if a future +probe sees a non-raw image based on guest writes. + +To avoid creating unsafe backing chains, you must pass ``-o +backing_fmt=`` (or the shorthand ``-F`` during create) to specify the +intended backing format. You may use ``qemu-img rebase -u`` to +retroactively add a backing format to an existing image. However, be +aware that there are already potential security risks to blindly using +``qemu-img info`` to probe the format of an untrusted backing image, +when deciding what format to add into an existing image. + Block devices ------------- diff --git a/qemu-img.c b/qemu-img.c index 7956a89965..ec0e2fabe5 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -2508,8 +2508,10 @@ static int img_convert(int argc, char **argv) if (out_baseimg_param) { if (!qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT)) { - warn_report("Deprecated use of backing file without explicit " - "backing format"); + error_report("Use of backing file requires explicit " + "backing format"); + ret = -1; + goto out; } } diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040 index ba7cb34ce8..f3677de9df 100755 --- a/tests/qemu-iotests/040 +++ b/tests/qemu-iotests/040 @@ -920,8 +920,8 @@ class TestCommitWithOverriddenBacking(iotests.QMPTestCase): def setUp(self): qemu_img('create', '-f', iotests.imgfmt, self.img_base_a, '1M') qemu_img('create', '-f', iotests.imgfmt, self.img_base_b, '1M') - qemu_img('create', '-f', iotests.imgfmt, '-b', self.img_base_a, \ - self.img_top) + qemu_img('create', '-f', iotests.imgfmt, '-b', self.img_base_a, + '-F', iotests.imgfmt, self.img_top) self.vm = iotests.VM() self.vm.launch() diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index 5cc02b24fc..db9f5dc540 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -1295,8 +1295,10 @@ class TestReplaces(iotests.QMPTestCase): class TestFilters(iotests.QMPTestCase): def setUp(self): qemu_img('create', '-f', iotests.imgfmt, backing_img, '1M') - qemu_img('create', '-f', iotests.imgfmt, '-b', backing_img, test_img) - qemu_img('create', '-f', iotests.imgfmt, '-b', backing_img, target_img) + qemu_img('create', '-f', iotests.imgfmt, '-b', backing_img, + '-F', iotests.imgfmt, test_img) + qemu_img('create', '-f', iotests.imgfmt, '-b', backing_img, + '-F', iotests.imgfmt, target_img) qemu_io('-c', 'write -P 1 0 512k', backing_img) qemu_io('-c', 'write -P 2 512k 512k', test_img) diff --git a/tests/qemu-iotests/114 b/tests/qemu-iotests/114 index 43cb0bc6c3..de6fd327ee 100755 --- a/tests/qemu-iotests/114 +++ b/tests/qemu-iotests/114 @@ -44,16 +44,16 @@ _supported_os Linux # qcow2.py does not work too well with external data files _unsupported_imgopts data_file -# Intentionally specify backing file without backing format; demonstrate -# the difference in warning messages when backing file could be probed. -# Note that only a non-raw probe result will affect the resulting image. +# Older qemu-img could set up backing file without backing format; modern +# qemu can't but we can use qcow2.py to simulate older files. truncate -s $((64 * 1024 * 1024)) "$TEST_IMG.orig" -_make_test_img -b "$TEST_IMG.orig" 64M +_make_test_img -b "$TEST_IMG.orig" -F raw 64M +$PYTHON qcow2.py "$TEST_IMG" del-header-ext 0xE2792ACA TEST_IMG="$TEST_IMG.base" _make_test_img 64M $QEMU_IMG convert -O qcow2 -B "$TEST_IMG.orig" "$TEST_IMG.orig" "$TEST_IMG" -_make_test_img -b "$TEST_IMG.base" 64M -_make_test_img -u -b "$TEST_IMG.base" 64M +_make_test_img -b "$TEST_IMG.base" -F $IMGFMT 64M +_make_test_img -u -b "$TEST_IMG.base" -F $IMGFMT 64M # Set an invalid backing file format $PYTHON qcow2.py "$TEST_IMG" add-header-ext 0xE2792ACA "foo" @@ -64,9 +64,9 @@ _img_info $QEMU_IO -c "open $TEST_IMG" -c "read 0 4k" 2>&1 | _filter_qemu_io | _filter_testdir $QEMU_IO -c "open -o backing.driver=$IMGFMT $TEST_IMG" -c "read 0 4k" | _filter_qemu_io -# Rebase the image, to show that omitting backing format triggers a warning, -# but probing now lets us use the backing file. -$QEMU_IMG rebase -u -b "$TEST_IMG.base" "$TEST_IMG" +# Rebase the image, to show that backing format is required. +($QEMU_IMG rebase -u -b "$TEST_IMG.base" "$TEST_IMG" 2>&1 && echo "unexpected pass") | _filter_testdir +$QEMU_IMG rebase -u -b "$TEST_IMG.base" -F $IMGFMT "$TEST_IMG" $QEMU_IO -c "open $TEST_IMG" -c "read 0 4k" 2>&1 | _filter_qemu_io | _filter_testdir # success, all done diff --git a/tests/qemu-iotests/114.out b/tests/qemu-iotests/114.out index 0a37d20c82..6d638da266 100644 --- a/tests/qemu-iotests/114.out +++ b/tests/qemu-iotests/114.out @@ -1,12 +1,9 @@ QA output created by 114 -qemu-img: warning: Deprecated use of backing file without explicit backing format (detected format of raw) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.orig +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.orig backing_fmt=raw Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 -qemu-img: warning: Deprecated use of backing file without explicit backing format -qemu-img: warning: Deprecated use of backing file without explicit backing format (detected format of IMGFMT) +qemu-img: Use of backing file requires explicit backing format +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT -qemu-img: warning: Deprecated use of unopened backing file without explicit backing format, use of this image requires potentially unsafe format probing -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 64 MiB (67108864 bytes) @@ -17,7 +14,7 @@ qemu-io: can't open device TEST_DIR/t.qcow2: Could not open backing file: Unknow no file open, try 'help open' read 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -qemu-img: warning: Deprecated use of backing file without explicit backing format, use of this image requires potentially unsafe format probing +qemu-img: Could not change the backing file to 'TEST_DIR/t.qcow2.base': Invalid argument read 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) *** done diff --git a/tests/qemu-iotests/301 b/tests/qemu-iotests/301 index 9f943cadbe..220de1043f 100755 --- a/tests/qemu-iotests/301 +++ b/tests/qemu-iotests/301 @@ -3,7 +3,7 @@ # # Test qcow backing file warnings # -# Copyright (C) 2020 Red Hat, Inc. +# Copyright (C) 2020-2021 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 @@ -46,7 +46,6 @@ echo "== qcow backed by qcow ==" TEST_IMG="$TEST_IMG.base" _make_test_img $size _make_test_img -b "$TEST_IMG.base" $size -_img_info _make_test_img -b "$TEST_IMG.base" -F $IMGFMT $size _img_info @@ -71,7 +70,6 @@ echo "== qcow backed by raw ==" rm "$TEST_IMG.base" truncate --size=$size "$TEST_IMG.base" _make_test_img -b "$TEST_IMG.base" $size -_img_info _make_test_img -b "$TEST_IMG.base" -F raw $size _img_info diff --git a/tests/qemu-iotests/301.out b/tests/qemu-iotests/301.out index 9004dad639..e280658191 100644 --- a/tests/qemu-iotests/301.out +++ b/tests/qemu-iotests/301.out @@ -2,13 +2,7 @@ QA output created by 301 == qcow backed by qcow == Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=33554432 -qemu-img: warning: Deprecated use of backing file without explicit backing format (detected format of IMGFMT) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT -image: TEST_DIR/t.IMGFMT -file format: IMGFMT -virtual size: 32 MiB (33554432 bytes) -cluster_size: 512 -backing file: TEST_DIR/t.IMGFMT.base +qemu-img: TEST_DIR/t.IMGFMT: Backing file specified without backing format Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT image: TEST_DIR/t.IMGFMT file format: IMGFMT @@ -36,13 +30,7 @@ cluster_size: 512 backing file: TEST_DIR/t.IMGFMT.base == qcow backed by raw == -qemu-img: warning: Deprecated use of backing file without explicit backing format (detected format of raw) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base -image: TEST_DIR/t.IMGFMT -file format: IMGFMT -virtual size: 32 MiB (33554432 bytes) -cluster_size: 512 -backing file: TEST_DIR/t.IMGFMT.base +qemu-img: TEST_DIR/t.IMGFMT: Backing file specified without backing format Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=raw image: TEST_DIR/t.IMGFMT file format: IMGFMT From a7cd44bef3d9380181734a93977c3d1df3eef2cf Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Thu, 8 Jul 2021 10:52:28 -0500 Subject: [PATCH 129/272] qemu-img: Improve error for rebase without backing format When removeing support for qemu-img being able to create backing chains without embedded backing formats, we caused a poor error message as caught by iotest 114. Improve the situation to inform the user what went wrong. Suggested-by: Kevin Wolf Signed-off-by: Eric Blake Message-Id: <20210708155228.2666172-1-eblake@redhat.com> Signed-off-by: Kevin Wolf --- qemu-img.c | 3 +++ tests/qemu-iotests/114.out | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/qemu-img.c b/qemu-img.c index ec0e2fabe5..7c4fc60312 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -3767,6 +3767,9 @@ static int img_rebase(int argc, char **argv) if (ret == -ENOSPC) { error_report("Could not change the backing file to '%s': No " "space left in the file header", out_baseimg); + } else if (ret == -EINVAL && out_baseimg && !out_basefmt) { + error_report("Could not change the backing file to '%s': backing " + "format must be specified", out_baseimg); } else if (ret < 0) { error_report("Could not change the backing file to '%s': %s", out_baseimg, strerror(-ret)); diff --git a/tests/qemu-iotests/114.out b/tests/qemu-iotests/114.out index 6d638da266..f51dd9d20a 100644 --- a/tests/qemu-iotests/114.out +++ b/tests/qemu-iotests/114.out @@ -14,7 +14,7 @@ qemu-io: can't open device TEST_DIR/t.qcow2: Could not open backing file: Unknow no file open, try 'help open' read 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -qemu-img: Could not change the backing file to 'TEST_DIR/t.qcow2.base': Invalid argument +qemu-img: Could not change the backing file to 'TEST_DIR/t.qcow2.base': backing format must be specified read 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) *** done From bcfd86d6a6432be75fd8700c7c1aabb243adf469 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 8 Jul 2021 13:47:04 +0200 Subject: [PATCH 130/272] qcow2: Fix dangling pointer after reopen for 'file' Without an external data file, s->data_file is a second pointer with the same value as bs->file. When changing bs->file to a different BdrvChild and freeing the old BdrvChild, s->data_file must also be updated, otherwise it points to freed memory and causes crashes. This problem was caught by iotests case 245. Fixes: df2b7086f169239ebad5d150efa29c9bb6d4f820 Signed-off-by: Kevin Wolf Message-Id: <20210708114709.206487-2-kwolf@redhat.com> Reviewed-by: Eric Blake Reviewed-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Kevin Wolf --- block/qcow2.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/block/qcow2.c b/block/qcow2.c index 0cac2eda36..9f1b6461c8 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1926,6 +1926,7 @@ static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp) static int qcow2_reopen_prepare(BDRVReopenState *state, BlockReopenQueue *queue, Error **errp) { + BDRVQcow2State *s = state->bs->opaque; Qcow2ReopenState *r; int ret; @@ -1956,6 +1957,16 @@ static int qcow2_reopen_prepare(BDRVReopenState *state, } } + /* + * Without an external data file, s->data_file points to the same BdrvChild + * as bs->file. It needs to be resynced after reopen because bs->file may + * be changed. We can't use it in the meantime. + */ + if (!has_data_file(state->bs)) { + assert(s->data_file == state->bs->file); + s->data_file = NULL; + } + return 0; fail: @@ -1966,7 +1977,16 @@ fail: static void qcow2_reopen_commit(BDRVReopenState *state) { + BDRVQcow2State *s = state->bs->opaque; + qcow2_update_options_commit(state->bs, state->opaque); + if (!s->data_file) { + /* + * If we don't have an external data file, s->data_file was cleared by + * qcow2_reopen_prepare() and needs to be updated. + */ + s->data_file = state->bs->file; + } g_free(state->opaque); } @@ -1990,6 +2010,15 @@ static void qcow2_reopen_commit_post(BDRVReopenState *state) static void qcow2_reopen_abort(BDRVReopenState *state) { + BDRVQcow2State *s = state->bs->opaque; + + if (!s->data_file) { + /* + * If we don't have an external data file, s->data_file was cleared by + * qcow2_reopen_prepare() and needs to be restored. + */ + s->data_file = state->bs->file; + } qcow2_update_options_abort(state->bs, state->opaque); g_free(state->opaque); } From ab5b522879e2a7880418cbd29340675e5427572f Mon Sep 17 00:00:00 2001 From: Alberto Garcia Date: Thu, 8 Jul 2021 13:47:05 +0200 Subject: [PATCH 131/272] block: Add bdrv_reopen_queue_free() Move the code to free a BlockReopenQueue to a separate function. It will be used in a subsequent patch. [ kwolf: Also free explicit_options and options, and explicitly qobject_ref() the value when it continues to be used. This makes future memory leaks less likely. ] Signed-off-by: Alberto Garcia Signed-off-by: Kevin Wolf Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20210708114709.206487-3-kwolf@redhat.com> Signed-off-by: Kevin Wolf --- block.c | 22 ++++++++++++++++------ include/block/block.h | 1 + 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/block.c b/block.c index ce96585575..a26465e3da 100644 --- a/block.c +++ b/block.c @@ -4095,6 +4095,19 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, NULL, 0, keep_old_opts); } +void bdrv_reopen_queue_free(BlockReopenQueue *bs_queue) +{ + if (bs_queue) { + BlockReopenQueueEntry *bs_entry, *next; + QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { + qobject_unref(bs_entry->state.explicit_options); + qobject_unref(bs_entry->state.options); + g_free(bs_entry); + } + g_free(bs_queue); + } +} + /* * Reopen multiple BlockDriverStates atomically & transactionally. * @@ -4197,15 +4210,10 @@ abort: if (bs_entry->prepared) { bdrv_reopen_abort(&bs_entry->state); } - qobject_unref(bs_entry->state.explicit_options); - qobject_unref(bs_entry->state.options); } cleanup: - QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { - g_free(bs_entry); - } - g_free(bs_queue); + bdrv_reopen_queue_free(bs_queue); return ret; } @@ -4573,6 +4581,8 @@ static void bdrv_reopen_commit(BDRVReopenState *reopen_state) /* set BDS specific flags now */ qobject_unref(bs->explicit_options); qobject_unref(bs->options); + qobject_ref(reopen_state->explicit_options); + qobject_ref(reopen_state->options); bs->explicit_options = reopen_state->explicit_options; bs->options = reopen_state->options; diff --git a/include/block/block.h b/include/block/block.h index 7ec77ecb1a..6d42992985 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -386,6 +386,7 @@ BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, BlockReopenQueue *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 bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only, Error **errp); From 6cf42ca2f9782f0335abf3e6b611fbced40cd099 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 8 Jul 2021 13:47:06 +0200 Subject: [PATCH 132/272] block: Acquire AioContexts during bdrv_reopen_multiple() As the BlockReopenQueue can contain nodes in multiple AioContexts, only one of which may be locked when AIO_WAIT_WHILE() can be called, we can't let the caller lock the right contexts. Instead, individually lock the AioContext of a single node when iterating the queue. Reintroduce bdrv_reopen() as a wrapper for reopening a single node that drains the node and temporarily drops the AioContext lock for bdrv_reopen_multiple(). Signed-off-by: Kevin Wolf Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20210708114709.206487-4-kwolf@redhat.com> Signed-off-by: Kevin Wolf --- block.c | 51 ++++++++++++++++++++++++++++++++++++------- block/replication.c | 7 ++++++ blockdev.c | 5 +++++ include/block/block.h | 2 ++ qemu-io-cmds.c | 7 +----- 5 files changed, 58 insertions(+), 14 deletions(-) diff --git a/block.c b/block.c index a26465e3da..be083f389e 100644 --- a/block.c +++ b/block.c @@ -4124,19 +4124,26 @@ void bdrv_reopen_queue_free(BlockReopenQueue *bs_queue) * * All affected nodes must be drained between bdrv_reopen_queue() and * bdrv_reopen_multiple(). + * + * To be called from the main thread, with all other AioContexts unlocked. */ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) { int ret = -1; BlockReopenQueueEntry *bs_entry, *next; + AioContext *ctx; Transaction *tran = tran_new(); g_autoptr(GHashTable) found = NULL; g_autoptr(GSList) refresh_list = NULL; + assert(qemu_get_current_aio_context() == qemu_get_aio_context()); assert(bs_queue != NULL); QTAILQ_FOREACH(bs_entry, bs_queue, entry) { + ctx = bdrv_get_aio_context(bs_entry->state.bs); + aio_context_acquire(ctx); ret = bdrv_flush(bs_entry->state.bs); + aio_context_release(ctx); if (ret < 0) { error_setg_errno(errp, -ret, "Error flushing drive"); goto abort; @@ -4145,7 +4152,10 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) QTAILQ_FOREACH(bs_entry, bs_queue, entry) { assert(bs_entry->state.bs->quiesce_counter > 0); + ctx = bdrv_get_aio_context(bs_entry->state.bs); + aio_context_acquire(ctx); ret = bdrv_reopen_prepare(&bs_entry->state, bs_queue, tran, errp); + aio_context_release(ctx); if (ret < 0) { goto abort; } @@ -4188,7 +4198,10 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) * to first element. */ QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) { + ctx = bdrv_get_aio_context(bs_entry->state.bs); + aio_context_acquire(ctx); bdrv_reopen_commit(&bs_entry->state); + aio_context_release(ctx); } tran_commit(tran); @@ -4197,7 +4210,10 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) BlockDriverState *bs = bs_entry->state.bs; if (bs->drv->bdrv_reopen_commit_post) { + ctx = bdrv_get_aio_context(bs); + aio_context_acquire(ctx); bs->drv->bdrv_reopen_commit_post(&bs_entry->state); + aio_context_release(ctx); } } @@ -4208,7 +4224,10 @@ abort: tran_abort(tran); QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { if (bs_entry->prepared) { + ctx = bdrv_get_aio_context(bs_entry->state.bs); + aio_context_acquire(ctx); bdrv_reopen_abort(&bs_entry->state); + aio_context_release(ctx); } } @@ -4218,21 +4237,37 @@ cleanup: return ret; } +int bdrv_reopen(BlockDriverState *bs, QDict *opts, bool keep_old_opts, + Error **errp) +{ + AioContext *ctx = bdrv_get_aio_context(bs); + BlockReopenQueue *queue; + int ret; + + bdrv_subtree_drained_begin(bs); + if (ctx != qemu_get_aio_context()) { + aio_context_release(ctx); + } + + queue = bdrv_reopen_queue(NULL, bs, opts, keep_old_opts); + ret = bdrv_reopen_multiple(queue, errp); + + if (ctx != qemu_get_aio_context()) { + aio_context_acquire(ctx); + } + bdrv_subtree_drained_end(bs); + + return ret; +} + int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only, Error **errp) { - int ret; - BlockReopenQueue *queue; QDict *opts = qdict_new(); qdict_put_bool(opts, BDRV_OPT_READ_ONLY, read_only); - bdrv_subtree_drained_begin(bs); - queue = bdrv_reopen_queue(NULL, bs, opts, true); - ret = bdrv_reopen_multiple(queue, errp); - bdrv_subtree_drained_end(bs); - - return ret; + return bdrv_reopen(bs, opts, true, errp); } /* diff --git a/block/replication.c b/block/replication.c index 52163f2d1f..774e15df16 100644 --- a/block/replication.c +++ b/block/replication.c @@ -390,7 +390,14 @@ static void reopen_backing_file(BlockDriverState *bs, bool writable, } if (reopen_queue) { + AioContext *ctx = bdrv_get_aio_context(bs); + if (ctx != qemu_get_aio_context()) { + aio_context_release(ctx); + } bdrv_reopen_multiple(reopen_queue, errp); + if (ctx != qemu_get_aio_context()) { + aio_context_acquire(ctx); + } } bdrv_subtree_drained_end(s->hidden_disk->bs); diff --git a/blockdev.c b/blockdev.c index 094c085962..0acbace8fd 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3592,8 +3592,13 @@ void qmp_x_blockdev_reopen(BlockdevOptions *options, Error **errp) ctx = bdrv_get_aio_context(bs); aio_context_acquire(ctx); bdrv_subtree_drained_begin(bs); + aio_context_release(ctx); + queue = bdrv_reopen_queue(NULL, bs, qdict, false); bdrv_reopen_multiple(queue, errp); + + ctx = bdrv_get_aio_context(bs); + aio_context_acquire(ctx); bdrv_subtree_drained_end(bs); aio_context_release(ctx); diff --git a/include/block/block.h b/include/block/block.h index 6d42992985..3477290f9a 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -388,6 +388,8 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, bool keep_old_opts); void bdrv_reopen_queue_free(BlockReopenQueue *bs_queue); int 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, Error **errp); int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index e8d862a426..46593d632d 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -2116,8 +2116,6 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv) bool writethrough = !blk_enable_write_cache(blk); bool has_rw_option = false; bool has_cache_option = false; - - BlockReopenQueue *brq; Error *local_err = NULL; while ((c = getopt(argc, argv, "c:o:rw")) != -1) { @@ -2210,10 +2208,7 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv) qdict_put_bool(opts, BDRV_OPT_CACHE_NO_FLUSH, flags & BDRV_O_NO_FLUSH); } - bdrv_subtree_drained_begin(bs); - brq = bdrv_reopen_queue(NULL, bs, opts, true); - bdrv_reopen_multiple(brq, &local_err); - bdrv_subtree_drained_end(bs); + bdrv_reopen(bs, opts, true, &local_err); if (local_err) { error_report_err(local_err); From 3908b7a8994fa5ef7a89aa58cd5a02fc58141592 Mon Sep 17 00:00:00 2001 From: Alberto Garcia Date: Thu, 8 Jul 2021 13:47:07 +0200 Subject: [PATCH 133/272] block: Support multiple reopening with x-blockdev-reopen [ kwolf: Fixed AioContext locking ] Signed-off-by: Alberto Garcia Signed-off-by: Kevin Wolf Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20210708114709.206487-5-kwolf@redhat.com> Signed-off-by: Kevin Wolf --- blockdev.c | 83 ++++++++++--------- qapi/block-core.json | 18 ++-- tests/qemu-iotests/155 | 9 +- tests/qemu-iotests/165 | 4 +- tests/qemu-iotests/245 | 27 +++--- tests/qemu-iotests/248 | 2 +- tests/qemu-iotests/248.out | 2 +- tests/qemu-iotests/296 | 9 +- tests/qemu-iotests/298 | 4 +- .../tests/remove-bitmap-from-backing | 18 ++-- 10 files changed, 100 insertions(+), 76 deletions(-) diff --git a/blockdev.c b/blockdev.c index 0acbace8fd..5ad0e9070e 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3559,51 +3559,60 @@ fail: visit_free(v); } -void qmp_x_blockdev_reopen(BlockdevOptions *options, Error **errp) +void qmp_x_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp) { - BlockDriverState *bs; - AioContext *ctx; - QObject *obj; - Visitor *v = qobject_output_visitor_new(&obj); - BlockReopenQueue *queue; - QDict *qdict; + BlockReopenQueue *queue = NULL; + GSList *drained = NULL; - /* Check for the selected node name */ - if (!options->has_node_name) { - error_setg(errp, "node-name not specified"); - goto fail; + /* Add each one of the BDS that we want to reopen to the queue */ + for (; reopen_list != NULL; reopen_list = reopen_list->next) { + BlockdevOptions *options = reopen_list->value; + BlockDriverState *bs; + AioContext *ctx; + QObject *obj; + Visitor *v; + QDict *qdict; + + /* Check for the selected node name */ + if (!options->has_node_name) { + error_setg(errp, "node-name not specified"); + goto fail; + } + + bs = bdrv_find_node(options->node_name); + if (!bs) { + error_setg(errp, "Failed to find node with node-name='%s'", + options->node_name); + goto fail; + } + + /* Put all options in a QDict and flatten it */ + v = qobject_output_visitor_new(&obj); + visit_type_BlockdevOptions(v, NULL, &options, &error_abort); + visit_complete(v, &obj); + visit_free(v); + + qdict = qobject_to(QDict, obj); + + qdict_flatten(qdict); + + ctx = bdrv_get_aio_context(bs); + aio_context_acquire(ctx); + + bdrv_subtree_drained_begin(bs); + queue = bdrv_reopen_queue(queue, bs, qdict, false); + drained = g_slist_prepend(drained, bs); + + aio_context_release(ctx); } - bs = bdrv_find_node(options->node_name); - if (!bs) { - error_setg(errp, "Failed to find node with node-name='%s'", - options->node_name); - goto fail; - } - - /* Put all options in a QDict and flatten it */ - visit_type_BlockdevOptions(v, NULL, &options, &error_abort); - visit_complete(v, &obj); - qdict = qobject_to(QDict, obj); - - qdict_flatten(qdict); - /* Perform the reopen operation */ - ctx = bdrv_get_aio_context(bs); - aio_context_acquire(ctx); - bdrv_subtree_drained_begin(bs); - aio_context_release(ctx); - - queue = bdrv_reopen_queue(NULL, bs, qdict, false); bdrv_reopen_multiple(queue, errp); - - ctx = bdrv_get_aio_context(bs); - aio_context_acquire(ctx); - bdrv_subtree_drained_end(bs); - aio_context_release(ctx); + queue = NULL; fail: - visit_free(v); + bdrv_reopen_queue_free(queue); + g_slist_free_full(drained, (GDestroyNotify) bdrv_subtree_drained_end); } void qmp_blockdev_del(const char *node_name, Error **errp) diff --git a/qapi/block-core.json b/qapi/block-core.json index 4a46552816..052520331e 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -4221,13 +4221,15 @@ ## # @x-blockdev-reopen: # -# Reopens a block device using the given set of options. Any option -# not specified will be reset to its default value regardless of its -# previous status. If an option cannot be changed or a particular +# Reopens one or more block devices using the given set of options. +# Any option not specified will be reset to its default value regardless +# of its previous status. If an option cannot be changed or a particular # driver does not support reopening then the command will return an -# error. +# error. All devices in the list are reopened in one transaction, so +# if one of them fails then the whole transaction is cancelled. # -# The top-level @node-name option (from BlockdevOptions) must be +# The command receives a list of block devices to reopen. For each 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 any @@ -4247,8 +4249,8 @@ # # 4) NULL: the current child (if any) is detached. # -# Options (1) and (2) are supported in all cases, but at the moment -# only @backing allows replacing or detaching an existing child. +# 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 # unless the node being reopened does not have a backing file and its @@ -4258,7 +4260,7 @@ # Since: 4.0 ## { 'command': 'x-blockdev-reopen', - 'data': 'BlockdevOptions', 'boxed': true } + 'data': { 'options': ['BlockdevOptions'] } } ## # @blockdev-del: diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155 index bafef9dd9a..2947bfb81a 100755 --- a/tests/qemu-iotests/155 +++ b/tests/qemu-iotests/155 @@ -261,9 +261,12 @@ class TestBlockdevMirrorReopen(MirrorBaseClass): result = self.vm.qmp('blockdev-add', node_name="backing", driver="null-co") self.assert_qmp(result, 'return', {}) - result = self.vm.qmp('x-blockdev-reopen', node_name="target", - driver=iotests.imgfmt, file="target-file", - backing="backing") + result = self.vm.qmp('x-blockdev-reopen', options=[{ + 'node-name': "target", + 'driver': iotests.imgfmt, + 'file': "target-file", + 'backing': "backing" + }]) self.assert_qmp(result, 'return', {}) class TestBlockdevMirrorReopenIothread(TestBlockdevMirrorReopen): diff --git a/tests/qemu-iotests/165 b/tests/qemu-iotests/165 index abc4ffadd5..ef4cf14516 100755 --- a/tests/qemu-iotests/165 +++ b/tests/qemu-iotests/165 @@ -137,7 +137,7 @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase): assert sha256_1 == self.getSha256() # Reopen to RW - result = self.vm.qmp('x-blockdev-reopen', **{ + result = self.vm.qmp('x-blockdev-reopen', options=[{ 'node-name': 'node0', 'driver': iotests.imgfmt, 'file': { @@ -145,7 +145,7 @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase): 'filename': disk }, 'read-only': False - }) + }]) self.assert_qmp(result, 'return', {}) # Check that bitmap is reopened to RW and we can write to it. diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245 index 0295129cbb..ca08955207 100755 --- a/tests/qemu-iotests/245 +++ b/tests/qemu-iotests/245 @@ -85,8 +85,18 @@ class TestBlockdevReopen(iotests.QMPTestCase): "Expected output of %d qemu-io commands, found %d" % (found, self.total_io_cmds)) - # Run x-blockdev-reopen with 'opts' but applying 'newopts' - # on top of it. The original 'opts' dict is unmodified + # Run x-blockdev-reopen on a list of block devices + def reopenMultiple(self, opts, errmsg = None): + result = self.vm.qmp('x-blockdev-reopen', conv_keys=False, options=opts) + if errmsg: + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', errmsg) + else: + self.assert_qmp(result, 'return', {}) + + # Run x-blockdev-reopen on a single block device (specified by + # 'opts') but applying 'newopts' on top of it. The original 'opts' + # dict is unmodified def reopen(self, opts, newopts = {}, errmsg = None): opts = copy.deepcopy(opts) @@ -101,12 +111,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): subdict = opts[prefix] subdict[key] = value - result = self.vm.qmp('x-blockdev-reopen', conv_keys = False, **opts) - if errmsg: - self.assert_qmp(result, 'error/class', 'GenericError') - self.assert_qmp(result, 'error/desc', errmsg) - else: - self.assert_qmp(result, 'return', {}) + self.reopenMultiple([ opts ], errmsg) # Run query-named-block-nodes and return the specified entry @@ -142,10 +147,10 @@ class TestBlockdevReopen(iotests.QMPTestCase): # We cannot change any of these self.reopen(opts, {'node-name': 'not-found'}, "Failed to find node with node-name='not-found'") self.reopen(opts, {'node-name': ''}, "Failed to find node with node-name=''") - self.reopen(opts, {'node-name': None}, "Invalid parameter type for 'node-name', expected: string") + self.reopen(opts, {'node-name': None}, "Invalid parameter type for 'options[0].node-name', expected: string") self.reopen(opts, {'driver': 'raw'}, "Cannot change the option 'driver'") self.reopen(opts, {'driver': ''}, "Invalid parameter ''") - self.reopen(opts, {'driver': None}, "Invalid parameter type for 'driver', expected: string") + self.reopen(opts, {'driver': None}, "Invalid parameter type for 'options[0].driver', expected: string") self.reopen(opts, {'file': 'not-found'}, "Cannot find device='' nor node-name='not-found'") self.reopen(opts, {'file': ''}, "Cannot find device='' nor node-name=''") self.reopen(opts, {'file': None}, "Invalid parameter type for 'file', expected: BlockdevRef") @@ -154,7 +159,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): self.reopen(opts, {'file.filename': hd_path[1]}, "Cannot change the option 'filename'") self.reopen(opts, {'file.aio': 'native'}, "Cannot change the option 'aio'") self.reopen(opts, {'file.locking': 'off'}, "Cannot change the option 'locking'") - self.reopen(opts, {'file.filename': None}, "Invalid parameter type for 'file.filename', expected: string") + self.reopen(opts, {'file.filename': None}, "Invalid parameter type for 'options[0].file.filename', expected: string") # node-name is optional in BlockdevOptions, but x-blockdev-reopen needs it del opts['node-name'] diff --git a/tests/qemu-iotests/248 b/tests/qemu-iotests/248 index 4daaed1530..03911333c4 100755 --- a/tests/qemu-iotests/248 +++ b/tests/qemu-iotests/248 @@ -63,7 +63,7 @@ vm.get_qmp_events() del blockdev_opts['file']['size'] vm.qmp_log('x-blockdev-reopen', filters=[filter_qmp_testfiles], - **blockdev_opts) + options = [ blockdev_opts ]) vm.qmp_log('block-job-resume', device='drive0') vm.event_wait('JOB_STATUS_CHANGE', timeout=1.0, diff --git a/tests/qemu-iotests/248.out b/tests/qemu-iotests/248.out index 369b25bf26..893f625347 100644 --- a/tests/qemu-iotests/248.out +++ b/tests/qemu-iotests/248.out @@ -2,7 +2,7 @@ {"return": {}} {"execute": "blockdev-mirror", "arguments": {"device": "drive0", "on-target-error": "enospc", "sync": "full", "target": "target"}} {"return": {}} -{"execute": "x-blockdev-reopen", "arguments": {"driver": "qcow2", "file": {"driver": "raw", "file": {"driver": "file", "filename": "TEST_DIR/PID-target"}}, "node-name": "target"}} +{"execute": "x-blockdev-reopen", "arguments": {"options": [{"driver": "qcow2", "file": {"driver": "raw", "file": {"driver": "file", "filename": "TEST_DIR/PID-target"}}, "node-name": "target"}]}} {"return": {}} {"execute": "block-job-resume", "arguments": {"device": "drive0"}} {"return": {}} diff --git a/tests/qemu-iotests/296 b/tests/qemu-iotests/296 index 7c65e987a1..fa07b98bb4 100755 --- a/tests/qemu-iotests/296 +++ b/tests/qemu-iotests/296 @@ -120,8 +120,7 @@ class EncryptionSetupTestCase(iotests.QMPTestCase): command = 'x-blockdev-reopen' if reOpen else 'blockdev-add' - result = vm.qmp(command, ** - { + opts = { 'driver': iotests.imgfmt, 'node-name': id, 'read-only': readOnly, @@ -131,7 +130,11 @@ class EncryptionSetupTestCase(iotests.QMPTestCase): 'filename': test_img, } } - ) + + if reOpen: + result = vm.qmp(command, options=[opts]) + else: + result = vm.qmp(command, **opts) self.assert_qmp(result, 'return', {}) diff --git a/tests/qemu-iotests/298 b/tests/qemu-iotests/298 index d535946b5f..a5b0d91224 100755 --- a/tests/qemu-iotests/298 +++ b/tests/qemu-iotests/298 @@ -98,7 +98,7 @@ class TestPreallocateFilter(TestPreallocateBase): self.check_big() def test_reopen_opts(self): - result = self.vm.qmp('x-blockdev-reopen', **{ + result = self.vm.qmp('x-blockdev-reopen', options=[{ 'node-name': 'disk', 'driver': iotests.imgfmt, 'file': { @@ -112,7 +112,7 @@ class TestPreallocateFilter(TestPreallocateBase): 'filename': disk } } - }) + }]) self.assert_qmp(result, 'return', {}) self.vm.hmp_qemu_io('drive0', 'write 0 1M') diff --git a/tests/qemu-iotests/tests/remove-bitmap-from-backing b/tests/qemu-iotests/tests/remove-bitmap-from-backing index 0ea4c36507..0b07f7e836 100755 --- a/tests/qemu-iotests/tests/remove-bitmap-from-backing +++ b/tests/qemu-iotests/tests/remove-bitmap-from-backing @@ -41,13 +41,15 @@ log('Trying to remove persistent bitmap from r-o base node, should fail:') vm.qmp_log('block-dirty-bitmap-remove', node='base', name='bitmap0') new_base_opts = { - 'node-name': 'base', - 'driver': 'qcow2', - 'file': { - 'driver': 'file', - 'filename': base - }, - 'read-only': False + 'options': [{ + 'node-name': 'base', + 'driver': 'qcow2', + 'file': { + 'driver': 'file', + 'filename': base + }, + 'read-only': False + }] } # Don't want to bother with filtering qmp_log for reopen command @@ -58,7 +60,7 @@ if result != {'return': {}}: log('Remove persistent bitmap from base node reopened to RW:') vm.qmp_log('block-dirty-bitmap-remove', node='base', name='bitmap0') -new_base_opts['read-only'] = True +new_base_opts['options'][0]['read-only'] = True result = vm.qmp('x-blockdev-reopen', **new_base_opts) if result != {'return': {}}: log('Failed to reopen: ' + str(result)) From 246ebc2d6a3adb8eb7514155daa7e6369ae8d654 Mon Sep 17 00:00:00 2001 From: Alberto Garcia Date: Thu, 8 Jul 2021 13:47:08 +0200 Subject: [PATCH 134/272] iotests: Test reopening multiple devices at the same time This test swaps the images used by two active block devices. This is now possible thanks to the new ability to run x-blockdev-reopen on multiple devices at the same time. Signed-off-by: Alberto Garcia Signed-off-by: Kevin Wolf Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20210708114709.206487-6-kwolf@redhat.com> Signed-off-by: Kevin Wolf --- tests/qemu-iotests/245 | 47 ++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/245.out | 4 ++-- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245 index ca08955207..8bc8101e6d 100755 --- a/tests/qemu-iotests/245 +++ b/tests/qemu-iotests/245 @@ -649,6 +649,53 @@ class TestBlockdevReopen(iotests.QMPTestCase): '-c', 'read -P 0x40 0x40008 1', '-c', 'read -P 0x80 0x40010 1', hd_path[0]) + # Swap the disk images of two active block devices + def test_swap_files(self): + # Add hd0 and hd2 (none of them with backing files) + opts0 = hd_opts(0) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts0) + self.assert_qmp(result, 'return', {}) + + opts2 = hd_opts(2) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts2) + self.assert_qmp(result, 'return', {}) + + # Write different data to both block devices + self.run_qemu_io("hd0", "write -P 0xa0 0 1k") + self.run_qemu_io("hd2", "write -P 0xa2 0 1k") + + # Check that the data reads correctly + self.run_qemu_io("hd0", "read -P 0xa0 0 1k") + self.run_qemu_io("hd2", "read -P 0xa2 0 1k") + + # It's not possible to make a block device use an image that + # is already being used by the other device. + self.reopen(opts0, {'file': 'hd2-file'}, + "Permission conflict on node 'hd2-file': permissions " + "'write, resize' are both required by node 'hd2' (uses " + "node 'hd2-file' as 'file' child) and unshared by node " + "'hd0' (uses node 'hd2-file' as 'file' child).") + self.reopen(opts2, {'file': 'hd0-file'}, + "Permission conflict on node 'hd0-file': permissions " + "'write, resize' are both required by node 'hd0' (uses " + "node 'hd0-file' as 'file' child) and unshared by node " + "'hd2' (uses node 'hd0-file' as 'file' child).") + + # But we can swap the images if we reopen both devices at the + # same time + opts0['file'] = 'hd2-file' + opts2['file'] = 'hd0-file' + self.reopenMultiple([opts0, opts2]) + self.run_qemu_io("hd0", "read -P 0xa2 0 1k") + self.run_qemu_io("hd2", "read -P 0xa0 0 1k") + + # And we can of course come back to the original state + opts0['file'] = 'hd0-file' + opts2['file'] = 'hd2-file' + self.reopenMultiple([opts0, opts2]) + self.run_qemu_io("hd0", "read -P 0xa0 0 1k") + self.run_qemu_io("hd2", "read -P 0xa2 0 1k") + # Misc reopen tests with different block drivers @iotests.skip_if_unsupported(['quorum', 'throttle']) def test_misc_drivers(self): diff --git a/tests/qemu-iotests/245.out b/tests/qemu-iotests/245.out index daf1e51922..4eced19294 100644 --- a/tests/qemu-iotests/245.out +++ b/tests/qemu-iotests/245.out @@ -17,8 +17,8 @@ read 1/1 bytes at offset 262152 read 1/1 bytes at offset 262160 1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -.............. +............... ---------------------------------------------------------------------- -Ran 24 tests +Ran 25 tests OK From e60edf69e2f64e818466019313517a2e6d6b63f4 Mon Sep 17 00:00:00 2001 From: Alberto Garcia Date: Thu, 8 Jul 2021 13:47:09 +0200 Subject: [PATCH 135/272] block: Make blockdev-reopen stable API This patch drops the 'x-' prefix from x-blockdev-reopen. Signed-off-by: Alberto Garcia Signed-off-by: Kevin Wolf Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20210708114709.206487-7-kwolf@redhat.com> Signed-off-by: Kevin Wolf --- blockdev.c | 2 +- qapi/block-core.json | 6 +++--- tests/qemu-iotests/155 | 2 +- tests/qemu-iotests/165 | 2 +- tests/qemu-iotests/245 | 10 +++++----- tests/qemu-iotests/248 | 2 +- tests/qemu-iotests/248.out | 2 +- tests/qemu-iotests/296 | 2 +- tests/qemu-iotests/298 | 2 +- tests/qemu-iotests/tests/remove-bitmap-from-backing | 4 ++-- 10 files changed, 17 insertions(+), 17 deletions(-) diff --git a/blockdev.c b/blockdev.c index 5ad0e9070e..3d8ac368a1 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3559,7 +3559,7 @@ fail: visit_free(v); } -void qmp_x_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp) +void qmp_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp) { BlockReopenQueue *queue = NULL; GSList *drained = NULL; diff --git a/qapi/block-core.json b/qapi/block-core.json index 052520331e..c7a311798a 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -4219,7 +4219,7 @@ { 'command': 'blockdev-add', 'data': 'BlockdevOptions', 'boxed': true } ## -# @x-blockdev-reopen: +# @blockdev-reopen: # # Reopens one or more block devices using the given set of options. # Any option not specified will be reset to its default value regardless @@ -4257,9 +4257,9 @@ # image does not have a default backing file name as part of its # metadata. # -# Since: 4.0 +# Since: 6.1 ## -{ 'command': 'x-blockdev-reopen', +{ 'command': 'blockdev-reopen', 'data': { 'options': ['BlockdevOptions'] } } ## diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155 index 2947bfb81a..eadda52615 100755 --- a/tests/qemu-iotests/155 +++ b/tests/qemu-iotests/155 @@ -261,7 +261,7 @@ class TestBlockdevMirrorReopen(MirrorBaseClass): result = self.vm.qmp('blockdev-add', node_name="backing", driver="null-co") self.assert_qmp(result, 'return', {}) - result = self.vm.qmp('x-blockdev-reopen', options=[{ + result = self.vm.qmp('blockdev-reopen', options=[{ 'node-name': "target", 'driver': iotests.imgfmt, 'file': "target-file", diff --git a/tests/qemu-iotests/165 b/tests/qemu-iotests/165 index ef4cf14516..ce499946b8 100755 --- a/tests/qemu-iotests/165 +++ b/tests/qemu-iotests/165 @@ -137,7 +137,7 @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase): assert sha256_1 == self.getSha256() # Reopen to RW - result = self.vm.qmp('x-blockdev-reopen', options=[{ + result = self.vm.qmp('blockdev-reopen', options=[{ 'node-name': 'node0', 'driver': iotests.imgfmt, 'file': { diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245 index 8bc8101e6d..bf8261eec0 100755 --- a/tests/qemu-iotests/245 +++ b/tests/qemu-iotests/245 @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # group: rw # -# Test cases for the QMP 'x-blockdev-reopen' command +# Test cases for the QMP 'blockdev-reopen' command # # Copyright (C) 2018-2019 Igalia, S.L. # Author: Alberto Garcia @@ -85,16 +85,16 @@ class TestBlockdevReopen(iotests.QMPTestCase): "Expected output of %d qemu-io commands, found %d" % (found, self.total_io_cmds)) - # Run x-blockdev-reopen on a list of block devices + # Run blockdev-reopen on a list of block devices def reopenMultiple(self, opts, errmsg = None): - result = self.vm.qmp('x-blockdev-reopen', conv_keys=False, options=opts) + result = self.vm.qmp('blockdev-reopen', conv_keys=False, options=opts) if errmsg: self.assert_qmp(result, 'error/class', 'GenericError') self.assert_qmp(result, 'error/desc', errmsg) else: self.assert_qmp(result, 'return', {}) - # Run x-blockdev-reopen on a single block device (specified by + # Run blockdev-reopen on a single block device (specified by # 'opts') but applying 'newopts' on top of it. The original 'opts' # dict is unmodified def reopen(self, opts, newopts = {}, errmsg = None): @@ -161,7 +161,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): self.reopen(opts, {'file.locking': 'off'}, "Cannot change the option 'locking'") self.reopen(opts, {'file.filename': None}, "Invalid parameter type for 'options[0].file.filename', expected: string") - # node-name is optional in BlockdevOptions, but x-blockdev-reopen needs it + # node-name is optional in BlockdevOptions, but blockdev-reopen needs it del opts['node-name'] self.reopen(opts, {}, "node-name not specified") diff --git a/tests/qemu-iotests/248 b/tests/qemu-iotests/248 index 03911333c4..2ec2416e8a 100755 --- a/tests/qemu-iotests/248 +++ b/tests/qemu-iotests/248 @@ -62,7 +62,7 @@ vm.event_wait('JOB_STATUS_CHANGE', timeout=3.0, vm.get_qmp_events() del blockdev_opts['file']['size'] -vm.qmp_log('x-blockdev-reopen', filters=[filter_qmp_testfiles], +vm.qmp_log('blockdev-reopen', filters=[filter_qmp_testfiles], options = [ blockdev_opts ]) vm.qmp_log('block-job-resume', device='drive0') diff --git a/tests/qemu-iotests/248.out b/tests/qemu-iotests/248.out index 893f625347..66e94ccd7e 100644 --- a/tests/qemu-iotests/248.out +++ b/tests/qemu-iotests/248.out @@ -2,7 +2,7 @@ {"return": {}} {"execute": "blockdev-mirror", "arguments": {"device": "drive0", "on-target-error": "enospc", "sync": "full", "target": "target"}} {"return": {}} -{"execute": "x-blockdev-reopen", "arguments": {"options": [{"driver": "qcow2", "file": {"driver": "raw", "file": {"driver": "file", "filename": "TEST_DIR/PID-target"}}, "node-name": "target"}]}} +{"execute": "blockdev-reopen", "arguments": {"options": [{"driver": "qcow2", "file": {"driver": "raw", "file": {"driver": "file", "filename": "TEST_DIR/PID-target"}}, "node-name": "target"}]}} {"return": {}} {"execute": "block-job-resume", "arguments": {"device": "drive0"}} {"return": {}} diff --git a/tests/qemu-iotests/296 b/tests/qemu-iotests/296 index fa07b98bb4..099a3eeaa5 100755 --- a/tests/qemu-iotests/296 +++ b/tests/qemu-iotests/296 @@ -118,7 +118,7 @@ class EncryptionSetupTestCase(iotests.QMPTestCase): def openImageQmp(self, vm, id, file, secret, readOnly = False, reOpen = False): - command = 'x-blockdev-reopen' if reOpen else 'blockdev-add' + command = 'blockdev-reopen' if reOpen else 'blockdev-add' opts = { 'driver': iotests.imgfmt, diff --git a/tests/qemu-iotests/298 b/tests/qemu-iotests/298 index a5b0d91224..fae72211b1 100755 --- a/tests/qemu-iotests/298 +++ b/tests/qemu-iotests/298 @@ -98,7 +98,7 @@ class TestPreallocateFilter(TestPreallocateBase): self.check_big() def test_reopen_opts(self): - result = self.vm.qmp('x-blockdev-reopen', options=[{ + result = self.vm.qmp('blockdev-reopen', options=[{ 'node-name': 'disk', 'driver': iotests.imgfmt, 'file': { diff --git a/tests/qemu-iotests/tests/remove-bitmap-from-backing b/tests/qemu-iotests/tests/remove-bitmap-from-backing index 0b07f7e836..8d48fc0f3c 100755 --- a/tests/qemu-iotests/tests/remove-bitmap-from-backing +++ b/tests/qemu-iotests/tests/remove-bitmap-from-backing @@ -53,7 +53,7 @@ new_base_opts = { } # Don't want to bother with filtering qmp_log for reopen command -result = vm.qmp('x-blockdev-reopen', **new_base_opts) +result = vm.qmp('blockdev-reopen', **new_base_opts) if result != {'return': {}}: log('Failed to reopen: ' + str(result)) @@ -61,7 +61,7 @@ log('Remove persistent bitmap from base node reopened to RW:') vm.qmp_log('block-dirty-bitmap-remove', node='base', name='bitmap0') new_base_opts['options'][0]['read-only'] = True -result = vm.qmp('x-blockdev-reopen', **new_base_opts) +result = vm.qmp('blockdev-reopen', **new_base_opts) if result != {'return': {}}: log('Failed to reopen: ' + str(result)) From 0f76debd1fff9bb8234e9ca921ef6f9c14be46a9 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Thu, 17 Jun 2021 18:56:44 +0200 Subject: [PATCH 136/272] stm32f100: Add the stm32f100 SoC This SoC is similar to stm32f205 SoC. This will be used by the STM32VLDISCOVERY to create a machine. Signed-off-by: Alexandre Iooss Reviewed-by: Alistair Francis Message-id: 20210617165647.2575955-2-erdnaxe@crans.org Signed-off-by: Peter Maydell --- MAINTAINERS | 6 ++ hw/arm/Kconfig | 6 ++ hw/arm/meson.build | 1 + hw/arm/stm32f100_soc.c | 182 +++++++++++++++++++++++++++++++++ include/hw/arm/stm32f100_soc.h | 57 +++++++++++ 5 files changed, 252 insertions(+) create mode 100644 hw/arm/stm32f100_soc.c create mode 100644 include/hw/arm/stm32f100_soc.h diff --git a/MAINTAINERS b/MAINTAINERS index 809830c655..8cfed2dd2d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -948,6 +948,12 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/arm/virt-acpi-build.c +STM32F100 +M: Alexandre Iooss +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/stm32f100_soc.c + STM32F205 M: Alistair Francis M: Peter Maydell diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 647b5c8b43..a5c2e1d991 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -326,6 +326,12 @@ config RASPI select SDHCI select USB_DWC2 +config STM32F100_SOC + bool + select ARM_V7M + select STM32F2XX_USART + select STM32F2XX_SPI + config STM32F205_SOC bool select ARM_V7M diff --git a/hw/arm/meson.build b/hw/arm/meson.build index be39117b9b..0e637e6a9e 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -39,6 +39,7 @@ arm_ss.add(when: 'CONFIG_STRONGARM', if_true: files('strongarm.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_RASPI', if_true: files('bcm2835_peripherals.c', 'bcm2836.c', 'raspi.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_XLNX_ZYNQMP_ARM', if_true: files('xlnx-zynqmp.c', 'xlnx-zcu102.c')) diff --git a/hw/arm/stm32f100_soc.c b/hw/arm/stm32f100_soc.c new file mode 100644 index 0000000000..0c4a5c6645 --- /dev/null +++ b/hw/arm/stm32f100_soc.c @@ -0,0 +1,182 @@ +/* + * STM32F100 SoC + * + * Copyright (c) 2021 Alexandre Iooss + * Copyright (c) 2014 Alistair Francis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "hw/arm/boot.h" +#include "exec/address-spaces.h" +#include "hw/arm/stm32f100_soc.h" +#include "hw/qdev-properties.h" +#include "hw/misc/unimp.h" +#include "sysemu/sysemu.h" + +/* stm32f100_soc implementation is derived from stm32f205_soc */ + +static const uint32_t usart_addr[STM_NUM_USARTS] = { 0x40013800, 0x40004400, + 0x40004800 }; +static const uint32_t spi_addr[STM_NUM_SPIS] = { 0x40013000, 0x40003800 }; + +static const int usart_irq[STM_NUM_USARTS] = {37, 38, 39}; +static const int spi_irq[STM_NUM_SPIS] = {35, 36}; + +static void stm32f100_soc_initfn(Object *obj) +{ + STM32F100State *s = STM32F100_SOC(obj); + int i; + + object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M); + + for (i = 0; i < STM_NUM_USARTS; i++) { + object_initialize_child(obj, "usart[*]", &s->usart[i], + TYPE_STM32F2XX_USART); + } + + for (i = 0; i < STM_NUM_SPIS; i++) { + object_initialize_child(obj, "spi[*]", &s->spi[i], TYPE_STM32F2XX_SPI); + } +} + +static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp) +{ + STM32F100State *s = STM32F100_SOC(dev_soc); + DeviceState *dev, *armv7m; + SysBusDevice *busdev; + int i; + + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *sram = g_new(MemoryRegion, 1); + MemoryRegion *flash = g_new(MemoryRegion, 1); + MemoryRegion *flash_alias = g_new(MemoryRegion, 1); + + /* + * Init flash region + * Flash starts at 0x08000000 and then is aliased to boot memory at 0x0 + */ + memory_region_init_rom(flash, OBJECT(dev_soc), "STM32F100.flash", + FLASH_SIZE, &error_fatal); + memory_region_init_alias(flash_alias, OBJECT(dev_soc), + "STM32F100.flash.alias", flash, 0, FLASH_SIZE); + memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, flash); + memory_region_add_subregion(system_memory, 0, flash_alias); + + /* Init SRAM region */ + memory_region_init_ram(sram, NULL, "STM32F100.sram", SRAM_SIZE, + &error_fatal); + memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, sram); + + /* Init ARMv7m */ + armv7m = DEVICE(&s->armv7m); + qdev_prop_set_uint32(armv7m, "num-irq", 61); + qdev_prop_set_string(armv7m, "cpu-type", s->cpu_type); + qdev_prop_set_bit(armv7m, "enable-bitband", true); + object_property_set_link(OBJECT(&s->armv7m), "memory", + OBJECT(get_system_memory()), &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) { + return; + } + + /* Attach UART (uses USART registers) and USART controllers */ + for (i = 0; i < STM_NUM_USARTS; i++) { + dev = DEVICE(&(s->usart[i])); + qdev_prop_set_chr(dev, "chardev", serial_hd(i)); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->usart[i]), errp)) { + return; + } + busdev = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(busdev, 0, usart_addr[i]); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, usart_irq[i])); + } + + /* SPI 1 and 2 */ + for (i = 0; i < STM_NUM_SPIS; i++) { + dev = DEVICE(&(s->spi[i])); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) { + return; + } + busdev = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(busdev, 0, spi_addr[i]); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, spi_irq[i])); + } + + create_unimplemented_device("timer[2]", 0x40000000, 0x400); + create_unimplemented_device("timer[3]", 0x40000400, 0x400); + create_unimplemented_device("timer[4]", 0x40000800, 0x400); + create_unimplemented_device("timer[6]", 0x40001000, 0x400); + create_unimplemented_device("timer[7]", 0x40001400, 0x400); + create_unimplemented_device("RTC", 0x40002800, 0x400); + create_unimplemented_device("WWDG", 0x40002C00, 0x400); + create_unimplemented_device("IWDG", 0x40003000, 0x400); + create_unimplemented_device("I2C1", 0x40005400, 0x400); + create_unimplemented_device("I2C2", 0x40005800, 0x400); + create_unimplemented_device("BKP", 0x40006C00, 0x400); + create_unimplemented_device("PWR", 0x40007000, 0x400); + create_unimplemented_device("DAC", 0x40007400, 0x400); + create_unimplemented_device("CEC", 0x40007800, 0x400); + create_unimplemented_device("AFIO", 0x40010000, 0x400); + create_unimplemented_device("EXTI", 0x40010400, 0x400); + create_unimplemented_device("GPIOA", 0x40010800, 0x400); + create_unimplemented_device("GPIOB", 0x40010C00, 0x400); + create_unimplemented_device("GPIOC", 0x40011000, 0x400); + create_unimplemented_device("GPIOD", 0x40011400, 0x400); + create_unimplemented_device("GPIOE", 0x40011800, 0x400); + create_unimplemented_device("ADC1", 0x40012400, 0x400); + create_unimplemented_device("timer[1]", 0x40012C00, 0x400); + create_unimplemented_device("timer[15]", 0x40014000, 0x400); + create_unimplemented_device("timer[16]", 0x40014400, 0x400); + create_unimplemented_device("timer[17]", 0x40014800, 0x400); + create_unimplemented_device("DMA", 0x40020000, 0x400); + create_unimplemented_device("RCC", 0x40021000, 0x400); + create_unimplemented_device("Flash Int", 0x40022000, 0x400); + create_unimplemented_device("CRC", 0x40023000, 0x400); +} + +static Property stm32f100_soc_properties[] = { + DEFINE_PROP_STRING("cpu-type", STM32F100State, cpu_type), + DEFINE_PROP_END_OF_LIST(), +}; + +static void stm32f100_soc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = stm32f100_soc_realize; + device_class_set_props(dc, stm32f100_soc_properties); +} + +static const TypeInfo stm32f100_soc_info = { + .name = TYPE_STM32F100_SOC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32F100State), + .instance_init = stm32f100_soc_initfn, + .class_init = stm32f100_soc_class_init, +}; + +static void stm32f100_soc_types(void) +{ + type_register_static(&stm32f100_soc_info); +} + +type_init(stm32f100_soc_types) diff --git a/include/hw/arm/stm32f100_soc.h b/include/hw/arm/stm32f100_soc.h new file mode 100644 index 0000000000..71bffcf4fd --- /dev/null +++ b/include/hw/arm/stm32f100_soc.h @@ -0,0 +1,57 @@ +/* + * STM32F100 SoC + * + * Copyright (c) 2021 Alexandre Iooss + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HW_ARM_STM32F100_SOC_H +#define HW_ARM_STM32F100_SOC_H + +#include "hw/char/stm32f2xx_usart.h" +#include "hw/ssi/stm32f2xx_spi.h" +#include "hw/arm/armv7m.h" +#include "qom/object.h" + +#define TYPE_STM32F100_SOC "stm32f100-soc" +OBJECT_DECLARE_SIMPLE_TYPE(STM32F100State, STM32F100_SOC) + +#define STM_NUM_USARTS 3 +#define STM_NUM_SPIS 2 + +#define FLASH_BASE_ADDRESS 0x08000000 +#define FLASH_SIZE (128 * 1024) +#define SRAM_BASE_ADDRESS 0x20000000 +#define SRAM_SIZE (8 * 1024) + +struct STM32F100State { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + char *cpu_type; + + ARMv7MState armv7m; + + STM32F2XXUsartState usart[STM_NUM_USARTS]; + STM32F2XXSPIState spi[STM_NUM_SPIS]; +}; + +#endif From 2ac2410c5e39ac4a317b38d14f0f878fe007c6e5 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Thu, 17 Jun 2021 18:56:45 +0200 Subject: [PATCH 137/272] stm32vldiscovery: Add the STM32VLDISCOVERY Machine This is a Cortex-M3 based machine. Information can be found at: https://www.st.com/en/evaluation-tools/stm32vldiscovery.html Signed-off-by: Alexandre Iooss Reviewed-by: Alistair Francis Message-id: 20210617165647.2575955-3-erdnaxe@crans.org Signed-off-by: Peter Maydell --- MAINTAINERS | 6 +++ default-configs/devices/arm-softmmu.mak | 1 + hw/arm/Kconfig | 4 ++ hw/arm/meson.build | 1 + hw/arm/stm32vldiscovery.c | 66 +++++++++++++++++++++++++ 5 files changed, 78 insertions(+) create mode 100644 hw/arm/stm32vldiscovery.c diff --git a/MAINTAINERS b/MAINTAINERS index 8cfed2dd2d..f5919498af 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -893,6 +893,12 @@ F: hw/*/stellaris* F: include/hw/input/gamepad.h F: docs/system/arm/stellaris.rst +STM32VLDISCOVERY +M: Alexandre Iooss +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/stm32vldiscovery.c + Versatile Express M: Peter Maydell L: qemu-arm@nongnu.org diff --git a/default-configs/devices/arm-softmmu.mak b/default-configs/devices/arm-softmmu.mak index 0500156a0c..cdc0e97f9d 100644 --- a/default-configs/devices/arm-softmmu.mak +++ b/default-configs/devices/arm-softmmu.mak @@ -18,6 +18,7 @@ CONFIG_CHEETAH=y CONFIG_SX1=y CONFIG_NSERIES=y CONFIG_STELLARIS=y +CONFIG_STM32VLDISCOVERY=y CONFIG_REALVIEW=y CONFIG_VERSATILE=y CONFIG_VEXPRESS=y diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index a5c2e1d991..c521189628 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -239,6 +239,10 @@ config STELLARIS select STELLARIS_ENET # ethernet select UNIMP +config STM32VLDISCOVERY + bool + select STM32F100_SOC + config STRONGARM bool select PXA2XX diff --git a/hw/arm/meson.build b/hw/arm/meson.build index 0e637e6a9e..721a8eb8be 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -24,6 +24,7 @@ arm_ss.add(when: 'CONFIG_Z2', if_true: files('z2.c')) arm_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_COLLIE', if_true: files('collie.c')) arm_ss.add(when: 'CONFIG_VERSATILE', if_true: files('versatilepb.c')) arm_ss.add(when: 'CONFIG_VEXPRESS', if_true: files('vexpress.c')) diff --git a/hw/arm/stm32vldiscovery.c b/hw/arm/stm32vldiscovery.c new file mode 100644 index 0000000000..7e8191ebf5 --- /dev/null +++ b/hw/arm/stm32vldiscovery.c @@ -0,0 +1,66 @@ +/* + * ST STM32VLDISCOVERY machine + * + * Copyright (c) 2021 Alexandre Iooss + * Copyright (c) 2014 Alistair Francis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/boards.h" +#include "hw/qdev-properties.h" +#include "qemu/error-report.h" +#include "hw/arm/stm32f100_soc.h" +#include "hw/arm/boot.h" + +/* stm32vldiscovery implementation is derived from netduinoplus2 */ + +/* Main SYSCLK frequency in Hz (24MHz) */ +#define SYSCLK_FRQ 24000000ULL + +static void stm32vldiscovery_init(MachineState *machine) +{ + DeviceState *dev; + + /* + * TODO: ideally we would model the SoC RCC and let it handle + * system_clock_scale, including its ability to define different + * possible SYSCLK sources. + */ + system_clock_scale = NANOSECONDS_PER_SECOND / SYSCLK_FRQ; + + dev = qdev_new(TYPE_STM32F100_SOC); + qdev_prop_set_string(dev, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m3")); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + armv7m_load_kernel(ARM_CPU(first_cpu), + machine->kernel_filename, + FLASH_SIZE); +} + +static void stm32vldiscovery_machine_init(MachineClass *mc) +{ + mc->desc = "ST STM32VLDISCOVERY (Cortex-M3)"; + mc->init = stm32vldiscovery_init; +} + +DEFINE_MACHINE("stm32vldiscovery", stm32vldiscovery_machine_init) + From 1af060e57480905f91f88362f867fec8e20b566e Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Thu, 17 Jun 2021 18:56:46 +0200 Subject: [PATCH 138/272] docs/system: arm: Add stm32 boards description This adds the target guide for Netduino 2, Netduino Plus 2 and STM32VLDISCOVERY. Signed-off-by: Alexandre Iooss Reviewed-by: Alistair Francis Message-id: 20210617165647.2575955-4-erdnaxe@crans.org Signed-off-by: Peter Maydell --- MAINTAINERS | 1 + docs/system/arm/stm32.rst | 66 ++++++++++++++++++++++++++++++++++++++ docs/system/target-arm.rst | 1 + 3 files changed, 68 insertions(+) create mode 100644 docs/system/arm/stm32.rst diff --git a/MAINTAINERS b/MAINTAINERS index f5919498af..bad893bfd9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -898,6 +898,7 @@ M: Alexandre Iooss L: qemu-arm@nongnu.org S: Maintained F: hw/arm/stm32vldiscovery.c +F: docs/system/arm/stm32.rst Versatile Express M: Peter Maydell diff --git a/docs/system/arm/stm32.rst b/docs/system/arm/stm32.rst new file mode 100644 index 0000000000..508b92cf86 --- /dev/null +++ b/docs/system/arm/stm32.rst @@ -0,0 +1,66 @@ +STMicroelectronics STM32 boards (``netduino2``, ``netduinoplus2``, ``stm32vldiscovery``) +======================================================================================== + +The `STM32`_ chips are a family of 32-bit ARM-based microcontroller by +STMicroelectronics. + +.. _STM32: https://www.st.com/en/microcontrollers-microprocessors/stm32-32-bit-arm-cortex-mcus.html + +The STM32F1 series is based on ARM Cortex-M3 core. The following machines are +based on this chip : + +- ``stm32vldiscovery`` STM32VLDISCOVERY board with STM32F100RBT6 microcontroller + +The STM32F2 series is based on ARM Cortex-M3 core. The following machines are +based on this chip : + +- ``netduino2`` Netduino 2 board with STM32F205RFT6 microcontroller + +The STM32F4 series is based on ARM Cortex-M4F core. This series is pin-to-pin +compatible with STM32F2 series. The following machines are based on this chip : + +- ``netduinoplus2`` Netduino Plus 2 board with STM32F405RGT6 microcontroller + +There are many other STM32 series that are currently not supported by QEMU. + +Supported devices +----------------- + + * ARM Cortex-M3, Cortex M4F + * Analog to Digital Converter (ADC) + * EXTI interrupt + * Serial ports (USART) + * SPI controller + * System configuration (SYSCFG) + * Timer controller (TIMER) + +Missing devices +--------------- + + * Camera interface (DCMI) + * Controller Area Network (CAN) + * Cycle Redundancy Check (CRC) calculation unit + * Digital to Analog Converter (DAC) + * DMA controller + * Ethernet controller + * Flash Interface Unit + * GPIO controller + * I2C controller + * Inter-Integrated Sound (I2S) controller + * Power supply configuration (PWR) + * Random Number Generator (RNG) + * Real-Time Clock (RTC) controller + * Reset and Clock Controller (RCC) + * Secure Digital Input/Output (SDIO) interface + * USB OTG + * Watchdog controller (IWDG, WWDG) + +Boot options +------------ + +The STM32 machines can be started using the ``-kernel`` option to load a +firmware. Example: + +.. code-block:: bash + + $ qemu-system-arm -M stm32vldiscovery -kernel firmware.bin diff --git a/docs/system/target-arm.rst b/docs/system/target-arm.rst index 13b3eeaf07..705b8835e4 100644 --- a/docs/system/target-arm.rst +++ b/docs/system/target-arm.rst @@ -97,6 +97,7 @@ undocumented; you can get a complete list by running arm/collie arm/sx1 arm/stellaris + arm/stm32 arm/virt arm/xlnx-versal-virt From 7cb4097f2d559b5ea4ad993653abc1e542deb625 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Thu, 17 Jun 2021 18:56:47 +0200 Subject: [PATCH 139/272] tests/boot-serial-test: Add STM32VLDISCOVERY board testcase New mini-kernel test for STM32VLDISCOVERY USART1. Signed-off-by: Alexandre Iooss Acked-by: Thomas Huth Acked-by: Alistair Francis Message-id: 20210617165647.2575955-5-erdnaxe@crans.org Signed-off-by: Peter Maydell --- tests/qtest/boot-serial-test.c | 37 ++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c index d40adddafa..96849cec91 100644 --- a/tests/qtest/boot-serial-test.c +++ b/tests/qtest/boot-serial-test.c @@ -94,6 +94,41 @@ static const uint8_t kernel_nrf51[] = { 0x1c, 0x25, 0x00, 0x40 /* 0x4000251c = UART TXD */ }; +static const uint8_t kernel_stm32vldiscovery[] = { + 0x00, 0x00, 0x00, 0x00, /* Stack top address */ + 0x1d, 0x00, 0x00, 0x00, /* Reset handler address */ + 0x00, 0x00, 0x00, 0x00, /* NMI */ + 0x00, 0x00, 0x00, 0x00, /* Hard fault */ + 0x00, 0x00, 0x00, 0x00, /* Memory management fault */ + 0x00, 0x00, 0x00, 0x00, /* Bus fault */ + 0x00, 0x00, 0x00, 0x00, /* Usage fault */ + 0x0b, 0x4b, /* ldr r3, [pc, #44] Get RCC */ + 0x44, 0xf2, 0x04, 0x02, /* movw r2, #16388 */ + 0x1a, 0x60, /* str r2, [r3] */ + 0x0a, 0x4b, /* ldr r3, [pc, #40] Get GPIOA */ + 0x1a, 0x68, /* ldr r2, [r3] */ + 0x22, 0xf0, 0xf0, 0x02, /* bic r2, r2, #240 */ + 0x1a, 0x60, /* str r2, [r3] */ + 0x1a, 0x68, /* ldr r2, [r3] */ + 0x42, 0xf0, 0xb0, 0x02, /* orr r2, r2, #176 */ + 0x1a, 0x60, /* str r2, [r3] */ + 0x07, 0x4b, /* ldr r3, [pc, #26] Get BAUD */ + 0x45, 0x22, /* movs r2, #69 */ + 0x1a, 0x60, /* str r2, [r3] */ + 0x06, 0x4b, /* ldr r3, [pc, #24] Get ENABLE */ + 0x42, 0xf2, 0x08, 0x02, /* movw r2, #8200 */ + 0x1a, 0x60, /* str r2, [r3] */ + 0x05, 0x4b, /* ldr r3, [pc, #20] Get TXD */ + 0x54, 0x22, /* movs r2, 'T' */ + 0x1a, 0x60, /* str r2, [r3] */ + 0xfe, 0xe7, /* b . */ + 0x18, 0x10, 0x02, 0x40, /* 0x40021018 = RCC */ + 0x04, 0x08, 0x01, 0x40, /* 0x40010804 = GPIOA */ + 0x08, 0x38, 0x01, 0x40, /* 0x40013808 = USART1 BAUD */ + 0x0c, 0x38, 0x01, 0x40, /* 0x4001380c = USART1 ENABLE */ + 0x04, 0x38, 0x01, 0x40 /* 0x40013804 = USART1 TXD */ +}; + typedef struct testdef { const char *arch; /* Target architecture */ const char *machine; /* Name of the machine */ @@ -144,6 +179,8 @@ static testdef_t tests[] = { { "aarch64", "virt", "-cpu max", "TT", sizeof(kernel_aarch64), kernel_aarch64 }, { "arm", "microbit", "", "T", sizeof(kernel_nrf51), kernel_nrf51 }, + { "arm", "stm32vldiscovery", "", "T", + sizeof(kernel_stm32vldiscovery), kernel_stm32vldiscovery }, { NULL } }; From f4ec71d07cd2375c9080fbd4e85beffd05d73a11 Mon Sep 17 00:00:00 2001 From: Ricardo Koller Date: Fri, 2 Jul 2021 16:37:01 -0700 Subject: [PATCH 140/272] hw/intc/arm_gicv3_cpuif: Fix virtual irq number check in icv_[dir|eoir]_write icv_eoir_write() and icv_dir_write() ignore invalid virtual IRQ numbers (like LPIs). The issue is that these functions check against the number of implemented IRQs (QEMU's default is num_irq=288) which can be lower than the maximum virtual IRQ number (1020 - 1). The consequence is that if a hypervisor creates an LR for an IRQ between 288 and 1020, then the guest is unable to deactivate the resulting IRQ. Note that other functions that deal with large IRQ numbers, like icv_iar_read, check against 1020 and not against num_irq. Fix the checks by using GICV3_MAXIRQ (1020) instead of the number of implemented IRQs. Signed-off-by: Ricardo Koller Message-id: 20210702233701.3369-1-ricarkol@google.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_cpuif.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index 3e0641aff9..a032d505f5 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -1227,7 +1227,7 @@ static void icv_dir_write(CPUARMState *env, const ARMCPRegInfo *ri, trace_gicv3_icv_dir_write(gicv3_redist_affid(cs), value); - if (irq >= cs->gic->num_irq) { + if (irq >= GICV3_MAXIRQ) { /* Also catches special interrupt numbers and LPIs */ return; } @@ -1262,7 +1262,7 @@ static void icv_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri, trace_gicv3_icv_eoir_write(ri->crm == 8 ? 0 : 1, gicv3_redist_affid(cs), value); - if (irq >= cs->gic->num_irq) { + if (irq >= GICV3_MAXIRQ) { /* Also catches special interrupt numbers and LPIs */ return; } From 102d7d1fba6d1121c86ef31c33b808a3104ab263 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 2 Jul 2021 11:40:09 +0100 Subject: [PATCH 141/272] hw/gpio/pl061: Convert DPRINTF to tracepoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the use of the DPRINTF debug macro in the PL061 model to use tracepoints. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson --- hw/gpio/pl061.c | 27 +++++++++------------------ hw/gpio/trace-events | 6 ++++++ 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c index e72e77572a..a6ace88895 100644 --- a/hw/gpio/pl061.c +++ b/hw/gpio/pl061.c @@ -15,19 +15,7 @@ #include "qemu/log.h" #include "qemu/module.h" #include "qom/object.h" - -//#define DEBUG_PL061 1 - -#ifdef DEBUG_PL061 -#define DPRINTF(fmt, ...) \ -do { printf("pl061: " fmt , ## __VA_ARGS__); } while (0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "pl061: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "pl061: error: " fmt , ## __VA_ARGS__);} while (0) -#endif +#include "trace.h" static const uint8_t pl061_id[12] = { 0x00, 0x00, 0x00, 0x00, 0x61, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; @@ -107,7 +95,7 @@ static void pl061_update(PL061State *s) uint8_t out; int i; - DPRINTF("dir = %d, data = %d\n", s->dir, s->data); + trace_pl061_update(DEVICE(s)->canonical_path, s->dir, s->data); /* Outputs float high. */ /* FIXME: This is board dependent. */ @@ -118,8 +106,9 @@ static void pl061_update(PL061State *s) for (i = 0; i < N_GPIOS; i++) { mask = 1 << i; if (changed & mask) { - DPRINTF("Set output %d = %d\n", i, (out & mask) != 0); - qemu_set_irq(s->out[i], (out & mask) != 0); + int level = (out & mask) != 0; + trace_pl061_set_output(DEVICE(s)->canonical_path, i, level); + qemu_set_irq(s->out[i], level); } } } @@ -131,7 +120,8 @@ static void pl061_update(PL061State *s) for (i = 0; i < N_GPIOS; i++) { mask = 1 << i; if (changed & mask) { - DPRINTF("Changed input %d = %d\n", i, (s->data & mask) != 0); + trace_pl061_input_change(DEVICE(s)->canonical_path, i, + (s->data & mask) != 0); if (!(s->isense & mask)) { /* Edge interrupt */ @@ -150,7 +140,8 @@ static void pl061_update(PL061State *s) /* Level interrupt */ s->istate |= ~(s->data ^ s->iev) & s->isense; - DPRINTF("istate = %02X\n", s->istate); + trace_pl061_update_istate(DEVICE(s)->canonical_path, + s->istate, s->im, (s->istate & s->im) != 0); qemu_set_irq(s->irq, (s->istate & s->im) != 0); } diff --git a/hw/gpio/trace-events b/hw/gpio/trace-events index f0b664158e..48ccbb183c 100644 --- a/hw/gpio/trace-events +++ b/hw/gpio/trace-events @@ -13,6 +13,12 @@ nrf51_gpio_write(uint64_t offset, uint64_t value) "offset 0x%" PRIx64 " value 0x nrf51_gpio_set(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64 nrf51_gpio_update_output_irq(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64 +# pl061.c +pl061_update(const char *id, uint32_t dir, uint32_t data) "%s GPIODIR 0x%x GPIODATA 0x%x" +pl061_set_output(const char *id, int gpio, int level) "%s setting output %d to %d" +pl061_input_change(const char *id, int gpio, int level) "%s input %d changed to %d" +pl061_update_istate(const char *id, uint32_t istate, uint32_t im, int level) "%s GPIORIS 0x%x GPIOIE 0x%x interrupt level %d" + # sifive_gpio.c sifive_gpio_read(uint64_t offset, uint64_t r) "offset 0x%" PRIx64 " value 0x%" PRIx64 sifive_gpio_write(uint64_t offset, uint64_t value) "offset 0x%" PRIx64 " value 0x%" PRIx64 From e24a9f6a595cc4502b67046ea3860cae2be15b71 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 2 Jul 2021 11:40:10 +0100 Subject: [PATCH 142/272] hw/gpio/pl061: Clean up read/write offset handling logic Currently the pl061_read() and pl061_write() functions handle offsets using a combination of three if() statements and a switch(). Clean this up to use just a switch, using case ranges. This requires that instead of catching accesses to the luminary-only registers on a stock PL061 via a check on s->rsvd_start we use an "is this luminary?" check in the cases for each luminary-only register. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- hw/gpio/pl061.c | 104 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 79 insertions(+), 25 deletions(-) diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c index a6ace88895..b21b230402 100644 --- a/hw/gpio/pl061.c +++ b/hw/gpio/pl061.c @@ -55,7 +55,6 @@ struct PL061State { qemu_irq irq; qemu_irq out[N_GPIOS]; const unsigned char *id; - uint32_t rsvd_start; /* reserved area: [rsvd_start, 0xfcc] */ }; static const VMStateDescription vmstate_pl061 = { @@ -151,16 +150,9 @@ static uint64_t pl061_read(void *opaque, hwaddr offset, { PL061State *s = (PL061State *)opaque; - if (offset < 0x400) { - return s->data & (offset >> 2); - } - if (offset >= s->rsvd_start && offset <= 0xfcc) { - goto err_out; - } - if (offset >= 0xfd0 && offset < 0x1000) { - return s->id[(offset - 0xfd0) >> 2]; - } switch (offset) { + case 0x0 ... 0x3ff: /* Data */ + return s->data & (offset >> 2); case 0x400: /* Direction */ return s->dir; case 0x404: /* Interrupt sense */ @@ -178,33 +170,68 @@ static uint64_t pl061_read(void *opaque, hwaddr offset, case 0x420: /* Alternate function select */ return s->afsel; case 0x500: /* 2mA drive */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } return s->dr2r; case 0x504: /* 4mA drive */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } return s->dr4r; case 0x508: /* 8mA drive */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } return s->dr8r; case 0x50c: /* Open drain */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } return s->odr; case 0x510: /* Pull-up */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } return s->pur; case 0x514: /* Pull-down */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } return s->pdr; case 0x518: /* Slew rate control */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } return s->slr; case 0x51c: /* Digital enable */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } return s->den; case 0x520: /* Lock */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } return s->locked; case 0x524: /* Commit */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } return s->cr; case 0x528: /* Analog mode select */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } return s->amsel; + case 0xfd0 ... 0xfff: /* ID registers */ + return s->id[(offset - 0xfd0) >> 2]; default: + bad_offset: + qemu_log_mask(LOG_GUEST_ERROR, + "pl061_read: Bad offset %x\n", (int)offset); break; } -err_out: - qemu_log_mask(LOG_GUEST_ERROR, - "pl061_read: Bad offset %x\n", (int)offset); return 0; } @@ -214,16 +241,12 @@ static void pl061_write(void *opaque, hwaddr offset, PL061State *s = (PL061State *)opaque; uint8_t mask; - if (offset < 0x400) { + switch (offset) { + case 0 ... 0x3ff: mask = (offset >> 2) & s->dir; s->data = (s->data & ~mask) | (value & mask); pl061_update(s); return; - } - if (offset >= s->rsvd_start) { - goto err_out; - } - switch (offset) { case 0x400: /* Direction */ s->dir = value & 0xff; break; @@ -247,47 +270,80 @@ static void pl061_write(void *opaque, hwaddr offset, s->afsel = (s->afsel & ~mask) | (value & mask); break; case 0x500: /* 2mA drive */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } s->dr2r = value & 0xff; break; case 0x504: /* 4mA drive */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } s->dr4r = value & 0xff; break; case 0x508: /* 8mA drive */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } s->dr8r = value & 0xff; break; case 0x50c: /* Open drain */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } s->odr = value & 0xff; break; case 0x510: /* Pull-up */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } s->pur = value & 0xff; break; case 0x514: /* Pull-down */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } s->pdr = value & 0xff; break; case 0x518: /* Slew rate control */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } s->slr = value & 0xff; break; case 0x51c: /* Digital enable */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } s->den = value & 0xff; break; case 0x520: /* Lock */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } s->locked = (value != 0xacce551); break; case 0x524: /* Commit */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } if (!s->locked) s->cr = value & 0xff; break; case 0x528: + if (s->id != pl061_id_luminary) { + goto bad_offset; + } s->amsel = value & 0xff; break; default: - goto err_out; + bad_offset: + qemu_log_mask(LOG_GUEST_ERROR, + "pl061_write: Bad offset %x\n", (int)offset); + return; } pl061_update(s); return; -err_out: - qemu_log_mask(LOG_GUEST_ERROR, - "pl061_write: Bad offset %x\n", (int)offset); } static void pl061_reset(DeviceState *dev) @@ -343,7 +399,6 @@ static void pl061_luminary_init(Object *obj) PL061State *s = PL061(obj); s->id = pl061_id_luminary; - s->rsvd_start = 0x52c; } static void pl061_init(Object *obj) @@ -353,7 +408,6 @@ static void pl061_init(Object *obj) SysBusDevice *sbd = SYS_BUS_DEVICE(obj); s->id = pl061_id; - s->rsvd_start = 0x424; memory_region_init_io(&s->iomem, obj, &pl061_ops, s, "pl061", 0x1000); sysbus_init_mmio(sbd, &s->iomem); From 74d359b52db760f8818476f4fbaab0ffab76a8ef Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 2 Jul 2021 11:40:11 +0100 Subject: [PATCH 143/272] hw/gpio/pl061: Add tracepoints for register read and write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add tracepoints for reads and writes to the PL061 registers. This requires restructuring pl061_read() to only return after the tracepoint, rather than having lots of early-returns. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson --- hw/gpio/pl061.c | 70 ++++++++++++++++++++++++++++++-------------- hw/gpio/trace-events | 2 ++ 2 files changed, 50 insertions(+), 22 deletions(-) diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c index b21b230402..42f6e6c489 100644 --- a/hw/gpio/pl061.c +++ b/hw/gpio/pl061.c @@ -149,90 +149,114 @@ static uint64_t pl061_read(void *opaque, hwaddr offset, unsigned size) { PL061State *s = (PL061State *)opaque; + uint64_t r = 0; switch (offset) { case 0x0 ... 0x3ff: /* Data */ - return s->data & (offset >> 2); + r = s->data & (offset >> 2); + break; case 0x400: /* Direction */ - return s->dir; + r = s->dir; + break; case 0x404: /* Interrupt sense */ - return s->isense; + r = s->isense; + break; case 0x408: /* Interrupt both edges */ - return s->ibe; + r = s->ibe; + break; case 0x40c: /* Interrupt event */ - return s->iev; + r = s->iev; + break; case 0x410: /* Interrupt mask */ - return s->im; + r = s->im; + break; case 0x414: /* Raw interrupt status */ - return s->istate; + r = s->istate; + break; case 0x418: /* Masked interrupt status */ - return s->istate & s->im; + r = s->istate & s->im; + break; case 0x420: /* Alternate function select */ - return s->afsel; + r = s->afsel; + break; case 0x500: /* 2mA drive */ if (s->id != pl061_id_luminary) { goto bad_offset; } - return s->dr2r; + r = s->dr2r; + break; case 0x504: /* 4mA drive */ if (s->id != pl061_id_luminary) { goto bad_offset; } - return s->dr4r; + r = s->dr4r; + break; case 0x508: /* 8mA drive */ if (s->id != pl061_id_luminary) { goto bad_offset; } - return s->dr8r; + r = s->dr8r; + break; case 0x50c: /* Open drain */ if (s->id != pl061_id_luminary) { goto bad_offset; } - return s->odr; + r = s->odr; + break; case 0x510: /* Pull-up */ if (s->id != pl061_id_luminary) { goto bad_offset; } - return s->pur; + r = s->pur; + break; case 0x514: /* Pull-down */ if (s->id != pl061_id_luminary) { goto bad_offset; } - return s->pdr; + r = s->pdr; + break; case 0x518: /* Slew rate control */ if (s->id != pl061_id_luminary) { goto bad_offset; } - return s->slr; + r = s->slr; + break; case 0x51c: /* Digital enable */ if (s->id != pl061_id_luminary) { goto bad_offset; } - return s->den; + r = s->den; + break; case 0x520: /* Lock */ if (s->id != pl061_id_luminary) { goto bad_offset; } - return s->locked; + r = s->locked; + break; case 0x524: /* Commit */ if (s->id != pl061_id_luminary) { goto bad_offset; } - return s->cr; + r = s->cr; + break; case 0x528: /* Analog mode select */ if (s->id != pl061_id_luminary) { goto bad_offset; } - return s->amsel; + r = s->amsel; + break; case 0xfd0 ... 0xfff: /* ID registers */ - return s->id[(offset - 0xfd0) >> 2]; + r = s->id[(offset - 0xfd0) >> 2]; + break; default: bad_offset: qemu_log_mask(LOG_GUEST_ERROR, "pl061_read: Bad offset %x\n", (int)offset); break; } - return 0; + + trace_pl061_read(DEVICE(s)->canonical_path, offset, r); + return r; } static void pl061_write(void *opaque, hwaddr offset, @@ -241,6 +265,8 @@ static void pl061_write(void *opaque, hwaddr offset, PL061State *s = (PL061State *)opaque; uint8_t mask; + trace_pl061_write(DEVICE(s)->canonical_path, offset, value); + switch (offset) { case 0 ... 0x3ff: mask = (offset >> 2) & s->dir; diff --git a/hw/gpio/trace-events b/hw/gpio/trace-events index 48ccbb183c..442be9406f 100644 --- a/hw/gpio/trace-events +++ b/hw/gpio/trace-events @@ -18,6 +18,8 @@ pl061_update(const char *id, uint32_t dir, uint32_t data) "%s GPIODIR 0x%x GPIOD pl061_set_output(const char *id, int gpio, int level) "%s setting output %d to %d" pl061_input_change(const char *id, int gpio, int level) "%s input %d changed to %d" pl061_update_istate(const char *id, uint32_t istate, uint32_t im, int level) "%s GPIORIS 0x%x GPIOIE 0x%x interrupt level %d" +pl061_read(const char *id, uint64_t offset, uint64_t r) "%s offset 0x%" PRIx64 " value 0x%" PRIx64 +pl061_write(const char *id, uint64_t offset, uint64_t value) "%s offset 0x%" PRIx64 " value 0x%" PRIx64 # sifive_gpio.c sifive_gpio_read(uint64_t offset, uint64_t r) "offset 0x%" PRIx64 " value 0x%" PRIx64 From 455736df2cfd3a980782986d597132776d630823 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 2 Jul 2021 11:40:12 +0100 Subject: [PATCH 144/272] hw/gpio/pl061: Document the interface of this device Add a comment documenting the "QEMU interface" of this device: which MMIO regions, IRQ lines, GPIO lines, etc it exposes. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- hw/gpio/pl061.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c index 42f6e6c489..a3c1386221 100644 --- a/hw/gpio/pl061.c +++ b/hw/gpio/pl061.c @@ -6,6 +6,13 @@ * Written by Paul Brook * * This code is licensed under the GPL. + * + * QEMU interface: + * + sysbus MMIO region 0: the device registers + * + sysbus IRQ: the GPIOINTR interrupt line + * + unnamed GPIO inputs 0..7: inputs to connect to the emulated GPIO lines + * + unnamed GPIO outputs 0..7: the emulated GPIO lines, considered as + * outputs */ #include "qemu/osdep.h" From ad06d56fc7155c7893b18efecb9fe0f2e9124eaf Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 2 Jul 2021 11:40:13 +0100 Subject: [PATCH 145/272] hw/gpio/pl061: Honour Luminary PL061 PUR and PDR registers The Luminary variant of the PL061 has registers GPIOPUR and GPIOPDR which lets the guest configure whether the GPIO lines are pull-up, pull-down, or truly floating. Instead of assuming all lines are pulled high, honour the PUR and PDR registers. For the plain PL061, continue to assume that lines have an external pull-up resistor, as we did before. The stellaris board actually relies on this behaviour -- the CD line of the ssd0323 display device is connected to GPIO output C7, and it is only because of a different bug which we're about to fix that we weren't incorrectly driving this line high on reset and putting the ssd0323 into data mode. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- hw/gpio/pl061.c | 58 +++++++++++++++++++++++++++++++++++++++++--- hw/gpio/trace-events | 2 +- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c index a3c1386221..9360c143ee 100644 --- a/hw/gpio/pl061.c +++ b/hw/gpio/pl061.c @@ -94,18 +94,68 @@ static const VMStateDescription vmstate_pl061 = { } }; +static uint8_t pl061_floating(PL061State *s) +{ + /* + * Return mask of bits which correspond to pins configured as inputs + * and which are floating (neither pulled up to 1 nor down to 0). + */ + uint8_t floating; + + if (s->id == pl061_id_luminary) { + /* + * If both PUR and PDR bits are clear, there is neither a pullup + * nor a pulldown in place, and the output truly floats. + */ + floating = ~(s->pur | s->pdr); + } else { + /* Assume outputs are pulled high. FIXME: this is board dependent. */ + floating = 0; + } + return floating & ~s->dir; +} + +static uint8_t pl061_pullups(PL061State *s) +{ + /* + * Return mask of bits which correspond to pins configured as inputs + * and which are pulled up to 1. + */ + uint8_t pullups; + + if (s->id == pl061_id_luminary) { + /* + * The Luminary variant of the PL061 has an extra registers which + * the guest can use to configure whether lines should be pullup + * or pulldown. + */ + pullups = s->pur; + } else { + /* Assume outputs are pulled high. FIXME: this is board dependent. */ + pullups = 0xff; + } + return pullups & ~s->dir; +} + static void pl061_update(PL061State *s) { uint8_t changed; uint8_t mask; uint8_t out; int i; + uint8_t pullups = pl061_pullups(s); + uint8_t floating = pl061_floating(s); - trace_pl061_update(DEVICE(s)->canonical_path, s->dir, s->data); + trace_pl061_update(DEVICE(s)->canonical_path, s->dir, s->data, + pullups, floating); - /* Outputs float high. */ - /* FIXME: This is board dependent. */ - out = (s->data & s->dir) | ~s->dir; + /* + * Pins configured as output are driven from the data register; + * otherwise if they're pulled up they're 1, and if they're floating + * then we give them the same value they had previously, so we don't + * report any change to the other end. + */ + out = (s->data & s->dir) | pullups | (s->old_out_data & floating); changed = s->old_out_data ^ out; if (changed) { s->old_out_data = out; diff --git a/hw/gpio/trace-events b/hw/gpio/trace-events index 442be9406f..eb5fb4701c 100644 --- a/hw/gpio/trace-events +++ b/hw/gpio/trace-events @@ -14,7 +14,7 @@ nrf51_gpio_set(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64 nrf51_gpio_update_output_irq(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64 # pl061.c -pl061_update(const char *id, uint32_t dir, uint32_t data) "%s GPIODIR 0x%x GPIODATA 0x%x" +pl061_update(const char *id, uint32_t dir, uint32_t data, uint32_t pullups, uint32_t floating) "%s GPIODIR 0x%x GPIODATA 0x%x pullups 0x%x floating 0x%x" pl061_set_output(const char *id, int gpio, int level) "%s setting output %d to %d" pl061_input_change(const char *id, int gpio, int level) "%s input %d changed to %d" pl061_update_istate(const char *id, uint32_t istate, uint32_t im, int level) "%s GPIORIS 0x%x GPIOIE 0x%x interrupt level %d" From c1e69e92aea696fa148c4d79aff6a2fdf46ef2b8 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 2 Jul 2021 11:40:14 +0100 Subject: [PATCH 146/272] hw/gpio/pl061: Make pullup/pulldown of outputs configurable The PL061 GPIO does not itself include pullup or pulldown resistors to set the value of a GPIO line treated as an output when it is configured as an input (ie when the PL061 itself is not driving it). In real hardware it is up to the board to add suitable pullups or pulldowns. Currently our implementation hardwires this to "outputs pulled high", which is correct for some boards (eg the realview ones: see figure 3-29 in the "RealView Platform Baseboard for ARM926EJ-S User Guide" DUI0224I), but wrong for others. In particular, the wiring in the 'virt' board and the gpio-pwr device assumes that wires should be pulled low, because otherwise the pull-to-high will trigger a shutdown or reset action. (The only reason this doesn't happen immediately on startup is due to another bug in the PL061, where we don't assert the GPIOs to the correct value on reset, but will do so as soon as the guest touches a register and pl061_update() gets called.) Add properties to the pl061 so the board can configure whether it wants GPIO lines to have pullup, pulldown, or neither. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- hw/gpio/pl061.c | 51 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c index 9360c143ee..5ba398fcd4 100644 --- a/hw/gpio/pl061.c +++ b/hw/gpio/pl061.c @@ -13,12 +13,28 @@ * + unnamed GPIO inputs 0..7: inputs to connect to the emulated GPIO lines * + unnamed GPIO outputs 0..7: the emulated GPIO lines, considered as * outputs + * + QOM property "pullups": an integer defining whether non-floating lines + * configured as inputs should be pulled up to logical 1 (ie whether in + * real hardware they have a pullup resistor on the line out of the PL061). + * This should be an 8-bit value, where bit 0 is 1 if GPIO line 0 should + * be pulled high, bit 1 configures line 1, and so on. The default is 0xff, + * indicating that all GPIO lines are pulled up to logical 1. + * + QOM property "pulldowns": an integer defining whether non-floating lines + * configured as inputs should be pulled down to logical 0 (ie whether in + * real hardware they have a pulldown resistor on the line out of the PL061). + * This should be an 8-bit value, where bit 0 is 1 if GPIO line 0 should + * be pulled low, bit 1 configures line 1, and so on. The default is 0x0. + * It is an error to set a bit in both "pullups" and "pulldowns". If a bit + * is 0 in both, then the line is considered to be floating, and it will + * not have qemu_set_irq() called on it when it is configured as an input. */ #include "qemu/osdep.h" #include "hw/irq.h" #include "hw/sysbus.h" +#include "hw/qdev-properties.h" #include "migration/vmstate.h" +#include "qapi/error.h" #include "qemu/log.h" #include "qemu/module.h" #include "qom/object.h" @@ -62,6 +78,9 @@ struct PL061State { qemu_irq irq; qemu_irq out[N_GPIOS]; const unsigned char *id; + /* Properties, for non-Luminary PL061 */ + uint32_t pullups; + uint32_t pulldowns; }; static const VMStateDescription vmstate_pl061 = { @@ -109,8 +128,7 @@ static uint8_t pl061_floating(PL061State *s) */ floating = ~(s->pur | s->pdr); } else { - /* Assume outputs are pulled high. FIXME: this is board dependent. */ - floating = 0; + floating = ~(s->pullups | s->pulldowns); } return floating & ~s->dir; } @@ -131,8 +149,7 @@ static uint8_t pl061_pullups(PL061State *s) */ pullups = s->pur; } else { - /* Assume outputs are pulled high. FIXME: this is board dependent. */ - pullups = 0xff; + pullups = s->pullups; } return pullups & ~s->dir; } @@ -499,12 +516,38 @@ static void pl061_init(Object *obj) qdev_init_gpio_out(dev, s->out, N_GPIOS); } +static void pl061_realize(DeviceState *dev, Error **errp) +{ + PL061State *s = PL061(dev); + + if (s->pullups > 0xff) { + error_setg(errp, "pullups property must be between 0 and 0xff"); + return; + } + if (s->pulldowns > 0xff) { + error_setg(errp, "pulldowns property must be between 0 and 0xff"); + return; + } + if (s->pullups & s->pulldowns) { + error_setg(errp, "no bit may be set both in pullups and pulldowns"); + return; + } +} + +static Property pl061_props[] = { + DEFINE_PROP_UINT32("pullups", PL061State, pullups, 0xff), + DEFINE_PROP_UINT32("pulldowns", PL061State, pulldowns, 0x0), + DEFINE_PROP_END_OF_LIST() +}; + static void pl061_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_pl061; dc->reset = &pl061_reset; + dc->realize = pl061_realize; + device_class_set_props(dc, pl061_props); } static const TypeInfo pl061_info = { From d6773a1f996db5339cdc1e01f14ffb70ca9f4d28 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 2 Jul 2021 11:40:15 +0100 Subject: [PATCH 147/272] hw/arm/virt: Make PL061 GPIO lines pulled low, not high For the virt board we have two PL061 devices -- one for NonSecure which is inputs only, and one for Secure which is outputs only. For the former, we don't care whether its outputs are pulled low or high when the line is configured as an input, because we don't connect them. For the latter, we do care, because we wire the lines up to the gpio-pwr device, which assumes that level 1 means "do the action" and 1 means "do nothing". For consistency in case we add more outputs in future, configure both PL061s to pull GPIO lines down to 0. Reported-by: Maxim Uvarov Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- hw/arm/virt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 4b96f06014..93ab9d21ea 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -895,6 +895,9 @@ static void create_gpio_devices(const VirtMachineState *vms, int gpio, MachineState *ms = MACHINE(vms); pl061_dev = qdev_new("pl061"); + /* Pull lines down to 0 if not driven by the PL061 */ + qdev_prop_set_uint32(pl061_dev, "pullups", 0); + qdev_prop_set_uint32(pl061_dev, "pulldowns", 0xff); s = SYS_BUS_DEVICE(pl061_dev); sysbus_realize_and_unref(s, &error_fatal); memory_region_add_subregion(mem, base, sysbus_mmio_get_region(s, 0)); From ef4989b0a898ae20a974d261b14d4e5c1c097292 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 2 Jul 2021 11:40:16 +0100 Subject: [PATCH 148/272] hw/gpio/pl061: Convert to 3-phase reset and assert GPIO lines correctly on reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PL061 comes out of reset with all its lines configured as input, which means they might need to be pulled to 0 or 1 depending on the 'pullups' and 'pulldowns' properties. Currently we do not assert these lines on reset; they will only be set whenever the guest first touches a register that triggers a call to pl061_update(). Convert the device to three-phase reset so we have a place where we can safely call qemu_set_irq() to set the floating lines to their correct values. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson --- hw/gpio/pl061.c | 29 +++++++++++++++++++++++++---- hw/gpio/trace-events | 1 + 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c index 5ba398fcd4..4002ab5154 100644 --- a/hw/gpio/pl061.c +++ b/hw/gpio/pl061.c @@ -446,13 +446,14 @@ static void pl061_write(void *opaque, hwaddr offset, return; } -static void pl061_reset(DeviceState *dev) +static void pl061_enter_reset(Object *obj, ResetType type) { - PL061State *s = PL061(dev); + PL061State *s = PL061(obj); + + trace_pl061_reset(DEVICE(s)->canonical_path); /* reset values from PL061 TRM, Stellaris LM3S5P31 & LM3S8962 Data Sheet */ s->data = 0; - s->old_out_data = 0; s->old_in_data = 0; s->dir = 0; s->isense = 0; @@ -474,6 +475,24 @@ static void pl061_reset(DeviceState *dev) s->amsel = 0; } +static void pl061_hold_reset(Object *obj) +{ + PL061State *s = PL061(obj); + int i, level; + uint8_t floating = pl061_floating(s); + uint8_t pullups = pl061_pullups(s); + + for (i = 0; i < N_GPIOS; i++) { + if (extract32(floating, i, 1)) { + continue; + } + level = extract32(pullups, i, 1); + trace_pl061_set_output(DEVICE(s)->canonical_path, i, level); + qemu_set_irq(s->out[i], level); + } + s->old_out_data = pullups; +} + static void pl061_set_irq(void * opaque, int irq, int level) { PL061State *s = (PL061State *)opaque; @@ -543,11 +562,13 @@ static Property pl061_props[] = { static void pl061_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); dc->vmsd = &vmstate_pl061; - dc->reset = &pl061_reset; dc->realize = pl061_realize; device_class_set_props(dc, pl061_props); + rc->phases.enter = pl061_enter_reset; + rc->phases.hold = pl061_hold_reset; } static const TypeInfo pl061_info = { diff --git a/hw/gpio/trace-events b/hw/gpio/trace-events index eb5fb4701c..1dab99c560 100644 --- a/hw/gpio/trace-events +++ b/hw/gpio/trace-events @@ -20,6 +20,7 @@ pl061_input_change(const char *id, int gpio, int level) "%s input %d changed to pl061_update_istate(const char *id, uint32_t istate, uint32_t im, int level) "%s GPIORIS 0x%x GPIOIE 0x%x interrupt level %d" pl061_read(const char *id, uint64_t offset, uint64_t r) "%s offset 0x%" PRIx64 " value 0x%" PRIx64 pl061_write(const char *id, uint64_t offset, uint64_t value) "%s offset 0x%" PRIx64 " value 0x%" PRIx64 +pl061_reset(const char *id) "%s reset" # sifive_gpio.c sifive_gpio_read(uint64_t offset, uint64_t r) "offset 0x%" PRIx64 " value 0x%" PRIx64 From 0642e159d2351a8fd7d03f78b5d97010cd514561 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 2 Jul 2021 11:40:17 +0100 Subject: [PATCH 149/272] hw/gpio/pl061: Document a shortcoming in our implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Luminary PL061s in the Stellaris LM3S9695 don't all have the same reset value for GPIOPUR. We can get away with not letting the board configure the PUR reset value because we don't actually wire anything up to the lines which should reset to pull-up. Add a comment noting this omission. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé --- hw/gpio/pl061.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c index 4002ab5154..899be861cc 100644 --- a/hw/gpio/pl061.c +++ b/hw/gpio/pl061.c @@ -453,6 +453,15 @@ static void pl061_enter_reset(Object *obj, ResetType type) trace_pl061_reset(DEVICE(s)->canonical_path); /* reset values from PL061 TRM, Stellaris LM3S5P31 & LM3S8962 Data Sheet */ + + /* + * FIXME: For the LM3S6965, not all of the PL061 instances have the + * same reset values for GPIOPUR, GPIOAFSEL and GPIODEN, so in theory + * we should allow the board to configure these via properties. + * In practice, we don't wire anything up to the affected GPIO lines + * (PB7, PC0, PC1, PC2, PC3 -- they're used for JTAG), so we can + * get away with this inaccuracy. + */ s->data = 0; s->old_in_data = 0; s->dir = 0; From 5092e014f4dd6a0174e487741382053694527bc5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 2 Jul 2021 11:40:18 +0100 Subject: [PATCH 150/272] hw/arm/stellaris: Expand comment about handling of OLED chipselect The stellaris board doesn't emulate the handling of the OLED chipselect line correctly. Expand the comment describing this, including a sketch of the theoretical correct way to do it. Signed-off-by: Peter Maydell --- hw/arm/stellaris.c | 56 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 8b4dab9b79..ad48cf2605 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -1453,13 +1453,67 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) DeviceState *sddev; DeviceState *ssddev; - /* Some boards have both an OLED controller and SD card connected to + /* + * Some boards have both an OLED controller and SD card connected to * the same SSI port, with the SD card chip select connected to a * GPIO pin. Technically the OLED chip select is connected to the * SSI Fss pin. We do not bother emulating that as both devices * should never be selected simultaneously, and our OLED controller * ignores stray 0xff commands that occur when deselecting the SD * card. + * + * The h/w wiring is: + * - GPIO pin D0 is wired to the active-low SD card chip select + * - GPIO pin A3 is wired to the active-low OLED chip select + * - The SoC wiring of the PL061 "auxiliary function" for A3 is + * SSI0Fss ("frame signal"), which is an output from the SoC's + * SSI controller. The SSI controller takes SSI0Fss low when it + * transmits a frame, so it can work as a chip-select signal. + * - GPIO A4 is aux-function SSI0Rx, and wired to the SD card Tx + * (the OLED never sends data to the CPU, so no wiring needed) + * - GPIO A5 is aux-function SSI0Tx, and wired to the SD card Rx + * and the OLED display-data-in + * - GPIO A2 is aux-function SSI0Clk, wired to SD card and OLED + * serial-clock input + * So a guest that wants to use the OLED can configure the PL061 + * to make pins A2, A3, A5 aux-function, so they are connected + * directly to the SSI controller. When the SSI controller sends + * data it asserts SSI0Fss which selects the OLED. + * A guest that wants to use the SD card configures A2, A4 and A5 + * as aux-function, but leaves A3 as a software-controlled GPIO + * line. It asserts the SD card chip-select by using the PL061 + * to control pin D0, and lets the SSI controller handle Clk, Tx + * and Rx. (The SSI controller asserts Fss during tx cycles as + * usual, but because A3 is not set to aux-function this is not + * forwarded to the OLED, and so the OLED stays unselected.) + * + * The QEMU implementation instead is: + * - GPIO pin D0 is wired to the active-low SD card chip select, + * and also to the OLED chip-select which is implemented + * as *active-high* + * - SSI controller signals go to the devices regardless of + * whether the guest programs A2, A4, A5 as aux-function or not + * + * The problem with this implementation is if the guest doesn't + * care about the SD card and only uses the OLED. In that case it + * may choose never to do anything with D0 (leaving it in its + * default floating state, which reliably leaves the card disabled + * because an SD card has a pullup on CS within the card itself), + * and only set up A2, A3, A5. This for us would mean the OLED + * never gets the chip-select assert it needs. We work around + * this with a manual raise of D0 here (despite board creation + * code being the wrong place to raise IRQ lines) to put the OLED + * into an initially selected state. + * + * In theory the right way to model this would be: + * - Implement aux-function support in the PL061, with an + * extra set of AFIN and AFOUT GPIO lines (set up so that + * if a GPIO line is in auxfn mode the main GPIO in and out + * track the AFIN and AFOUT lines) + * - Wire the AFOUT for D0 up to either a line from the + * SSI controller that's pulled low around every transmit, + * or at least to an always-0 line here on the board + * - Make the ssd0323 OLED controller chipselect active-low */ bus = qdev_get_child_bus(dev, "ssi"); From 49a6f3bffbdfada7d5e9ba6e272713eba19dbf12 Mon Sep 17 00:00:00 2001 From: "hnick@vmware.com" Date: Tue, 6 Jul 2021 14:44:57 +0100 Subject: [PATCH 151/272] target/arm: Correct the encoding of MDCCSR_EL0 and DBGDSCRint Signed-off-by: Nick Hudson Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/helper.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index a66c1f0b9e..910ace4274 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6326,11 +6326,21 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { .access = PL1_RW, .accessfn = access_tda, .fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), .resetvalue = 0 }, - /* MDCCSR_EL0, aka DBGDSCRint. This is a read-only mirror of MDSCR_EL1. + /* + * MDCCSR_EL0[30:29] map to EDSCR[30:29]. Simply RAZ as the external + * Debug Communication Channel is not implemented. + */ + { .name = "MDCCSR_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 2, .opc1 = 3, .crn = 0, .crm = 1, .opc2 = 0, + .access = PL0_R, .accessfn = access_tda, + .type = ARM_CP_CONST, .resetvalue = 0 }, + /* + * DBGDSCRint[15,12,5:2] map to MDSCR_EL1[15,12,5:2]. Map all bits as + * it is unlikely a guest will care. * We don't implement the configurable EL0 access. */ - { .name = "MDCCSR_EL0", .state = ARM_CP_STATE_BOTH, - .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 0, + { .name = "DBGDSCRint", .state = ARM_CP_STATE_AA32, + .cp = 14, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 0, .type = ARM_CP_ALIAS, .access = PL1_R, .accessfn = access_tda, .fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), }, From 05449abb1d4c5f0c69ceb3d8d03cbc75de39b646 Mon Sep 17 00:00:00 2001 From: Rebecca Cran Date: Tue, 6 Jul 2021 15:14:32 -0600 Subject: [PATCH 152/272] hw/intc: Improve formatting of MEMTX_ERROR guest error message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a space in the message printed when gicr_read*/gicr_write* returns MEMTX_ERROR in arm_gicv3_redist.c. Signed-off-by: Rebecca Cran Reviewed-by: Philippe Mathieu-Daudé Message-id: 20210706211432.31902-1-rebecca@nuviainc.com Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_redist.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c index 8645220d61..53da703ed8 100644 --- a/hw/intc/arm_gicv3_redist.c +++ b/hw/intc/arm_gicv3_redist.c @@ -453,7 +453,7 @@ MemTxResult gicv3_redist_read(void *opaque, hwaddr offset, uint64_t *data, if (r == MEMTX_ERROR) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid guest read at offset " TARGET_FMT_plx - "size %u\n", __func__, offset, size); + " size %u\n", __func__, offset, size); trace_gicv3_redist_badread(gicv3_redist_affid(cs), offset, size, attrs.secure); /* The spec requires that reserved registers are RAZ/WI; @@ -510,7 +510,7 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data, if (r == MEMTX_ERROR) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid guest write at offset " TARGET_FMT_plx - "size %u\n", __func__, offset, size); + " size %u\n", __func__, offset, size); trace_gicv3_redist_badwrite(gicv3_redist_affid(cs), offset, data, size, attrs.secure); /* The spec requires that reserved registers are RAZ/WI; From 12033e16e94538b2b37f65f41cbd86f78cda1cac Mon Sep 17 00:00:00 2001 From: Miroslav Rezanina Date: Wed, 7 Jul 2021 02:31:24 -0400 Subject: [PATCH 153/272] configure: fix libdaxctl options For some reason, libdaxctl option setting was set to work in an opposite way (--enable-libdaxctl disabled it and vice versa). Fixing this so configuration works properly. Signed-off-by: Miroslav Rezanina Reviewed-by: Thomas Huth Message-Id: <20210707063124.81954-1-mrezanin@redhat.com> Fixes: 83ef16821a ("configure, meson: convert libdaxctl detection to meson", 2021-07-06) Signed-off-by: Paolo Bonzini --- configure | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure b/configure index 650d9c0735..4f51528a77 100755 --- a/configure +++ b/configure @@ -1531,9 +1531,9 @@ for opt do ;; --disable-keyring) secret_keyring="no" ;; - --enable-libdaxctl) libdaxctl=disabled + --enable-libdaxctl) libdaxctl="enabled" ;; - --disable-libdaxctl) libdaxctl=enabled + --disable-libdaxctl) libdaxctl="disabled" ;; --enable-fuse) fuse="enabled" ;; From 5cd5d8a71a70f2291f688c3851de4f438e5cd0f8 Mon Sep 17 00:00:00 2001 From: Miroslav Rezanina Date: Wed, 7 Jul 2021 03:51:44 -0400 Subject: [PATCH 154/272] configure: fix libpmem configuration option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For some reason, libpmem option setting was set to work in an opposite way (--enable-libpmem disabled it and vice versa). Fixing this so configuration works properly. Signed-off-by: Miroslav Rezanina Reviewed-by: Connor Kuehl Reviewed-by: Pankaj Gupta Reviewed-by: Philippe Mathieu-Daudé Tested-by: Connor Kuehl Message-Id: <20210707075144.82717-1-mrezanin@redhat.com> Fixes: e36e8c70f6 ("configure, meson: convert libpmem detection to meson", 2021-07-06) Signed-off-by: Paolo Bonzini --- configure | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure b/configure index 4f51528a77..a172c83e15 100755 --- a/configure +++ b/configure @@ -1501,9 +1501,9 @@ for opt do ;; --disable-debug-mutex) debug_mutex=no ;; - --enable-libpmem) libpmem=disabled + --enable-libpmem) libpmem="enabled" ;; - --disable-libpmem) libpmem=enabled + --disable-libpmem) libpmem="disabled" ;; --enable-xkbcommon) xkbcommon="enabled" ;; From 63a7f853063133fd1aa34ab0744b009fa3d7e183 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 8 Jul 2021 13:50:06 +0200 Subject: [PATCH 155/272] meson: fix missing preprocessor symbols While most libraries do not need a CONFIG_* symbol because the "when:" clauses are enough, some do. Add them back or stop using them if possible. In the case of libpmem, the statement to add the CONFIG_* symbol was still in configure, but could not be triggered because it checked for "no" instead of "disabled" (and it would be wrong anyway since the test for the library has not been done yet). Reported-by: Li Zhijian Fixes: 587d59d6cc ("configure, meson: convert virgl detection to meson", 2021-07-06) Fixes: 83ef16821a ("configure, meson: convert libdaxctl detection to meson", 2021-07-06) Fixes: e36e8c70f6 ("configure, meson: convert libpmem detection to meson", 2021-07-06) Fixes: 53c22b68e3 ("configure, meson: convert liburing detection to meson", 2021-07-06) Signed-off-by: Paolo Bonzini --- block/meson.build | 2 +- configure | 6 ------ contrib/vhost-user-gpu/meson.build | 2 +- meson.build | 4 ++++ util/meson.build | 2 +- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/block/meson.build b/block/meson.build index ef1ba3d973..0450914c7a 100644 --- a/block/meson.build +++ b/block/meson.build @@ -66,7 +66,7 @@ block_ss.add(when: libiscsi, if_true: files('iscsi-opts.c')) block_ss.add(when: 'CONFIG_LINUX', if_true: files('nvme.c')) block_ss.add(when: 'CONFIG_REPLICATION', if_true: files('replication.c')) block_ss.add(when: ['CONFIG_LINUX_AIO', libaio], if_true: files('linux-aio.c')) -block_ss.add(when: ['CONFIG_LINUX_IO_URING', linux_io_uring], if_true: files('io_uring.c')) +block_ss.add(when: linux_io_uring, if_true: files('io_uring.c')) block_modules = {} diff --git a/configure b/configure index a172c83e15..a04b1e075c 100755 --- a/configure +++ b/configure @@ -4818,12 +4818,6 @@ elif test "$pthread_setname_np_wo_tid" = "yes" ; then echo "CONFIG_PTHREAD_SETNAME_NP_WO_TID=y" >> $config_host_mak fi -if test "$libpmem" = "yes" ; then - echo "CONFIG_LIBPMEM=y" >> $config_host_mak - echo "LIBPMEM_LIBS=$libpmem_libs" >> $config_host_mak - echo "LIBPMEM_CFLAGS=$libpmem_cflags" >> $config_host_mak -fi - if test "$bochs" = "yes" ; then echo "CONFIG_BOCHS=y" >> $config_host_mak fi diff --git a/contrib/vhost-user-gpu/meson.build b/contrib/vhost-user-gpu/meson.build index 0ce1515a10..4cb52a91d7 100644 --- a/contrib/vhost-user-gpu/meson.build +++ b/contrib/vhost-user-gpu/meson.build @@ -1,4 +1,4 @@ -if 'CONFIG_TOOLS' in config_host and 'CONFIG_VIRGL' in config_host \ +if 'CONFIG_TOOLS' in config_host and virgl.found() \ and 'CONFIG_GBM' in config_host and 'CONFIG_LINUX' in config_host \ and pixman.found() executable('vhost-user-gpu', files('vhost-user-gpu.c', 'virgl.c', 'vugbm.c'), diff --git a/meson.build b/meson.build index 7e12de01be..9cd966a86b 100644 --- a/meson.build +++ b/meson.build @@ -1223,8 +1223,11 @@ config_host_data.set('CONFIG_VTE', vte.found()) config_host_data.set('CONFIG_LIBATTR', have_old_libattr) config_host_data.set('CONFIG_LIBCAP_NG', libcap_ng.found()) config_host_data.set('CONFIG_EBPF', libbpf.found()) +config_host_data.set('CONFIG_LIBDAXCTL', libdaxctl.found()) config_host_data.set('CONFIG_LIBISCSI', libiscsi.found()) config_host_data.set('CONFIG_LIBNFS', libnfs.found()) +config_host_data.set('CONFIG_LINUX_IO_URING', linux_io_uring.found()) +config_host_data.set('CONFIG_LIBPMEM', libpmem.found()) config_host_data.set('CONFIG_RBD', rbd.found()) config_host_data.set('CONFIG_SDL', sdl.found()) config_host_data.set('CONFIG_SDL_IMAGE', sdl_image.found()) @@ -1237,6 +1240,7 @@ config_host_data.set('CONFIG_VNC_JPEG', jpeg.found()) config_host_data.set('CONFIG_VNC_PNG', png.found()) config_host_data.set('CONFIG_VNC_SASL', sasl.found()) config_host_data.set('CONFIG_VIRTFS', have_virtfs) +config_host_data.set('CONFIG_VTE', vte.found()) config_host_data.set('CONFIG_XKBCOMMON', xkbcommon.found()) config_host_data.set('CONFIG_KEYUTILS', keyutils.found()) config_host_data.set('CONFIG_GETTID', has_gettid) diff --git a/util/meson.build b/util/meson.build index 0ffd7f4bde..779f413c86 100644 --- a/util/meson.build +++ b/util/meson.build @@ -5,7 +5,7 @@ util_ss.add(when: 'CONFIG_POSIX', if_true: files('fdmon-poll.c')) if config_host_data.get('CONFIG_EPOLL_CREATE1') util_ss.add(files('fdmon-epoll.c')) endif -util_ss.add(when: ['CONFIG_LINUX_IO_URING', linux_io_uring], if_true: files('fdmon-io_uring.c')) +util_ss.add(when: linux_io_uring, if_true: files('fdmon-io_uring.c')) util_ss.add(when: 'CONFIG_POSIX', if_true: files('compatfd.c')) util_ss.add(when: 'CONFIG_POSIX', if_true: files('event_notifier-posix.c')) util_ss.add(when: 'CONFIG_POSIX', if_true: files('mmap-alloc.c')) From 7db492a1b65699ee6384874844cb87ff7200a811 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 7 Jul 2021 12:48:17 +0200 Subject: [PATCH 156/272] osdep: fix HAVE_BROKEN_SIZE_MAX case While config-host.mak entries are expanded to "1" for compatibility with create-config.sh, tests done directly in meson.build expand to the empty string and cannot be placed to the right of the && operator. Adjust osdep.h after commit e46bd55d9c ("configure: convert HAVE_BROKEN_SIZE_MAX to meson", 2021-07-06) changed the way HAVE_BROKEN_SIZE_MAX is defined. Reported-by: Frederic Bezies Fixes: e46bd55d9c ("configure: convert HAVE_BROKEN_SIZE_MAX to meson", 2021-07-06) Resolves: #463 Signed-off-by: Paolo Bonzini --- include/qemu/osdep.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index c91a78b5e6..60718fc342 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -256,7 +256,7 @@ extern "C" { /* Mac OSX has a bug that incorrectly defines SIZE_MAX with * the wrong type. Our replacement isn't usable in preprocessor * expressions, but it is sufficient for our needs. */ -#if defined(HAVE_BROKEN_SIZE_MAX) && HAVE_BROKEN_SIZE_MAX +#ifdef HAVE_BROKEN_SIZE_MAX #undef SIZE_MAX #define SIZE_MAX ((size_t)-1) #endif From 22524c10c489ed7c20be2f5878157a64095e5734 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:03 +0200 Subject: [PATCH 157/272] modules: add modinfo macros Add macros for module info annotations. Instead of having that module meta-data stored in lists in util/module.c place directly in the module source code. Signed-off-by: Gerd Hoffmann Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-2-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- include/qemu/module.h | 61 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/include/qemu/module.h b/include/qemu/module.h index 944d403cbd..b595f15975 100644 --- a/include/qemu/module.h +++ b/include/qemu/module.h @@ -73,4 +73,65 @@ bool module_load_one(const char *prefix, const char *lib_name, bool mayfail); void module_load_qom_one(const char *type); void module_load_qom_all(void); +/** + * DOC: module info annotation macros + * + * `scripts/modinfo-collect.py` will collect module info, + * using the preprocessor and -DQEMU_MODINFO. + * + * `scripts/modinfo-generate.py` will create a module meta-data database + * from the collected information so qemu knows about module + * dependencies and QOM objects implemented by modules. + * + * See `*.modinfo` and `modinfo.c` in the build directory to check the + * script results. + */ +#ifdef QEMU_MODINFO +# define modinfo(kind, value) \ + MODINFO_START kind value MODINFO_END +#else +# define modinfo(kind, value) +#endif + +/** + * module_obj + * + * @name: QOM type. + * + * This module implements QOM type @name. + */ +#define module_obj(name) modinfo(obj, name) + +/** + * module_dep + * + * @name: module name + * + * This module depends on module @name. + */ +#define module_dep(name) modinfo(dep, name) + +/** + * module_arch + * + * @name: target architecture + * + * This module is for target architecture @arch. + * + * Note that target-dependent modules are tagged automatically, so + * this is only needed in case target-independent modules should be + * restricted. Use case example: the ccw bus is implemented by s390x + * only. + */ +#define module_arch(name) modinfo(arch, name) + +/** + * module_opts + * + * @name: QemuOpts name + * + * This module registers QemuOpts @name. + */ +#define module_opts(name) modinfo(opts, name) + #endif From f5723ab66560a10f8461ac223e3d8369c10dc964 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:04 +0200 Subject: [PATCH 158/272] modules: collect module meta-data Add script to collect the module meta-data from the source code, store the results in *.modinfo files. Signed-off-by: Gerd Hoffmann Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-3-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- meson.build | 16 +++++++++ scripts/modinfo-collect.py | 67 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100755 scripts/modinfo-collect.py diff --git a/meson.build b/meson.build index 9cd966a86b..ff580f1f80 100644 --- a/meson.build +++ b/meson.build @@ -2241,6 +2241,9 @@ subdir('tests/qtest/fuzz') # Library dependencies # ######################## +modinfo_collect = find_program('scripts/modinfo-collect.py') +modinfo_files = [] + block_mods = [] softmmu_mods = [] foreach d, list : modules @@ -2254,6 +2257,19 @@ foreach d, list : modules else softmmu_mods += sl endif + if module_ss.sources() != [] + # FIXME: Should use sl.extract_all_objects(recursive: true) as + # input. Sources can be used multiple times but objects are + # unique when it comes to lookup in compile_commands.json. + # Depnds on a mesion version with + # https://github.com/mesonbuild/meson/pull/8900 + modinfo_files += custom_target(d + '-' + m + '.modinfo', + output: d + '-' + m + '.modinfo', + input: module_ss.sources(), + capture: true, + build_by_default: true, # to be removed when added to a target + command: [modinfo_collect, '@INPUT@']) + endif else if d == 'block' block_ss.add_all(module_ss) diff --git a/scripts/modinfo-collect.py b/scripts/modinfo-collect.py new file mode 100755 index 0000000000..4acb188c3e --- /dev/null +++ b/scripts/modinfo-collect.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import os +import sys +import json +import shlex +import subprocess + +def find_command(src, target, compile_commands): + for command in compile_commands: + if command['file'] != src: + continue + if target != '' and command['command'].find(target) == -1: + continue + return command['command'] + return 'false' + +def process_command(src, command): + skip = False + arg = False + out = [] + for item in shlex.split(command): + if arg: + out.append(x) + arg = False + continue + if skip: + skip = False + continue + if item == '-MF' or item == '-MQ' or item == '-o': + skip = True + continue + if item == '-c': + skip = True + continue + out.append(item) + out.append('-DQEMU_MODINFO') + out.append('-E') + out.append(src) + return out + +def main(args): + target = '' + if args[0] == '--target': + args.pop(0) + target = args.pop(0) + print("MODINFO_DEBUG target %s" % target) + arch = target[:-8] # cut '-softmmu' + print("MODINFO_START arch \"%s\" MODINFO_END" % arch) + with open('compile_commands.json') as f: + compile_commands = json.load(f) + for src in args: + print("MODINFO_DEBUG src %s" % src) + command = find_command(src, target, compile_commands) + cmdline = process_command(src, command) + print("MODINFO_DEBUG cmd", cmdline) + result = subprocess.run(cmdline, stdout = subprocess.PIPE, + universal_newlines = True) + if result.returncode != 0: + sys.exit(result.returncode) + for line in result.stdout.split('\n'): + if line.find('MODINFO') != -1: + print(line) + +if __name__ == "__main__": + main(sys.argv[1:]) From 5ebbfecc3e6fa443a506ec5fe65f0ca98973d404 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:05 +0200 Subject: [PATCH 159/272] modules: generate modinfo.c Add script to generate C source with a small database containing the module meta-data. Signed-off-by: Gerd Hoffmann Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-4-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- include/qemu/module.h | 17 ++++++++ meson.build | 13 +++++- scripts/modinfo-generate.py | 84 +++++++++++++++++++++++++++++++++++++ softmmu/vl.c | 4 ++ util/module.c | 11 +++++ 5 files changed, 128 insertions(+), 1 deletion(-) create mode 100755 scripts/modinfo-generate.py diff --git a/include/qemu/module.h b/include/qemu/module.h index b595f15975..9cd305de59 100644 --- a/include/qemu/module.h +++ b/include/qemu/module.h @@ -134,4 +134,21 @@ void module_load_qom_all(void); */ #define module_opts(name) modinfo(opts, name) +/* + * module info database + * + * scripts/modinfo-generate.c will build this using the data collected + * by scripts/modinfo-collect.py + */ +typedef struct QemuModinfo QemuModinfo; +struct QemuModinfo { + const char *name; + const char *arch; + const char **objs; + const char **deps; + const char **opts; +}; +extern const QemuModinfo qemu_modinfo[]; +void module_init_info(const QemuModinfo *info); + #endif diff --git a/meson.build b/meson.build index ff580f1f80..7b827f7caa 100644 --- a/meson.build +++ b/meson.build @@ -2242,6 +2242,7 @@ subdir('tests/qtest/fuzz') ######################## modinfo_collect = find_program('scripts/modinfo-collect.py') +modinfo_generate = find_program('scripts/modinfo-generate.py') modinfo_files = [] block_mods = [] @@ -2267,7 +2268,6 @@ foreach d, list : modules output: d + '-' + m + '.modinfo', input: module_ss.sources(), capture: true, - build_by_default: true, # to be removed when added to a target command: [modinfo_collect, '@INPUT@']) endif else @@ -2280,6 +2280,17 @@ foreach d, list : modules endforeach endforeach +if enable_modules + modinfo_src = custom_target('modinfo.c', + output: 'modinfo.c', + input: modinfo_files, + command: [modinfo_generate, '@INPUT@'], + capture: true) + modinfo_lib = static_library('modinfo', modinfo_src) + modinfo_dep = declare_dependency(link_whole: modinfo_lib) + softmmu_ss.add(modinfo_dep) +endif + nm = find_program('nm') undefsym = find_program('scripts/undefsym.py') block_syms = custom_target('block.syms', output: 'block.syms', diff --git a/scripts/modinfo-generate.py b/scripts/modinfo-generate.py new file mode 100755 index 0000000000..a6d98a6bc4 --- /dev/null +++ b/scripts/modinfo-generate.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import os +import sys + +def print_array(name, values): + if len(values) == 0: + return + list = ", ".join(values) + print(" .%s = ((const char*[]){ %s, NULL })," % (name, list)) + +def parse_line(line): + kind = "" + data = "" + get_kind = False + get_data = False + for item in line.split(): + if item == "MODINFO_START": + get_kind = True + continue + if item.startswith("MODINFO_END"): + get_data = False + continue + if get_kind: + kind = item + get_kind = False + get_data = True + continue + if get_data: + data += " " + item + continue + return (kind, data) + +def generate(name, lines): + arch = "" + objs = [] + deps = [] + opts = [] + for line in lines: + if line.find("MODINFO_START") != -1: + (kind, data) = parse_line(line) + if kind == 'obj': + objs.append(data) + elif kind == 'dep': + deps.append(data) + elif kind == 'opts': + opts.append(data) + elif kind == 'arch': + arch = data; + else: + print("unknown:", kind) + exit(1) + + print(" .name = \"%s\"," % name) + if arch != "": + print(" .arch = %s," % arch) + print_array("objs", objs) + print_array("deps", deps) + print_array("opts", opts) + print("},{"); + +def print_pre(): + print("/* generated by scripts/modinfo-generate.py */") + print("#include \"qemu/osdep.h\"") + print("#include \"qemu/module.h\"") + print("const QemuModinfo qemu_modinfo[] = {{") + +def print_post(): + print(" /* end of list */") + print("}};") + +def main(args): + print_pre() + for modinfo in args: + with open(modinfo) as f: + lines = f.readlines() + print(" /* %s */" % modinfo) + (basename, ext) = os.path.splitext(modinfo) + generate(basename, lines) + print_post() + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/softmmu/vl.c b/softmmu/vl.c index a50c8575a1..7cef3423a7 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -2755,6 +2755,10 @@ void qemu_init(int argc, char **argv, char **envp) error_init(argv[0]); qemu_init_exec_dir(argv[0]); +#ifdef CONFIG_MODULES + module_init_info(qemu_modinfo); +#endif + qemu_init_subsystems(); /* first pass of option parsing */ diff --git a/util/module.c b/util/module.c index eee8ff2de1..8d3e8275b9 100644 --- a/util/module.c +++ b/util/module.c @@ -110,6 +110,17 @@ void module_call_init(module_init_type type) } #ifdef CONFIG_MODULES + +static const QemuModinfo module_info_stub[] = { { + /* end of list */ +} }; +static const QemuModinfo *module_info = module_info_stub; + +void module_init_info(const QemuModinfo *info) +{ + module_info = info; +} + static int module_load_file(const char *fname, bool mayfail, bool export_symbols) { GModule *g_module; From af19eecf84e823645a9fb768360c92dc3fa59e65 Mon Sep 17 00:00:00 2001 From: "Jose R. Ziviani" Date: Thu, 24 Jun 2021 12:38:06 +0200 Subject: [PATCH 160/272] modules: check if all dependencies can be satisfied Verifies if all dependencies are correctly listed in the modinfo.c too and stop the builds if they're not. Signed-off-by: Jose R. Ziviani Signed-off-by: Gerd Hoffmann Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-5-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- scripts/modinfo-generate.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/scripts/modinfo-generate.py b/scripts/modinfo-generate.py index a6d98a6bc4..f559eed007 100755 --- a/scripts/modinfo-generate.py +++ b/scripts/modinfo-generate.py @@ -59,6 +59,7 @@ def generate(name, lines): print_array("deps", deps) print_array("opts", opts) print("},{"); + return deps def print_pre(): print("/* generated by scripts/modinfo-generate.py */") @@ -71,14 +72,26 @@ def print_post(): print("}};") def main(args): + deps = {} print_pre() for modinfo in args: with open(modinfo) as f: lines = f.readlines() print(" /* %s */" % modinfo) (basename, ext) = os.path.splitext(modinfo) - generate(basename, lines) + deps[basename] = generate(basename, lines) print_post() + flattened_deps = {flat.strip('" ') for dep in deps.values() for flat in dep} + error = False + for dep in flattened_deps: + if dep not in deps.keys(): + print("Dependency {} cannot be satisfied".format(dep), + file=sys.stderr) + error = True + + if error: + exit(1) + if __name__ == "__main__": main(sys.argv[1:]) From ec604e0a8a5ce1fd0a6c051084e499f1c21554c3 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:07 +0200 Subject: [PATCH 161/272] modules: add qxl module annotations Signed-off-by: Gerd Hoffmann Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-6-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- hw/display/qxl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 6e1f8ff1b2..84f99088e0 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -2522,6 +2522,7 @@ static const TypeInfo qxl_primary_info = { .parent = TYPE_PCI_QXL, .class_init = qxl_primary_class_init, }; +module_obj("qxl-vga"); static void qxl_secondary_class_init(ObjectClass *klass, void *data) { @@ -2538,6 +2539,7 @@ static const TypeInfo qxl_secondary_info = { .parent = TYPE_PCI_QXL, .class_init = qxl_secondary_class_init, }; +module_obj("qxl"); static void qxl_register_types(void) { @@ -2547,3 +2549,5 @@ static void qxl_register_types(void) } type_init(qxl_register_types) + +module_dep("ui-spice-core"); From 561d0f456824e7dd38f25acf14014975e740e130 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:08 +0200 Subject: [PATCH 162/272] modules: add virtio-gpu module annotations Signed-off-by: Gerd Hoffmann Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-7-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- hw/display/vhost-user-gpu-pci.c | 1 + hw/display/vhost-user-gpu.c | 1 + hw/display/vhost-user-vga.c | 1 + hw/display/virtio-gpu-base.c | 1 + hw/display/virtio-gpu-gl.c | 3 +++ hw/display/virtio-gpu-pci-gl.c | 3 +++ hw/display/virtio-gpu-pci.c | 2 ++ hw/display/virtio-gpu.c | 1 + hw/display/virtio-vga-gl.c | 3 +++ hw/display/virtio-vga.c | 2 ++ 10 files changed, 18 insertions(+) diff --git a/hw/display/vhost-user-gpu-pci.c b/hw/display/vhost-user-gpu-pci.c index a02b23ecaf..daefcf7101 100644 --- a/hw/display/vhost-user-gpu-pci.c +++ b/hw/display/vhost-user-gpu-pci.c @@ -43,6 +43,7 @@ static const VirtioPCIDeviceTypeInfo vhost_user_gpu_pci_info = { .instance_size = sizeof(VhostUserGPUPCI), .instance_init = vhost_user_gpu_pci_initfn, }; +module_obj(TYPE_VHOST_USER_GPU_PCI); static void vhost_user_gpu_pci_register_types(void) { diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c index 389199e6ca..49df56cd14 100644 --- a/hw/display/vhost-user-gpu.c +++ b/hw/display/vhost-user-gpu.c @@ -598,6 +598,7 @@ static const TypeInfo vhost_user_gpu_info = { .instance_finalize = vhost_user_gpu_instance_finalize, .class_init = vhost_user_gpu_class_init, }; +module_obj(TYPE_VHOST_USER_GPU); static void vhost_user_gpu_register_types(void) { diff --git a/hw/display/vhost-user-vga.c b/hw/display/vhost-user-vga.c index a34a99856d..072c9c65bc 100644 --- a/hw/display/vhost-user-vga.c +++ b/hw/display/vhost-user-vga.c @@ -44,6 +44,7 @@ static const VirtioPCIDeviceTypeInfo vhost_user_vga_info = { .instance_size = sizeof(VhostUserVGA), .instance_init = vhost_user_vga_inst_initfn, }; +module_obj(TYPE_VHOST_USER_VGA); static void vhost_user_vga_register_types(void) { diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c index dd294276cb..c8da4806e0 100644 --- a/hw/display/virtio-gpu-base.c +++ b/hw/display/virtio-gpu-base.c @@ -256,6 +256,7 @@ static const TypeInfo virtio_gpu_base_info = { .class_init = virtio_gpu_base_class_init, .abstract = true }; +module_obj(TYPE_VIRTIO_GPU_BASE); static void virtio_register_types(void) diff --git a/hw/display/virtio-gpu-gl.c b/hw/display/virtio-gpu-gl.c index d971b48080..7ab93bf8c8 100644 --- a/hw/display/virtio-gpu-gl.c +++ b/hw/display/virtio-gpu-gl.c @@ -154,6 +154,7 @@ static const TypeInfo virtio_gpu_gl_info = { .instance_size = sizeof(VirtIOGPUGL), .class_init = virtio_gpu_gl_class_init, }; +module_obj(TYPE_VIRTIO_GPU_GL); static void virtio_register_types(void) { @@ -161,3 +162,5 @@ static void virtio_register_types(void) } type_init(virtio_register_types) + +module_dep("hw-display-virtio-gpu"); diff --git a/hw/display/virtio-gpu-pci-gl.c b/hw/display/virtio-gpu-pci-gl.c index 902dda3452..99b14a0718 100644 --- a/hw/display/virtio-gpu-pci-gl.c +++ b/hw/display/virtio-gpu-pci-gl.c @@ -46,6 +46,7 @@ static const VirtioPCIDeviceTypeInfo virtio_gpu_gl_pci_info = { .instance_size = sizeof(VirtIOGPUGLPCI), .instance_init = virtio_gpu_gl_initfn, }; +module_obj(TYPE_VIRTIO_GPU_GL_PCI); static void virtio_gpu_gl_pci_register_types(void) { @@ -53,3 +54,5 @@ static void virtio_gpu_gl_pci_register_types(void) } type_init(virtio_gpu_gl_pci_register_types) + +module_dep("hw-display-virtio-gpu-pci"); diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c index d742a30aec..e36eee0c40 100644 --- a/hw/display/virtio-gpu-pci.c +++ b/hw/display/virtio-gpu-pci.c @@ -64,6 +64,7 @@ static const TypeInfo virtio_gpu_pci_base_info = { .class_init = virtio_gpu_pci_base_class_init, .abstract = true }; +module_obj(TYPE_VIRTIO_GPU_PCI_BASE); #define TYPE_VIRTIO_GPU_PCI "virtio-gpu-pci" typedef struct VirtIOGPUPCI VirtIOGPUPCI; @@ -90,6 +91,7 @@ static const VirtioPCIDeviceTypeInfo virtio_gpu_pci_info = { .instance_size = sizeof(VirtIOGPUPCI), .instance_init = virtio_gpu_initfn, }; +module_obj(TYPE_VIRTIO_GPU_PCI); static void virtio_gpu_pci_register_types(void) { diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index e183f4ecda..6b7f643951 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -1427,6 +1427,7 @@ static const TypeInfo virtio_gpu_info = { .class_size = sizeof(VirtIOGPUClass), .class_init = virtio_gpu_class_init, }; +module_obj(TYPE_VIRTIO_GPU); static void virtio_register_types(void) { diff --git a/hw/display/virtio-vga-gl.c b/hw/display/virtio-vga-gl.c index c971340ebb..f22549097c 100644 --- a/hw/display/virtio-vga-gl.c +++ b/hw/display/virtio-vga-gl.c @@ -36,6 +36,7 @@ static VirtioPCIDeviceTypeInfo virtio_vga_gl_info = { .instance_size = sizeof(VirtIOVGAGL), .instance_init = virtio_vga_gl_inst_initfn, }; +module_obj(TYPE_VIRTIO_VGA_GL); static void virtio_vga_register_types(void) { @@ -45,3 +46,5 @@ static void virtio_vga_register_types(void) } type_init(virtio_vga_register_types) + +module_dep("hw-display-virtio-vga"); diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c index d3c6404061..9e57f61e9e 100644 --- a/hw/display/virtio-vga.c +++ b/hw/display/virtio-vga.c @@ -239,6 +239,7 @@ static TypeInfo virtio_vga_base_info = { .class_init = virtio_vga_base_class_init, .abstract = true, }; +module_obj(TYPE_VIRTIO_VGA_BASE); #define TYPE_VIRTIO_VGA "virtio-vga" @@ -268,6 +269,7 @@ static VirtioPCIDeviceTypeInfo virtio_vga_info = { .instance_size = sizeof(VirtIOVGA), .instance_init = virtio_vga_inst_initfn, }; +module_obj(TYPE_VIRTIO_VGA); static void virtio_vga_register_types(void) { From 882273d953f4ed6ae9465e15810c4fcb2fe3aaab Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:09 +0200 Subject: [PATCH 163/272] modules: add chardev module annotations Signed-off-by: Gerd Hoffmann Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-8-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- chardev/baum.c | 1 + chardev/spice.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/chardev/baum.c b/chardev/baum.c index 5deca778bc..79d618e350 100644 --- a/chardev/baum.c +++ b/chardev/baum.c @@ -680,6 +680,7 @@ static const TypeInfo char_braille_type_info = { .instance_finalize = char_braille_finalize, .class_init = char_braille_class_init, }; +module_obj(TYPE_CHARDEV_BRAILLE); static void register_types(void) { diff --git a/chardev/spice.c b/chardev/spice.c index 1104426e3a..3ffb3fdc0d 100644 --- a/chardev/spice.c +++ b/chardev/spice.c @@ -366,6 +366,7 @@ static const TypeInfo char_spice_type_info = { .class_init = char_spice_class_init, .abstract = true, }; +module_obj(TYPE_CHARDEV_SPICE); static void char_spicevmc_class_init(ObjectClass *oc, void *data) { @@ -396,6 +397,7 @@ static const TypeInfo char_spiceport_type_info = { .parent = TYPE_CHARDEV_SPICE, .class_init = char_spiceport_class_init, }; +module_obj(TYPE_CHARDEV_SPICEPORT); static void register_types(void) { @@ -405,3 +407,5 @@ static void register_types(void) } type_init(register_types); + +module_dep("ui-spice-core"); From f6b12dfd80f3b0d6fbaf982718946e5ad72a543e Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:10 +0200 Subject: [PATCH 164/272] modules: add audio module annotations Signed-off-by: Gerd Hoffmann Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-9-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- audio/spiceaudio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c index 999bfbde47..a8d370fe6f 100644 --- a/audio/spiceaudio.c +++ b/audio/spiceaudio.c @@ -317,3 +317,5 @@ static void register_audio_spice(void) audio_driver_register(&spice_audio_driver); } type_init(register_audio_spice); + +module_dep("ui-spice-core"); From 320f4833331c45bc77b633d8f08ba4fba89ea924 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:11 +0200 Subject: [PATCH 165/272] modules: add usb-redir module annotations Signed-off-by: Gerd Hoffmann Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-10-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- hw/usb/redirect.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 6a75b0dc4a..4ec9326e05 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -2608,6 +2608,7 @@ static const TypeInfo usbredir_dev_info = { .class_init = usbredir_class_initfn, .instance_init = usbredir_instance_init, }; +module_obj(TYPE_USB_REDIR); static void usbredir_register_types(void) { From be4bf77c9cb9f46396473e9f8cd9cb56e29b851c Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:12 +0200 Subject: [PATCH 166/272] modules: add ccid module annotations Signed-off-by: Gerd Hoffmann Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-11-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- hw/usb/ccid-card-emulated.c | 1 + hw/usb/ccid-card-passthru.c | 1 + 2 files changed, 2 insertions(+) diff --git a/hw/usb/ccid-card-emulated.c b/hw/usb/ccid-card-emulated.c index 5c76bed77a..6c8c0355e0 100644 --- a/hw/usb/ccid-card-emulated.c +++ b/hw/usb/ccid-card-emulated.c @@ -612,6 +612,7 @@ static const TypeInfo emulated_card_info = { .instance_size = sizeof(EmulatedState), .class_init = emulated_class_initfn, }; +module_obj(TYPE_EMULATED_CCID); static void ccid_card_emulated_register_types(void) { diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c index 7212d0d7fb..fa3040fb71 100644 --- a/hw/usb/ccid-card-passthru.c +++ b/hw/usb/ccid-card-passthru.c @@ -414,6 +414,7 @@ static const TypeInfo passthru_card_info = { .instance_size = sizeof(PassthruState), .class_init = passthru_class_initfn, }; +module_obj(TYPE_CCID_PASSTHRU); static void ccid_card_passthru_register_types(void) { From b36ae1c1a235e531e9ccc90bf588749c7bc2d426 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:13 +0200 Subject: [PATCH 167/272] modules: add ui module annotations Signed-off-by: Gerd Hoffmann Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-12-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- ui/egl-headless.c | 4 ++++ ui/gtk.c | 4 ++++ ui/sdl2.c | 4 ++++ ui/spice-app.c | 3 +++ ui/spice-core.c | 5 +++++ 5 files changed, 20 insertions(+) diff --git a/ui/egl-headless.c b/ui/egl-headless.c index da377a74af..75404e0e87 100644 --- a/ui/egl-headless.c +++ b/ui/egl-headless.c @@ -213,3 +213,7 @@ static void register_egl(void) } type_init(register_egl); + +#ifdef CONFIG_OPENGL +module_dep("ui-opengl"); +#endif diff --git a/ui/gtk.c b/ui/gtk.c index 98046f577b..376b4d528d 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -2333,3 +2333,7 @@ static void register_gtk(void) } type_init(register_gtk); + +#ifdef CONFIG_OPENGL +module_dep("ui-opengl"); +#endif diff --git a/ui/sdl2.c b/ui/sdl2.c index a203cb0239..36d9010cb6 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -918,3 +918,7 @@ static void register_sdl1(void) } type_init(register_sdl1); + +#ifdef CONFIG_OPENGL +module_dep("ui-opengl"); +#endif diff --git a/ui/spice-app.c b/ui/spice-app.c index 4325ac2d9c..641f4a9d53 100644 --- a/ui/spice-app.c +++ b/ui/spice-app.c @@ -221,3 +221,6 @@ static void register_spice_app(void) } type_init(register_spice_app); + +module_dep("ui-spice-core"); +module_dep("chardev-spice"); diff --git a/ui/spice-core.c b/ui/spice-core.c index 272d19b0c1..86d43783ac 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -1037,3 +1037,8 @@ static void spice_register_config(void) qemu_add_opts(&qemu_spice_opts); } opts_init(spice_register_config); +module_opts("spice"); + +#ifdef CONFIG_OPENGL +module_dep("ui-opengl"); +#endif From 8245782fd2e097cf499cd58b2c118772f48b0327 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:14 +0200 Subject: [PATCH 168/272] modules: add s390x module annotations Signed-off-by: Gerd Hoffmann Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-13-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- hw/s390x/virtio-ccw-gpu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/s390x/virtio-ccw-gpu.c b/hw/s390x/virtio-ccw-gpu.c index 75a9e4bb39..5868a2a070 100644 --- a/hw/s390x/virtio-ccw-gpu.c +++ b/hw/s390x/virtio-ccw-gpu.c @@ -59,6 +59,7 @@ static const TypeInfo virtio_ccw_gpu = { .instance_init = virtio_ccw_gpu_instance_init, .class_init = virtio_ccw_gpu_class_init, }; +module_obj(TYPE_VIRTIO_GPU_CCW); static void virtio_ccw_gpu_register(void) { @@ -68,3 +69,5 @@ static void virtio_ccw_gpu_register(void) } type_init(virtio_ccw_gpu_register) + +module_arch("s390x"); From f8ade0dc01ba4920f3649db463b55253b71c0999 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:15 +0200 Subject: [PATCH 169/272] modules: add block module annotations Signed-off-by: Gerd Hoffmann Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-14-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- block/iscsi-opts.c | 1 + 1 file changed, 1 insertion(+) diff --git a/block/iscsi-opts.c b/block/iscsi-opts.c index afaf8837d6..4f2da405e6 100644 --- a/block/iscsi-opts.c +++ b/block/iscsi-opts.c @@ -68,3 +68,4 @@ static void iscsi_block_opts_init(void) } block_init(iscsi_block_opts_init); +module_opts("iscsi"); From e897b9a73558a345878c132489afcc55ecbec711 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:16 +0200 Subject: [PATCH 170/272] modules: use modinfo for dependencies Use module database for module dependencies. Drop hard-coded dependency list. Signed-off-by: Gerd Hoffmann Reviewed-by: Paolo Bonzini Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-15-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- util/module.c | 55 ++++++++++++++++++++------------------------------- 1 file changed, 21 insertions(+), 34 deletions(-) diff --git a/util/module.c b/util/module.c index 8d3e8275b9..7d7b69cbda 100644 --- a/util/module.c +++ b/util/module.c @@ -182,28 +182,6 @@ static int module_load_file(const char *fname, bool mayfail, bool export_symbols out: return ret; } - -static const struct { - const char *name; - const char *dep; -} module_deps[] = { - { "audio-spice", "ui-spice-core" }, - { "chardev-spice", "ui-spice-core" }, - { "hw-display-qxl", "ui-spice-core" }, - { "ui-spice-app", "ui-spice-core" }, - { "ui-spice-app", "chardev-spice" }, - - { "hw-display-virtio-gpu-gl", "hw-display-virtio-gpu" }, - { "hw-display-virtio-gpu-pci-gl", "hw-display-virtio-gpu-pci" }, - { "hw-display-virtio-vga-gl", "hw-display-virtio-vga" }, - -#ifdef CONFIG_OPENGL - { "ui-egl-headless", "ui-opengl" }, - { "ui-gtk", "ui-opengl" }, - { "ui-sdl", "ui-opengl" }, - { "ui-spice-core", "ui-opengl" }, -#endif -}; #endif bool module_load_one(const char *prefix, const char *lib_name, bool mayfail) @@ -219,9 +197,11 @@ bool module_load_one(const char *prefix, const char *lib_name, bool mayfail) char *dirs[5]; char *module_name; int i = 0, n_dirs = 0; - int ret, dep; + int ret; bool export_symbols = false; static GHashTable *loaded_modules; + const QemuModinfo *modinfo; + const char **sl; if (!g_module_supported()) { fprintf(stderr, "Module is not supported by system.\n"); @@ -234,23 +214,30 @@ bool module_load_one(const char *prefix, const char *lib_name, bool mayfail) module_name = g_strdup_printf("%s%s", prefix, lib_name); - for (dep = 0; dep < ARRAY_SIZE(module_deps); dep++) { - if (strcmp(module_name, module_deps[dep].name) == 0) { - /* we depend on another module */ - module_load_one("", module_deps[dep].dep, false); - } - if (strcmp(module_name, module_deps[dep].dep) == 0) { - /* another module depends on us */ - export_symbols = true; - } - } - if (g_hash_table_contains(loaded_modules, module_name)) { g_free(module_name); return true; } g_hash_table_add(loaded_modules, module_name); + for (modinfo = module_info; modinfo->name != NULL; modinfo++) { + if (modinfo->deps) { + if (strcmp(modinfo->name, module_name) == 0) { + /* we depend on other module(s) */ + for (sl = modinfo->deps; *sl != NULL; sl++) { + module_load_one("", *sl, false); + } + } else { + for (sl = modinfo->deps; *sl != NULL; sl++) { + if (strcmp(module_name, *sl) == 0) { + /* another module depends on us */ + export_symbols = true; + } + } + } + } + } + search_dir = getenv("QEMU_MODULE_DIR"); if (search_dir != NULL) { dirs[n_dirs++] = g_strdup_printf("%s", search_dir); From 9f4a0f0978cde9d8e27453b3f2d3679b53623c47 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:17 +0200 Subject: [PATCH 171/272] modules: use modinfo for qom load Use module database to figure which module implements a given QOM type. Drop hard-coded object list. Signed-off-by: Gerd Hoffmann Reviewed-by: Paolo Bonzini Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-16-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- util/module.c | 77 ++++++++++++++++----------------------------------- 1 file changed, 24 insertions(+), 53 deletions(-) diff --git a/util/module.c b/util/module.c index 7d7b69cbda..745ae0fb20 100644 --- a/util/module.c +++ b/util/module.c @@ -280,80 +280,51 @@ bool module_load_one(const char *prefix, const char *lib_name, bool mayfail) return success; } -/* - * Building devices and other qom objects modular is mostly useful in - * case they have dependencies to external shared libraries, so we can - * cut down the core qemu library dependencies. Which is the case for - * only a very few devices & objects. - * - * So with the expectation that this will be rather the exception than - * the rule and the list will not gain that many entries, go with a - * simple manually maintained list for now. - * - * The list must be sorted by module (module_load_qom_all() needs this). - */ -static struct { - const char *type; - const char *prefix; - const char *module; -} const qom_modules[] = { - { "ccid-card-passthru", "hw-", "usb-smartcard" }, - { "ccid-card-emulated", "hw-", "usb-smartcard" }, - { "usb-redir", "hw-", "usb-redirect" }, - { "qxl-vga", "hw-", "display-qxl" }, - { "qxl", "hw-", "display-qxl" }, - { "virtio-gpu-device", "hw-", "display-virtio-gpu" }, - { "virtio-gpu-gl-device", "hw-", "display-virtio-gpu-gl" }, - { "vhost-user-gpu", "hw-", "display-virtio-gpu" }, - { "virtio-gpu-pci-base", "hw-", "display-virtio-gpu-pci" }, - { "virtio-gpu-pci", "hw-", "display-virtio-gpu-pci" }, - { "virtio-gpu-gl-pci", "hw-", "display-virtio-gpu-pci-gl" }, - { "vhost-user-gpu-pci", "hw-", "display-virtio-gpu-pci" }, - { "virtio-gpu-ccw", "hw-", "s390x-virtio-gpu-ccw" }, - { "virtio-vga-base", "hw-", "display-virtio-vga" }, - { "virtio-vga", "hw-", "display-virtio-vga" }, - { "virtio-vga-gl", "hw-", "display-virtio-vga-gl" }, - { "vhost-user-vga", "hw-", "display-virtio-vga" }, - { "chardev-braille", "chardev-", "baum" }, - { "chardev-spicevmc", "chardev-", "spice" }, - { "chardev-spiceport", "chardev-", "spice" }, -}; +#ifdef CONFIG_MODULES static bool module_loaded_qom_all; void module_load_qom_one(const char *type) { - int i; + const QemuModinfo *modinfo; + const char **sl; if (!type) { return; } - for (i = 0; i < ARRAY_SIZE(qom_modules); i++) { - if (strcmp(qom_modules[i].type, type) == 0) { - module_load_one(qom_modules[i].prefix, - qom_modules[i].module, - false); - return; + + for (modinfo = module_info; modinfo->name != NULL; modinfo++) { + if (!modinfo->objs) { + continue; + } + for (sl = modinfo->objs; *sl != NULL; sl++) { + if (strcmp(type, *sl) == 0) { + module_load_one("", modinfo->name, false); + } } } } void module_load_qom_all(void) { - int i; + const QemuModinfo *modinfo; if (module_loaded_qom_all) { return; } - for (i = 0; i < ARRAY_SIZE(qom_modules); i++) { - if (i > 0 && (strcmp(qom_modules[i - 1].module, - qom_modules[i].module) == 0 && - strcmp(qom_modules[i - 1].prefix, - qom_modules[i].prefix) == 0)) { - /* one module implementing multiple types -> load only once */ + + for (modinfo = module_info; modinfo->name != NULL; modinfo++) { + if (!modinfo->objs) { continue; } - module_load_one(qom_modules[i].prefix, qom_modules[i].module, true); + module_load_one("", modinfo->name, false); } module_loaded_qom_all = true; } + +#else + +void module_load_qom_one(const char *type) {} +void module_load_qom_all(void) {} + +#endif From 5111edaf9e9ffac1a1b46d5942200af13b413ea8 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:18 +0200 Subject: [PATCH 172/272] modules: use modinfo for qemu opts load Use module database to figure which module adds given QemuOpts group. Signed-off-by: Gerd Hoffmann Reviewed-by: Paolo Bonzini Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-17-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- softmmu/vl.c | 17 ----------------- stubs/module-opts.c | 4 ---- util/module.c | 19 +++++++++++++++++++ 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/softmmu/vl.c b/softmmu/vl.c index 7cef3423a7..190b71a0f2 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -2691,23 +2691,6 @@ void qmp_x_exit_preconfig(Error **errp) } } -#ifdef CONFIG_MODULES -void qemu_load_module_for_opts(const char *group) -{ - static bool spice_tried; - if (g_str_equal(group, "spice") && !spice_tried) { - ui_module_load_one("spice-core"); - spice_tried = true; - } - - static bool iscsi_tried; - if (g_str_equal(group, "iscsi") && !iscsi_tried) { - block_module_load_one("iscsi"); - iscsi_tried = true; - } -} -#endif - void qemu_init(int argc, char **argv, char **envp) { QemuOpts *opts; diff --git a/stubs/module-opts.c b/stubs/module-opts.c index a7d0e4ad6e..5412429ea8 100644 --- a/stubs/module-opts.c +++ b/stubs/module-opts.c @@ -1,6 +1,2 @@ #include "qemu/osdep.h" #include "qemu/config-file.h" - -void qemu_load_module_for_opts(const char *group) -{ -} diff --git a/util/module.c b/util/module.c index 745ae0fb20..a9ec2da997 100644 --- a/util/module.c +++ b/util/module.c @@ -20,6 +20,7 @@ #include "qemu/queue.h" #include "qemu/module.h" #include "qemu/cutils.h" +#include "qemu/config-file.h" #ifdef CONFIG_MODULE_UPGRADES #include "qemu-version.h" #endif @@ -322,8 +323,26 @@ void module_load_qom_all(void) module_loaded_qom_all = true; } +void qemu_load_module_for_opts(const char *group) +{ + const QemuModinfo *modinfo; + const char **sl; + + for (modinfo = module_info; modinfo->name != NULL; modinfo++) { + if (!modinfo->opts) { + continue; + } + for (sl = modinfo->opts; *sl != NULL; sl++) { + if (strcmp(group, *sl) == 0) { + module_load_one("", modinfo->name, false); + } + } + } +} + #else +void qemu_load_module_for_opts(const char *group) {} void module_load_qom_one(const char *type) {} void module_load_qom_all(void) {} From 819b8b13c1bc48080f4ca526c6e12d58a27ea887 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:19 +0200 Subject: [PATCH 173/272] modules: add tracepoints One for module load and one for qom type lookup. Signed-off-by: Gerd Hoffmann Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-18-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- util/module.c | 3 +++ util/trace-events | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/util/module.c b/util/module.c index a9ec2da997..acaaecad56 100644 --- a/util/module.c +++ b/util/module.c @@ -24,6 +24,7 @@ #ifdef CONFIG_MODULE_UPGRADES #include "qemu-version.h" #endif +#include "trace.h" typedef struct ModuleEntry { @@ -176,6 +177,7 @@ static int module_load_file(const char *fname, bool mayfail, bool export_symbols ret = 0; } + trace_module_load_module(fname); QTAILQ_FOREACH_SAFE(e, &dso_init_list, node, next) { QTAILQ_REMOVE(&dso_init_list, e, node); g_free(e); @@ -294,6 +296,7 @@ void module_load_qom_one(const char *type) return; } + trace_module_lookup_object_type(type); for (modinfo = module_info; modinfo->name != NULL; modinfo++) { if (!modinfo->objs) { continue; diff --git a/util/trace-events b/util/trace-events index 806cac14a7..c8f53d7d9f 100644 --- a/util/trace-events +++ b/util/trace-events @@ -100,3 +100,7 @@ uffd_create_fd_api_failed(int err) "errno: %i" uffd_create_fd_api_noioctl(uint64_t ioctl_req, uint64_t ioctl_supp) "ioctl_req: 0x%" PRIx64 "ioctl_supp: 0x%" PRIx64 uffd_register_memory_failed(void *addr, uint64_t length, uint64_t mode, int err) "addr: %p length: %" PRIu64 " mode: 0x%" PRIx64 " errno: %i" uffd_unregister_memory_failed(void *addr, uint64_t length, int err) "addr: %p length: %" PRIu64 " errno: %i" + +# module.c +module_load_module(const char *name) "file %s" +module_lookup_object_type(const char *name) "name %s" From d7795d3cc52fa8c297908912a9541ecd4f810f03 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:20 +0200 Subject: [PATCH 174/272] modules: check arch and block load on mismatch Add module_allow_arch() to set the target architecture. In case a module is limited to some arch verify arches match and ignore the module if not. Signed-off-by: Gerd Hoffmann Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-19-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- include/qemu/module.h | 1 + softmmu/vl.c | 3 +++ util/module.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/include/qemu/module.h b/include/qemu/module.h index 9cd305de59..3deac0078b 100644 --- a/include/qemu/module.h +++ b/include/qemu/module.h @@ -72,6 +72,7 @@ void module_call_init(module_init_type type); bool module_load_one(const char *prefix, const char *lib_name, bool mayfail); void module_load_qom_one(const char *type); void module_load_qom_all(void); +void module_allow_arch(const char *arch); /** * DOC: module info annotation macros diff --git a/softmmu/vl.c b/softmmu/vl.c index 190b71a0f2..2004d57108 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -126,6 +126,8 @@ #include "sysemu/iothread.h" #include "qemu/guest-random.h" +#include "config-host.h" + #define MAX_VIRTIO_CONSOLES 1 typedef struct BlockdevOptionsQueueEntry { @@ -2740,6 +2742,7 @@ void qemu_init(int argc, char **argv, char **envp) #ifdef CONFIG_MODULES module_init_info(qemu_modinfo); + module_allow_arch(TARGET_NAME); #endif qemu_init_subsystems(); diff --git a/util/module.c b/util/module.c index acaaecad56..065aed09ff 100644 --- a/util/module.c +++ b/util/module.c @@ -117,12 +117,33 @@ static const QemuModinfo module_info_stub[] = { { /* end of list */ } }; static const QemuModinfo *module_info = module_info_stub; +static const char *module_arch; void module_init_info(const QemuModinfo *info) { module_info = info; } +void module_allow_arch(const char *arch) +{ + module_arch = arch; +} + +static bool module_check_arch(const QemuModinfo *modinfo) +{ + if (modinfo->arch) { + if (!module_arch) { + /* no arch set -> ignore all */ + return false; + } + if (strcmp(module_arch, modinfo->arch) != 0) { + /* mismatch */ + return false; + } + } + return true; +} + static int module_load_file(const char *fname, bool mayfail, bool export_symbols) { GModule *g_module; @@ -224,6 +245,13 @@ bool module_load_one(const char *prefix, const char *lib_name, bool mayfail) g_hash_table_add(loaded_modules, module_name); for (modinfo = module_info; modinfo->name != NULL; modinfo++) { + if (modinfo->arch) { + if (strcmp(modinfo->name, module_name) == 0) { + if (!module_check_arch(modinfo)) { + return false; + } + } + } if (modinfo->deps) { if (strcmp(modinfo->name, module_name) == 0) { /* we depend on other module(s) */ @@ -345,6 +373,7 @@ void qemu_load_module_for_opts(const char *group) #else +void module_allow_arch(const char *arch) {} void qemu_load_module_for_opts(const char *group) {} void module_load_qom_one(const char *type) {} void module_load_qom_all(void) {} From ab0cfc3dcbe763ae615a284dbc68997af933ff9c Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:21 +0200 Subject: [PATCH 175/272] modules: check arch on qom lookup With target-specific modules we can have multiple modules implementing the same object. Therefore we have to check the target arch on lookup to find the correct module. Signed-off-by: Gerd Hoffmann Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-20-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- util/module.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/util/module.c b/util/module.c index 065aed09ff..6bb4ad915a 100644 --- a/util/module.c +++ b/util/module.c @@ -329,6 +329,9 @@ void module_load_qom_one(const char *type) if (!modinfo->objs) { continue; } + if (!module_check_arch(modinfo)) { + continue; + } for (sl = modinfo->objs; *sl != NULL; sl++) { if (strcmp(type, *sl) == 0) { module_load_one("", modinfo->name, false); @@ -349,6 +352,9 @@ void module_load_qom_all(void) if (!modinfo->objs) { continue; } + if (!module_check_arch(modinfo)) { + continue; + } module_load_one("", modinfo->name, false); } module_loaded_qom_all = true; From db2e89df998abbcf11b68f29558c45c8379d8916 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:22 +0200 Subject: [PATCH 176/272] modules: target-specific module build infrastructure Signed-off-by: Gerd Hoffmann Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-21-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- meson.build | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/meson.build b/meson.build index 7b827f7caa..7babef4de4 100644 --- a/meson.build +++ b/meson.build @@ -2001,6 +2001,7 @@ user_ss = ss.source_set() util_ss = ss.source_set() modules = {} +target_modules = {} hw_arch = {} target_arch = {} target_softmmu_arch = {} @@ -2280,6 +2281,42 @@ foreach d, list : modules endforeach endforeach +foreach d, list : target_modules + foreach m, module_ss : list + if enable_modules and targetos != 'windows' + foreach target : target_dirs + if target.endswith('-softmmu') + config_target = config_target_mak[target] + config_target += config_host + target_inc = [include_directories('target' / config_target['TARGET_BASE_ARCH'])] + c_args = ['-DNEED_CPU_H', + '-DCONFIG_TARGET="@0@-config-target.h"'.format(target), + '-DCONFIG_DEVICES="@0@-config-devices.h"'.format(target)] + target_module_ss = module_ss.apply(config_target, strict: false) + if target_module_ss.sources() != [] + module_name = d + '-' + m + '-' + config_target['TARGET_NAME'] + sl = static_library(module_name, + [genh, target_module_ss.sources()], + dependencies: [modulecommon, target_module_ss.dependencies()], + include_directories: target_inc, + c_args: c_args, + pic: true) + softmmu_mods += sl + # FIXME: Should use sl.extract_all_objects(recursive: true) too. + modinfo_files += custom_target(module_name + '.modinfo', + output: module_name + '.modinfo', + input: target_module_ss.sources(), + capture: true, + command: [modinfo_collect, '--target', target, '@INPUT@']) + endif + endif + endforeach + else + specific_ss.add_all(module_ss) + endif + endforeach +endforeach + if enable_modules modinfo_src = custom_target('modinfo.c', output: 'modinfo.c', From 964711c44c9a45bbda0553a456e581fa9c1a3749 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:23 +0200 Subject: [PATCH 177/272] modules: add documentation for module sourcesets Signed-off-by: Gerd Hoffmann Reviewed-by: Paolo Bonzini Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-22-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- docs/devel/build-system.rst | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/devel/build-system.rst b/docs/devel/build-system.rst index 7ef36f42d0..fd1650442e 100644 --- a/docs/devel/build-system.rst +++ b/docs/devel/build-system.rst @@ -272,6 +272,23 @@ Target-dependent emulator sourcesets: target_arch += {'arm': arm_ss} target_softmmu_arch += {'arm': arm_softmmu_ss} +Module sourcesets: + There are two dictionaries for modules: `modules` is used for + target-independent modules and `target_modules` is used for + target-dependent modules. When modules are disabled the `module` + source sets are added to `softmmu_ss` and the `target_modules` + source sets are added to `specific_ss`. + + Both dictionaries are nested. One dictionary is created per + subdirectory, and these per-subdirectory dictionaries are added to + the toplevel dictionaries. For example:: + + hw_display_modules = {} + qxl_ss = ss.source_set() + ... + hw_display_modules += { 'qxl': qxl_ss } + modules += { 'hw-display': hw_display_modules } + Utility sourcesets: All binaries link with a static library `libqemuutil.a`. This library is built from several sourcesets; most of them however host generated From e95b135f88f6e3a29387cd4aabb410b8f966fc81 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:24 +0200 Subject: [PATCH 178/272] modules: add module_obj() note to QOM docs Signed-off-by: Gerd Hoffmann Reviewed-by: Paolo Bonzini Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-23-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- docs/devel/qom.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/devel/qom.rst b/docs/devel/qom.rst index 42d0dc4f4d..e5fe3597cd 100644 --- a/docs/devel/qom.rst +++ b/docs/devel/qom.rst @@ -87,6 +87,14 @@ specific type: #define MY_DEVICE(obj) \ OBJECT_CHECK(MyDevice, obj, TYPE_MY_DEVICE) +In case the ObjectClass implementation can be built as module a +module_obj() line must be added to make sure qemu loads the module +when the object is needed. + +.. code-block:: c + + module_obj(TYPE_MY_DEVICE); + Class Initialization ==================== From f76585efce3e3fe30c75efe9d126d5ebebd0b5f1 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:26 +0200 Subject: [PATCH 179/272] modules: hook up modules.h to docs build Signed-off-by: Gerd Hoffmann Reviewed-by: Paolo Bonzini Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-25-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- docs/devel/index.rst | 1 + docs/devel/modules.rst | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 docs/devel/modules.rst diff --git a/docs/devel/index.rst b/docs/devel/index.rst index 977c3893bd..ba90badbbd 100644 --- a/docs/devel/index.rst +++ b/docs/devel/index.rst @@ -41,6 +41,7 @@ Contents: s390-dasd-ipl clocks qom + modules block-coroutine-wrapper multi-process ebpf_rss diff --git a/docs/devel/modules.rst b/docs/devel/modules.rst new file mode 100644 index 0000000000..066f347b89 --- /dev/null +++ b/docs/devel/modules.rst @@ -0,0 +1,5 @@ +============ +Qemu modules +============ + +.. kernel-doc:: include/qemu/module.h From f934907a8b72cf315e5f587794a60d451d489672 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:27 +0200 Subject: [PATCH 180/272] accel: autoload modules Call module_object_class_by_name() instead of object_class_by_name() for objects possibly implemented as module Signed-off-by: Gerd Hoffmann Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-26-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- accel/accel-common.c | 2 +- accel/accel-softmmu.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/accel/accel-common.c b/accel/accel-common.c index cf07f78421..7b8ec7e0f7 100644 --- a/accel/accel-common.c +++ b/accel/accel-common.c @@ -44,7 +44,7 @@ static const TypeInfo accel_type = { AccelClass *accel_find(const char *opt_name) { char *class_name = g_strdup_printf(ACCEL_CLASS_NAME("%s"), opt_name); - AccelClass *ac = ACCEL_CLASS(object_class_by_name(class_name)); + AccelClass *ac = ACCEL_CLASS(module_object_class_by_name(class_name)); g_free(class_name); return ac; } diff --git a/accel/accel-softmmu.c b/accel/accel-softmmu.c index 50fa5acaa4..67276e4f52 100644 --- a/accel/accel-softmmu.c +++ b/accel/accel-softmmu.c @@ -72,7 +72,7 @@ void accel_init_ops_interfaces(AccelClass *ac) g_assert(ac_name != NULL); ops_name = g_strdup_printf("%s" ACCEL_OPS_SUFFIX, ac_name); - ops = ACCEL_OPS_CLASS(object_class_by_name(ops_name)); + ops = ACCEL_OPS_CLASS(module_object_class_by_name(ops_name)); g_free(ops_name); /* From a05ca2d4163139c5f2e5488c36326f725a11a6d0 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:28 +0200 Subject: [PATCH 181/272] accel: add qtest module annotations Add module annotations for qtest so autoloading works. Signed-off-by: Gerd Hoffmann Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-27-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- accel/qtest/qtest.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c index edb29f6fa4..7e6b8110d5 100644 --- a/accel/qtest/qtest.c +++ b/accel/qtest/qtest.c @@ -45,6 +45,7 @@ static const TypeInfo qtest_accel_type = { .parent = TYPE_ACCEL, .class_init = qtest_accel_class_init, }; +module_obj(TYPE_QTEST_ACCEL); static void qtest_accel_ops_class_init(ObjectClass *oc, void *data) { @@ -61,6 +62,7 @@ static const TypeInfo qtest_accel_ops_type = { .class_init = qtest_accel_ops_class_init, .abstract = true, }; +module_obj(ACCEL_OPS_NAME("qtest")); static void qtest_type_init(void) { From c94a7b8892dbb6b688994e5b9a717bda23de4e9a Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:29 +0200 Subject: [PATCH 182/272] accel: build qtest modular Allow building accelerators as module. Start with qtest as first user. Signed-off-by: Gerd Hoffmann Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-28-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- accel/qtest/meson.build | 8 ++------ meson.build | 6 ++++++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/accel/qtest/meson.build b/accel/qtest/meson.build index a2f3276459..4c65600293 100644 --- a/accel/qtest/meson.build +++ b/accel/qtest/meson.build @@ -1,6 +1,2 @@ -qtest_ss = ss.source_set() -qtest_ss.add(files( - 'qtest.c', -)) - -specific_ss.add_all(when: ['CONFIG_SOFTMMU', 'CONFIG_POSIX'], if_true: qtest_ss) +qtest_module_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_POSIX'], + if_true: files('qtest.c')) diff --git a/meson.build b/meson.build index 7babef4de4..c943e44975 100644 --- a/meson.build +++ b/meson.build @@ -2000,6 +2000,9 @@ trace_ss = ss.source_set() user_ss = ss.source_set() util_ss = ss.source_set() +# accel modules +qtest_module_ss = ss.source_set() + modules = {} target_modules = {} hw_arch = {} @@ -2238,6 +2241,9 @@ specific_ss.add_all(when: 'CONFIG_LINUX_USER', if_true: linux_user_ss) subdir('tests/qtest/libqos') subdir('tests/qtest/fuzz') +# accel modules +target_modules += { 'accel' : { 'qtest': qtest_module_ss }} + ######################## # Library dependencies # ######################## From 9e5d3b692e24201ea160e78e56dcadb8a7e22905 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:30 +0200 Subject: [PATCH 183/272] accel: add tcg module annotations Add module annotations for tcg so autoloading works. Signed-off-by: Gerd Hoffmann Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-29-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- accel/tcg/tcg-accel-ops.c | 1 + accel/tcg/tcg-all.c | 1 + 2 files changed, 2 insertions(+) diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 7191315aee..1a8e8390bd 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -124,6 +124,7 @@ static const TypeInfo tcg_accel_ops_type = { .class_init = tcg_accel_ops_class_init, .abstract = true, }; +module_obj(ACCEL_OPS_NAME("tcg")); static void tcg_accel_ops_register_types(void) { diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index 00803f76d8..d6336a9c96 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -238,6 +238,7 @@ static const TypeInfo tcg_accel_type = { .class_init = tcg_accel_class_init, .instance_size = sizeof(TCGState), }; +module_obj(TYPE_TCG_ACCEL); static void register_accel_types(void) { From dae0ec159f92050026961656e9b7ded1e72758a4 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:31 +0200 Subject: [PATCH 184/272] accel: build tcg modular Build tcg accel ops as module. Which is only a small fraction of tcg. Also only x86 for now. Signed-off-by: Gerd Hoffmann Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-30-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- accel/tcg/meson.build | 5 ++++- meson.build | 14 +++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 1236ac7b91..0ae9180282 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -15,8 +15,11 @@ specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_ss) specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: files( 'cputlb.c', +)) + +tcg_module_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: files( 'tcg-accel-ops.c', 'tcg-accel-ops-mttcg.c', 'tcg-accel-ops-icount.c', - 'tcg-accel-ops-rr.c' + 'tcg-accel-ops-rr.c', )) diff --git a/meson.build b/meson.build index c943e44975..d9aa2d7820 100644 --- a/meson.build +++ b/meson.build @@ -92,6 +92,8 @@ if cpu in ['x86', 'x86_64'] } endif +modular_tcg = ['i386-softmmu', 'x86_64-softmmu'] + edk2_targets = [ 'arm-softmmu', 'aarch64-softmmu', 'i386-softmmu', 'x86_64-softmmu' ] install_edk2_blobs = false if get_option('install_blobs') @@ -1531,6 +1533,11 @@ foreach target : target_dirs elif sym == 'CONFIG_XEN' and have_xen_pci_passthrough config_target += { 'CONFIG_XEN_PCI_PASSTHROUGH': 'y' } endif + if target in modular_tcg + config_target += { 'CONFIG_TCG_MODULAR': 'y' } + else + config_target += { 'CONFIG_TCG_BUILTIN': 'y' } + endif accel_kconfig += [ sym + '=y' ] endif endforeach @@ -2002,6 +2009,7 @@ util_ss = ss.source_set() # accel modules qtest_module_ss = ss.source_set() +tcg_module_ss = ss.source_set() modules = {} target_modules = {} @@ -2242,7 +2250,11 @@ subdir('tests/qtest/libqos') subdir('tests/qtest/fuzz') # accel modules -target_modules += { 'accel' : { 'qtest': qtest_module_ss }} +tcg_real_module_ss = ss.source_set() +tcg_real_module_ss.add_all(when: 'CONFIG_TCG_MODULAR', if_true: tcg_module_ss) +specific_ss.add_all(when: 'CONFIG_TCG_BUILTIN', if_true: tcg_module_ss) +target_modules += { 'accel' : { 'qtest': qtest_module_ss, + 'tcg': tcg_real_module_ss }} ######################## # Library dependencies # From f0e48cbd791e88728fcea65366dbb6d9a63a16e5 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:32 +0200 Subject: [PATCH 185/272] monitor: allow register hmp commands Allow commands having a NULL cmd pointer, add a function to set the pointer later. Use case: allow modules implement hmp commands. Signed-off-by: Gerd Hoffmann Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-31-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- include/monitor/monitor.h | 3 +++ monitor/hmp.c | 7 +++++++ monitor/misc.c | 16 ++++++++++++++++ 3 files changed, 26 insertions(+) diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index 1211d6e6d6..1a8a369b50 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -51,4 +51,7 @@ int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags); void monitor_fdset_dup_fd_remove(int dup_fd); int64_t monitor_fdset_dup_fd_find(int dup_fd); +void monitor_register_hmp(const char *name, bool info, + void (*cmd)(Monitor *mon, const QDict *qdict)); + #endif /* MONITOR_H */ diff --git a/monitor/hmp.c b/monitor/hmp.c index 6c0b33a0b1..d50c3124e1 100644 --- a/monitor/hmp.c +++ b/monitor/hmp.c @@ -1089,6 +1089,13 @@ void handle_hmp_command(MonitorHMP *mon, const char *cmdline) return; } + if (!cmd->cmd) { + /* FIXME: is it useful to try autoload modules here ??? */ + monitor_printf(&mon->common, "Command \"%.*s\" is not available.\n", + (int)(cmdline - cmd_start), cmd_start); + return; + } + qdict = monitor_parse_arguments(&mon->common, &cmdline, cmd); if (!qdict) { while (cmdline > cmd_start && qemu_isspace(cmdline[-1])) { diff --git a/monitor/misc.c b/monitor/misc.c index 1539e18557..ad476c6e51 100644 --- a/monitor/misc.c +++ b/monitor/misc.c @@ -1974,6 +1974,22 @@ static void sortcmdlist(void) compare_mon_cmd); } +void monitor_register_hmp(const char *name, bool info, + void (*cmd)(Monitor *mon, const QDict *qdict)) +{ + HMPCommand *table = info ? hmp_info_cmds : hmp_cmds; + + while (table->name != NULL) { + if (strcmp(table->name, name) == 0) { + g_assert(table->cmd == NULL); + table->cmd = cmd; + return; + } + table++; + } + g_assert_not_reached(); +} + void monitor_init_globals(void) { monitor_init_globals_core(); From b7b2a60b01036c6e7c21e7dc41829c7b5f6011b4 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:33 +0200 Subject: [PATCH 186/272] usb: drop usb_host_dev_is_scsi_storage hook Introduce an usb device flag instead, set it when usb-host looks at the device descriptors anyway. Also set it for emulated storage devices, for consistency. Add an inline helper function to check the flag. Signed-off-by: Gerd Hoffmann Acked-by: David Gibson Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-32-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- hw/ppc/spapr.c | 2 +- hw/usb/dev-storage-bot.c | 1 + hw/usb/dev-storage-classic.c | 1 + hw/usb/dev-uas.c | 1 + hw/usb/host-libusb.c | 36 +++++++----------------------------- hw/usb/host-stub.c | 5 ----- include/hw/usb.h | 7 ++++++- 7 files changed, 17 insertions(+), 36 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 4dd90b75cc..f83a081af0 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3106,7 +3106,7 @@ static char *spapr_get_fw_dev_path(FWPathProvider *p, BusState *bus, */ if (strcmp("usb-host", qdev_fw_name(dev)) == 0) { USBDevice *usbdev = CAST(USBDevice, dev, TYPE_USB_DEVICE); - if (usb_host_dev_is_scsi_storage(usbdev)) { + if (usb_device_is_scsi_storage(usbdev)) { return g_strdup_printf("storage@%s/disk", usbdev->port->path); } } diff --git a/hw/usb/dev-storage-bot.c b/hw/usb/dev-storage-bot.c index 6aad026d11..68ebaca10c 100644 --- a/hw/usb/dev-storage-bot.c +++ b/hw/usb/dev-storage-bot.c @@ -32,6 +32,7 @@ static void usb_msd_bot_realize(USBDevice *dev, Error **errp) usb_desc_create_serial(dev); usb_desc_init(dev); + dev->flags |= (1 << USB_DEV_FLAG_IS_SCSI_STORAGE); if (d->hotplugged) { s->dev.auto_attach = 0; } diff --git a/hw/usb/dev-storage-classic.c b/hw/usb/dev-storage-classic.c index 00cb34b22f..3d017a4e67 100644 --- a/hw/usb/dev-storage-classic.c +++ b/hw/usb/dev-storage-classic.c @@ -64,6 +64,7 @@ static void usb_msd_storage_realize(USBDevice *dev, Error **errp) usb_desc_create_serial(dev); usb_desc_init(dev); + dev->flags |= (1 << USB_DEV_FLAG_IS_SCSI_STORAGE); scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev), &usb_msd_scsi_info_storage, NULL); scsi_dev = scsi_bus_legacy_add_drive(&s->bus, blk, 0, !!s->removable, diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c index d2bd85d3f6..263056231c 100644 --- a/hw/usb/dev-uas.c +++ b/hw/usb/dev-uas.c @@ -926,6 +926,7 @@ static void usb_uas_realize(USBDevice *dev, Error **errp) QTAILQ_INIT(&uas->requests); uas->status_bh = qemu_bh_new(usb_uas_send_status_bh, uas); + dev->flags |= (1 << USB_DEV_FLAG_IS_SCSI_STORAGE); scsi_bus_new(&uas->bus, sizeof(uas->bus), DEVICE(dev), &usb_uas_scsi_info, NULL); } diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index 2518306f52..e6d21aa8e1 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -770,6 +770,13 @@ static void usb_host_speed_compat(USBHostDevice *s) for (i = 0; i < conf->bNumInterfaces; i++) { for (a = 0; a < conf->interface[i].num_altsetting; a++) { intf = &conf->interface[i].altsetting[a]; + + if (intf->bInterfaceClass == LIBUSB_CLASS_MASS_STORAGE && + intf->bInterfaceSubClass == 6) { /* SCSI */ + udev->flags |= (1 << USB_DEV_FLAG_IS_SCSI_STORAGE); + break; + } + for (e = 0; e < intf->bNumEndpoints; e++) { endp = &intf->endpoint[e]; type = endp->bmAttributes & 0x3; @@ -1893,35 +1900,6 @@ static void usb_host_auto_check(void *unused) timer_mod(usb_auto_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 2000); } -/** - * Check whether USB host device has a USB mass storage SCSI interface - */ -bool usb_host_dev_is_scsi_storage(USBDevice *ud) -{ - USBHostDevice *uhd = USB_HOST_DEVICE(ud); - struct libusb_config_descriptor *conf; - const struct libusb_interface_descriptor *intf; - bool is_scsi_storage = false; - int i; - - if (!uhd || libusb_get_active_config_descriptor(uhd->dev, &conf) != 0) { - return false; - } - - for (i = 0; i < conf->bNumInterfaces; i++) { - intf = &conf->interface[i].altsetting[ud->altsetting[i]]; - if (intf->bInterfaceClass == LIBUSB_CLASS_MASS_STORAGE && - intf->bInterfaceSubClass == 6) { /* 6 means SCSI */ - is_scsi_storage = true; - break; - } - } - - libusb_free_config_descriptor(conf); - - return is_scsi_storage; -} - void hmp_info_usbhost(Monitor *mon, const QDict *qdict) { libusb_device **devs = NULL; diff --git a/hw/usb/host-stub.c b/hw/usb/host-stub.c index 80809ceba5..bbe69baa39 100644 --- a/hw/usb/host-stub.c +++ b/hw/usb/host-stub.c @@ -38,8 +38,3 @@ void hmp_info_usbhost(Monitor *mon, const QDict *qdict) { monitor_printf(mon, "USB host devices not supported\n"); } - -bool usb_host_dev_is_scsi_storage(USBDevice *ud) -{ - return false; -} diff --git a/include/hw/usb.h b/include/hw/usb.h index 436e07b304..33668dd0a9 100644 --- a/include/hw/usb.h +++ b/include/hw/usb.h @@ -219,6 +219,7 @@ enum USBDeviceFlags { USB_DEV_FLAG_IS_HOST, USB_DEV_FLAG_MSOS_DESC_ENABLE, USB_DEV_FLAG_MSOS_DESC_IN_USE, + USB_DEV_FLAG_IS_SCSI_STORAGE, }; /* definition of a USB device */ @@ -465,7 +466,6 @@ void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p); /* usb-linux.c */ void hmp_info_usbhost(Monitor *mon, const QDict *qdict); -bool usb_host_dev_is_scsi_storage(USBDevice *usbdev); /* usb ports of the VM */ @@ -561,6 +561,11 @@ const char *usb_device_get_product_desc(USBDevice *dev); const USBDesc *usb_device_get_usb_desc(USBDevice *dev); +static inline bool usb_device_is_scsi_storage(USBDevice *dev) +{ + return dev->flags & (1 << USB_DEV_FLAG_IS_SCSI_STORAGE); +} + /* quirks.c */ /* In bulk endpoints are streaming data sources (iow behave like isoc eps) */ From f1a74bf976d52409047a187ff4ef1f3ac8c6c612 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:34 +0200 Subject: [PATCH 187/272] monitor/usb: register 'info usbhost' dynamically Signed-off-by: Gerd Hoffmann Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-33-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- hmp-commands-info.hx | 1 - hw/usb/host-libusb.c | 1 + hw/usb/host-stub.c | 40 ---------------------------------------- hw/usb/meson.build | 4 +--- 4 files changed, 2 insertions(+), 44 deletions(-) delete mode 100644 hw/usb/host-stub.c diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index fb59c27200..ce42aef47a 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -368,7 +368,6 @@ ERST .args_type = "", .params = "", .help = "show host USB devices", - .cmd = hmp_info_usbhost, }, SRST diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index e6d21aa8e1..2b7f87872c 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -1781,6 +1781,7 @@ static TypeInfo usb_host_dev_info = { static void usb_host_register_types(void) { type_register_static(&usb_host_dev_info); + monitor_register_hmp("usbhost", true, hmp_info_usbhost); } type_init(usb_host_register_types) diff --git a/hw/usb/host-stub.c b/hw/usb/host-stub.c deleted file mode 100644 index bbe69baa39..0000000000 --- a/hw/usb/host-stub.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Stub host USB redirector - * - * Copyright (c) 2005 Fabrice Bellard - * - * Copyright (c) 2008 Max Krasnyansky - * Support for host device auto connect & disconnect - * Major rewrite to support fully async operation - * - * Copyright 2008 TJ - * Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition - * to the legacy /proc/bus/usb USB device discovery and handling - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "hw/usb.h" -#include "monitor/monitor.h" - -void hmp_info_usbhost(Monitor *mon, const QDict *qdict) -{ - monitor_printf(mon, "USB host devices not supported\n"); -} diff --git a/hw/usb/meson.build b/hw/usb/meson.build index 4f24b5274d..817f3e027a 100644 --- a/hw/usb/meson.build +++ b/hw/usb/meson.build @@ -73,9 +73,7 @@ endif # usb pass-through softmmu_ss.add(when: ['CONFIG_USB', libusb], - if_true: files('host-libusb.c'), - if_false: files('host-stub.c')) -softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('host-stub.c')) + if_true: files('host-libusb.c')) softmmu_ss.add(when: ['CONFIG_USB', 'CONFIG_XEN', libusb], if_true: files('xen-usb.c')) From 627302afb2f85cdd4b59595361876487aef19b7a Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:35 +0200 Subject: [PATCH 188/272] usb: build usb-host as module Drop one more shared library dependency (libusb) from core qemu. Signed-off-by: Gerd Hoffmann Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-34-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- hw/usb/host-libusb.c | 1 + hw/usb/meson.build | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index 2b7f87872c..c0f314462a 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -1777,6 +1777,7 @@ static TypeInfo usb_host_dev_info = { .class_init = usb_host_class_initfn, .instance_init = usb_host_instance_init, }; +module_obj(TYPE_USB_HOST_DEVICE); static void usb_host_register_types(void) { diff --git a/hw/usb/meson.build b/hw/usb/meson.build index 817f3e027a..3ca6127937 100644 --- a/hw/usb/meson.build +++ b/hw/usb/meson.build @@ -72,8 +72,12 @@ if usbredir.found() endif # usb pass-through -softmmu_ss.add(when: ['CONFIG_USB', libusb], - if_true: files('host-libusb.c')) +if config_host.has_key('CONFIG_USB_LIBUSB') + usbhost_ss = ss.source_set() + usbhost_ss.add(when: ['CONFIG_USB', libusb], + if_true: files('host-libusb.c')) + hw_usb_modules += {'host': usbhost_ss} +endif softmmu_ss.add(when: ['CONFIG_USB', 'CONFIG_XEN', libusb], if_true: files('xen-usb.c')) From bca6eb34f0318b1b8211c9d227d5439e2d44286d Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Jun 2021 12:38:36 +0200 Subject: [PATCH 189/272] monitor/tcg: move tcg hmp commands to accel/tcg, register them dynamically One more little step towards modular tcg ... Signed-off-by: Gerd Hoffmann Acked-by: Dr. David Alan Gilbert Reviewed-by: Jose R. Ziviani Message-Id: <20210624103836.2382472-35-kraxel@redhat.com> Signed-off-by: Paolo Bonzini --- accel/tcg/hmp.c | 29 +++++++++++++++++++++++++++++ accel/tcg/meson.build | 1 + hmp-commands-info.hx | 2 -- monitor/misc.c | 18 ------------------ 4 files changed, 30 insertions(+), 20 deletions(-) create mode 100644 accel/tcg/hmp.c diff --git a/accel/tcg/hmp.c b/accel/tcg/hmp.c new file mode 100644 index 0000000000..a6e72fdb3e --- /dev/null +++ b/accel/tcg/hmp.c @@ -0,0 +1,29 @@ +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "exec/exec-all.h" +#include "monitor/monitor.h" +#include "sysemu/tcg.h" + +static void hmp_info_jit(Monitor *mon, const QDict *qdict) +{ + if (!tcg_enabled()) { + error_report("JIT information is only available with accel=tcg"); + return; + } + + dump_exec_info(); + dump_drift_info(); +} + +static void hmp_info_opcount(Monitor *mon, const QDict *qdict) +{ + dump_opcount_info(); +} + +static void hmp_tcg_register(void) +{ + monitor_register_hmp("jit", true, hmp_info_jit); + monitor_register_hmp("opcount", true, hmp_info_opcount); +} + +type_init(hmp_tcg_register); diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 0ae9180282..137a1a44cc 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -15,6 +15,7 @@ specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_ss) specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: files( 'cputlb.c', + 'hmp.c', )) tcg_module_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: files( diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index ce42aef47a..27206ac049 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -274,7 +274,6 @@ ERST .args_type = "", .params = "", .help = "show dynamic compiler info", - .cmd = hmp_info_jit, }, #endif @@ -289,7 +288,6 @@ ERST .args_type = "", .params = "", .help = "show dynamic compiler opcode counters", - .cmd = hmp_info_opcount, }, #endif diff --git a/monitor/misc.c b/monitor/misc.c index ad476c6e51..b28874d6dc 100644 --- a/monitor/misc.c +++ b/monitor/misc.c @@ -320,24 +320,6 @@ static void hmp_info_registers(Monitor *mon, const QDict *qdict) } } -#ifdef CONFIG_TCG -static void hmp_info_jit(Monitor *mon, const QDict *qdict) -{ - if (!tcg_enabled()) { - error_report("JIT information is only available with accel=tcg"); - return; - } - - dump_exec_info(); - dump_drift_info(); -} - -static void hmp_info_opcount(Monitor *mon, const QDict *qdict) -{ - dump_opcount_info(); -} -#endif - static void hmp_info_sync_profile(Monitor *mon, const QDict *qdict) { int64_t max = qdict_get_try_int(qdict, "max", 10); From 481077b28b3edee0d6c6cfbd48774270f819aaf1 Mon Sep 17 00:00:00 2001 From: Lara Lazier Date: Mon, 5 Jul 2021 10:17:59 +0200 Subject: [PATCH 190/272] target/i386: Added MSRPM and IOPM size check The address of the last entry in the MSRPM and in the IOPM must be smaller than the largest physical address. (APM2 15.10-15.11) Signed-off-by: Lara Lazier Message-Id: <20210705081802.18960-2-laramglazier@gmail.com> Signed-off-by: Paolo Bonzini --- target/i386/svm.h | 3 +++ target/i386/tcg/sysemu/svm_helper.c | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/target/i386/svm.h b/target/i386/svm.h index 5098733053..adc058dc76 100644 --- a/target/i386/svm.h +++ b/target/i386/svm.h @@ -137,6 +137,9 @@ #define SVM_CR0_RESERVED_MASK 0xffffffff00000000U +#define SVM_MSRPM_SIZE (1ULL << 13) +#define SVM_IOPM_SIZE ((1ULL << 13) + 1) + struct QEMU_PACKED vmcb_control_area { uint16_t intercept_cr_read; uint16_t intercept_cr_write; diff --git a/target/i386/tcg/sysemu/svm_helper.c b/target/i386/tcg/sysemu/svm_helper.c index 1c2dbc1862..fa701829e5 100644 --- a/target/i386/tcg/sysemu/svm_helper.c +++ b/target/i386/tcg/sysemu/svm_helper.c @@ -68,6 +68,7 @@ static inline void svm_load_seg_cache(CPUX86State *env, hwaddr addr, void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) { CPUState *cs = env_cpu(env); + X86CPU *cpu = env_archcpu(env); target_ulong addr; uint64_t nested_ctl; uint32_t event_inj; @@ -159,6 +160,20 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) asid = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.asid)); + uint64_t msrpm_base_pa = x86_ldq_phys(cs, env->vm_vmcb + + offsetof(struct vmcb, + control.msrpm_base_pa)); + uint64_t iopm_base_pa = x86_ldq_phys(cs, env->vm_vmcb + + offsetof(struct vmcb, control.iopm_base_pa)); + + if ((msrpm_base_pa & ~0xfff) >= (1ull << cpu->phys_bits) - SVM_MSRPM_SIZE) { + cpu_vmexit(env, SVM_EXIT_ERR, 0, GETPC()); + } + + if ((iopm_base_pa & ~0xfff) >= (1ull << cpu->phys_bits) - SVM_IOPM_SIZE) { + cpu_vmexit(env, SVM_EXIT_ERR, 0, GETPC()); + } + env->nested_pg_mode = 0; if (!cpu_svm_has_intercept(env, SVM_EXIT_VMRUN)) { From acf23ffb58322179841cb68ff0fd595fede59618 Mon Sep 17 00:00:00 2001 From: Lara Lazier Date: Mon, 5 Jul 2021 10:18:00 +0200 Subject: [PATCH 191/272] target/i386: Added DR6 and DR7 consistency checks DR6[63:32] and DR7[63:32] are reserved and need to be zero. (AMD64 Architecture Programmer's Manual, V2, 15.5) Signed-off-by: Lara Lazier Message-Id: <20210705081802.18960-3-laramglazier@gmail.com> [Ignore for 32-bit builds. - Paolo] Signed-off-by: Paolo Bonzini --- target/i386/svm.h | 2 ++ target/i386/tcg/sysemu/svm_helper.c | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/target/i386/svm.h b/target/i386/svm.h index adc058dc76..4bde9f3475 100644 --- a/target/i386/svm.h +++ b/target/i386/svm.h @@ -140,6 +140,8 @@ #define SVM_MSRPM_SIZE (1ULL << 13) #define SVM_IOPM_SIZE ((1ULL << 13) + 1) +#define SVM_DR_RESERVED_MASK 0xffffffff00000000ULL + struct QEMU_PACKED vmcb_control_area { uint16_t intercept_cr_read; uint16_t intercept_cr_write; diff --git a/target/i386/tcg/sysemu/svm_helper.c b/target/i386/tcg/sysemu/svm_helper.c index fa701829e5..047f31628e 100644 --- a/target/i386/tcg/sysemu/svm_helper.c +++ b/target/i386/tcg/sysemu/svm_helper.c @@ -269,7 +269,14 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) env->dr[6] = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.dr6)); - /* FIXME: guest state consistency checks */ +#ifdef TARGET_X86_64 + if (env->dr[6] & SVM_DR_RESERVED_MASK) { + cpu_vmexit(env, SVM_EXIT_ERR, 0, GETPC()); + } + if (env->dr[7] & SVM_DR_RESERVED_MASK) { + cpu_vmexit(env, SVM_EXIT_ERR, 0, GETPC()); + } +#endif switch (x86_ldub_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.tlb_ctl))) { From 533883fd7ec18a99b28815583bce6f78b2c9643b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 6 Jul 2021 17:53:29 +0200 Subject: [PATCH 192/272] target/i386: fix exceptions for MOV to DR Use raise_exception_ra (without error code) when raising the illegal opcode operation; raise #GP when setting bits 63:32 of DR6 or DR7. Move helper_get_dr to sysemu/ since it is a privileged instruction that is not needed on user-mode emulators. Signed-off-by: Paolo Bonzini --- target/i386/cpu.h | 2 ++ target/i386/helper.h | 3 +- target/i386/svm.h | 2 -- target/i386/tcg/bpt_helper.c | 21 ------------- target/i386/tcg/sysemu/bpt_helper.c | 47 ++++++++++++++++++----------- target/i386/tcg/sysemu/svm_helper.c | 4 +-- target/i386/tcg/translate.c | 1 + 7 files changed, 35 insertions(+), 45 deletions(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 0f7ddbfeae..8f3747dd28 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -268,6 +268,8 @@ typedef enum X86Seg { #define DR7_TYPE_IO_RW 0x2 #define DR7_TYPE_DATA_RW 0x3 +#define DR_RESERVED_MASK 0xffffffff00000000ULL + #define PG_PRESENT_BIT 0 #define PG_RW_BIT 1 #define PG_USER_BIT 2 diff --git a/target/i386/helper.h b/target/i386/helper.h index f3d8c3f949..574ff75615 100644 --- a/target/i386/helper.h +++ b/target/i386/helper.h @@ -46,9 +46,8 @@ DEF_HELPER_1(clts, void, env) #ifndef CONFIG_USER_ONLY DEF_HELPER_FLAGS_3(set_dr, TCG_CALL_NO_WG, void, env, int, tl) -#endif /* !CONFIG_USER_ONLY */ - DEF_HELPER_FLAGS_2(get_dr, TCG_CALL_NO_WG, tl, env, int) +#endif /* !CONFIG_USER_ONLY */ DEF_HELPER_1(sysenter, void, env) DEF_HELPER_2(sysexit, void, env, int) diff --git a/target/i386/svm.h b/target/i386/svm.h index 4bde9f3475..adc058dc76 100644 --- a/target/i386/svm.h +++ b/target/i386/svm.h @@ -140,8 +140,6 @@ #define SVM_MSRPM_SIZE (1ULL << 13) #define SVM_IOPM_SIZE ((1ULL << 13) + 1) -#define SVM_DR_RESERVED_MASK 0xffffffff00000000ULL - struct QEMU_PACKED vmcb_control_area { uint16_t intercept_cr_read; uint16_t intercept_cr_write; diff --git a/target/i386/tcg/bpt_helper.c b/target/i386/tcg/bpt_helper.c index 83cd89581e..b6c1fff16e 100644 --- a/target/i386/tcg/bpt_helper.c +++ b/target/i386/tcg/bpt_helper.c @@ -37,24 +37,3 @@ void helper_rechecking_single_step(CPUX86State *env) helper_single_step(env); } } - -target_ulong helper_get_dr(CPUX86State *env, int reg) -{ - switch (reg) { - case 0: case 1: case 2: case 3: case 6: case 7: - return env->dr[reg]; - case 4: - if (env->cr[4] & CR4_DE_MASK) { - break; - } else { - return env->dr[6]; - } - case 5: - if (env->cr[4] & CR4_DE_MASK) { - break; - } else { - return env->dr[7]; - } - } - raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC()); -} diff --git a/target/i386/tcg/sysemu/bpt_helper.c b/target/i386/tcg/sysemu/bpt_helper.c index 9bdf7e170b..805118c6b5 100644 --- a/target/i386/tcg/sysemu/bpt_helper.c +++ b/target/i386/tcg/sysemu/bpt_helper.c @@ -234,10 +234,30 @@ void breakpoint_handler(CPUState *cs) } } +target_ulong helper_get_dr(CPUX86State *env, int reg) +{ + if (reg >= 4 && reg < 6) { + if (env->cr[4] & CR4_DE_MASK) { + raise_exception_ra(env, EXCP06_ILLOP, GETPC()); + } else { + reg += 2; + } + } + + return env->dr[reg]; +} + void helper_set_dr(CPUX86State *env, int reg, target_ulong t0) { - switch (reg) { - case 0: case 1: case 2: case 3: + if (reg >= 4 && reg < 6) { + if (env->cr[4] & CR4_DE_MASK) { + raise_exception_ra(env, EXCP06_ILLOP, GETPC()); + } else { + reg += 2; + } + } + + if (reg < 4) { if (hw_breakpoint_enabled(env->dr[7], reg) && hw_breakpoint_type(env->dr[7], reg) != DR7_TYPE_IO_RW) { hw_breakpoint_remove(env, reg); @@ -246,25 +266,16 @@ void helper_set_dr(CPUX86State *env, int reg, target_ulong t0) } else { env->dr[reg] = t0; } - return; - case 4: - if (env->cr[4] & CR4_DE_MASK) { - break; + } else { + if (t0 & DR_RESERVED_MASK) { + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } - /* fallthru */ - case 6: - env->dr[6] = t0 | DR6_FIXED_1; - return; - case 5: - if (env->cr[4] & CR4_DE_MASK) { - break; + if (reg == 6) { + env->dr[6] = t0 | DR6_FIXED_1; + } else { + cpu_x86_update_dr7(env, t0); } - /* fallthru */ - case 7: - cpu_x86_update_dr7(env, t0); - return; } - raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC()); } /* Check if Port I/O is trapped by a breakpoint. */ diff --git a/target/i386/tcg/sysemu/svm_helper.c b/target/i386/tcg/sysemu/svm_helper.c index 047f31628e..00618cff23 100644 --- a/target/i386/tcg/sysemu/svm_helper.c +++ b/target/i386/tcg/sysemu/svm_helper.c @@ -270,10 +270,10 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) env->vm_vmcb + offsetof(struct vmcb, save.dr6)); #ifdef TARGET_X86_64 - if (env->dr[6] & SVM_DR_RESERVED_MASK) { + if (env->dr[6] & DR_RESERVED_MASK) { cpu_vmexit(env, SVM_EXIT_ERR, 0, GETPC()); } - if (env->dr[7] & SVM_DR_RESERVED_MASK) { + if (env->dr[7] & DR_RESERVED_MASK) { cpu_vmexit(env, SVM_EXIT_ERR, 0, GETPC()); } #endif diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index b21873ed23..9e445b9bf0 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -206,6 +206,7 @@ STUB_HELPER(outw, TCGv_env env, TCGv_i32 port, TCGv_i32 val) STUB_HELPER(outl, TCGv_env env, TCGv_i32 port, TCGv_i32 val) STUB_HELPER(rdmsr, TCGv_env env) STUB_HELPER(read_crN, TCGv ret, TCGv_env env, TCGv_i32 reg) +STUB_HELPER(get_dr, TCGv ret, TCGv_env env, TCGv_i32 reg) STUB_HELPER(set_dr, TCGv_env env, TCGv_i32 reg, TCGv val) STUB_HELPER(stgi, TCGv_env env) STUB_HELPER(svm_check_intercept, TCGv_env env, TCGv_i32 type) From 838e37007cae48d32102e2f2addb2473138a98df Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 5 Jul 2021 19:17:38 +0200 Subject: [PATCH 193/272] vl: fix leak of qdict_crumple return value Coverity reports that qemu_parse_config_group is returning without unrefing the "crumpled" dictionary in case its top level item is a list. But actually the contract with qemu_record_config_group is the same as for qemu_parse_config_group itself: if those function need to stash the dictionary they get, they have to take a reference themselves (currently this is never the case for either function). Therefore, just add an unconditional qobject_unref(crumpled) to qemu_parse_config_group. Reported-by: Peter Maydell Signed-off-by: Paolo Bonzini --- softmmu/vl.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/softmmu/vl.c b/softmmu/vl.c index 2004d57108..4df1496101 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -2193,12 +2193,17 @@ static void qemu_parse_config_group(const char *group, QDict *qdict, if (!crumpled) { return; } - if (qobject_type(crumpled) != QTYPE_QDICT) { - assert(qobject_type(crumpled) == QTYPE_QLIST); + switch (qobject_type(crumpled)) { + case QTYPE_QDICT: + qemu_record_config_group(group, qobject_to(QDict, crumpled), false, errp); + break; + case QTYPE_QLIST: error_setg(errp, "Lists cannot be at top level of a configuration section"); - return; + break; + default: + g_assert_not_reached(); } - qemu_record_config_group(group, qobject_to(QDict, crumpled), false, errp); + qobject_unref(crumpled); } static void qemu_read_default_config_file(Error **errp) From 904ad5ec1583145ef411acb2dec63beeb12ea721 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 7 Jul 2021 16:35:26 +0200 Subject: [PATCH 194/272] meson: switch function tests from compilation to linking Some tests for glibc functions cause compilation to emit warnings but still succeed even if the function is not there. Therefore, change from cc.compiles to cc.links. Reported-by: Richard Zak Signed-off-by: Paolo Bonzini --- meson.build | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/meson.build b/meson.build index d9aa2d7820..5a56e3fe2f 100644 --- a/meson.build +++ b/meson.build @@ -1340,10 +1340,10 @@ config_host_data.set('HAVE_STRUCT_STAT_ST_ATIM', cc.has_member('struct stat', 'st_atim', prefix: '#include ')) -config_host_data.set('CONFIG_EVENTFD', cc.compiles(''' +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.compiles(gnu_source_prefix + ''' +config_host_data.set('CONFIG_FDATASYNC', cc.links(gnu_source_prefix + ''' #include int main(void) { #if defined(_POSIX_SYNCHRONIZED_IO) && _POSIX_SYNCHRONIZED_IO > 0 @@ -1352,22 +1352,22 @@ config_host_data.set('CONFIG_FDATASYNC', cc.compiles(gnu_source_prefix + ''' #error Not supported #endif }''')) -config_host_data.set('CONFIG_MADVISE', cc.compiles(gnu_source_prefix + ''' +config_host_data.set('CONFIG_MADVISE', cc.links(gnu_source_prefix + ''' #include #include #include int main(void) { return madvise(NULL, 0, MADV_DONTNEED); }''')) -config_host_data.set('CONFIG_MEMFD', cc.compiles(gnu_source_prefix + ''' +config_host_data.set('CONFIG_MEMFD', cc.links(gnu_source_prefix + ''' #include int main(void) { return memfd_create("foo", MFD_ALLOW_SEALING); }''')) -config_host_data.set('CONFIG_OPEN_BY_HANDLE', cc.compiles(gnu_source_prefix + ''' +config_host_data.set('CONFIG_OPEN_BY_HANDLE', cc.links(gnu_source_prefix + ''' #include #if !defined(AT_EMPTY_PATH) # error missing definition #else int main(void) { struct file_handle fh; return open_by_handle_at(0, &fh, 0); } #endif''')) -config_host_data.set('CONFIG_PIPE2', cc.compiles(gnu_source_prefix + ''' +config_host_data.set('CONFIG_PIPE2', cc.links(gnu_source_prefix + ''' #include #include @@ -1376,16 +1376,16 @@ config_host_data.set('CONFIG_PIPE2', cc.compiles(gnu_source_prefix + ''' int pipefd[2]; return pipe2(pipefd, O_CLOEXEC); }''')) -config_host_data.set('CONFIG_POSIX_MADVISE', cc.compiles(gnu_source_prefix + ''' +config_host_data.set('CONFIG_POSIX_MADVISE', cc.links(gnu_source_prefix + ''' #include #include int main(void) { return posix_madvise(NULL, 0, POSIX_MADV_DONTNEED); }''')) -config_host_data.set('CONFIG_SIGNALFD', cc.compiles(gnu_source_prefix + ''' +config_host_data.set('CONFIG_SIGNALFD', cc.links(gnu_source_prefix + ''' #include #include #include int main(void) { return syscall(SYS_signalfd, -1, NULL, _NSIG / 8); }''')) -config_host_data.set('CONFIG_SPLICE', cc.compiles(gnu_source_prefix + ''' +config_host_data.set('CONFIG_SPLICE', cc.links(gnu_source_prefix + ''' #include #include #include From f4063f9c319e3924b0c6d09dfe43e94d01253ee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 7 Jul 2021 14:17:40 +0100 Subject: [PATCH 195/272] meson: Introduce target-specific Kconfig MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a target-specific Kconfig. We need the definitions in Kconfig so the minikconf tool can verify they exits. However CONFIG_FOO is only enabled for target foo via the meson.build rules. Two architecture have a particularity, ARM and MIPS. As their translators have been split you can potentially build a plain 32 bit build along with a 64-bit version including the 32-bit subset. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20210131111316.232778-6-f4bug@amsat.org> Signed-off-by: Alex Bennée Reviewed-by: Thomas Huth Message-Id: <20210707131744.26027-2-alex.bennee@linaro.org> Signed-off-by: Paolo Bonzini --- Kconfig | 1 + meson.build | 3 ++- target/Kconfig | 19 +++++++++++++++++++ target/alpha/Kconfig | 2 ++ target/arm/Kconfig | 6 ++++++ target/avr/Kconfig | 2 ++ target/cris/Kconfig | 2 ++ target/hppa/Kconfig | 2 ++ target/i386/Kconfig | 5 +++++ target/m68k/Kconfig | 2 ++ target/microblaze/Kconfig | 2 ++ target/mips/Kconfig | 6 ++++++ target/nios2/Kconfig | 2 ++ target/openrisc/Kconfig | 2 ++ target/ppc/Kconfig | 5 +++++ target/riscv/Kconfig | 5 +++++ target/rx/Kconfig | 2 ++ target/s390x/Kconfig | 2 ++ target/sh4/Kconfig | 2 ++ target/sparc/Kconfig | 5 +++++ target/tricore/Kconfig | 2 ++ target/xtensa/Kconfig | 2 ++ 22 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 target/Kconfig create mode 100644 target/alpha/Kconfig create mode 100644 target/arm/Kconfig create mode 100644 target/avr/Kconfig create mode 100644 target/cris/Kconfig create mode 100644 target/hppa/Kconfig create mode 100644 target/i386/Kconfig create mode 100644 target/m68k/Kconfig create mode 100644 target/microblaze/Kconfig create mode 100644 target/mips/Kconfig create mode 100644 target/nios2/Kconfig create mode 100644 target/openrisc/Kconfig create mode 100644 target/ppc/Kconfig create mode 100644 target/riscv/Kconfig create mode 100644 target/rx/Kconfig create mode 100644 target/s390x/Kconfig create mode 100644 target/sh4/Kconfig create mode 100644 target/sparc/Kconfig create mode 100644 target/tricore/Kconfig create mode 100644 target/xtensa/Kconfig diff --git a/Kconfig b/Kconfig index d52ebd839b..fb6a24a2de 100644 --- a/Kconfig +++ b/Kconfig @@ -1,5 +1,6 @@ source Kconfig.host source backends/Kconfig source accel/Kconfig +source target/Kconfig source hw/Kconfig source semihosting/Kconfig diff --git a/meson.build b/meson.build index 5a56e3fe2f..d82f7a789d 100644 --- a/meson.build +++ b/meson.build @@ -1604,7 +1604,8 @@ foreach target : target_dirs command: [minikconf, get_option('default_devices') ? '--defconfig' : '--allnoconfig', config_devices_mak, '@DEPFILE@', '@INPUT@', - host_kconfig, accel_kconfig]) + host_kconfig, accel_kconfig, + 'CONFIG_' + config_target['TARGET_ARCH'].to_upper() + '=y']) config_devices_data = configuration_data() config_devices = keyval.load(config_devices_mak) diff --git a/target/Kconfig b/target/Kconfig new file mode 100644 index 0000000000..ae7f24fc66 --- /dev/null +++ b/target/Kconfig @@ -0,0 +1,19 @@ +source alpha/Kconfig +source arm/Kconfig +source avr/Kconfig +source cris/Kconfig +source hppa/Kconfig +source i386/Kconfig +source m68k/Kconfig +source microblaze/Kconfig +source mips/Kconfig +source nios2/Kconfig +source openrisc/Kconfig +source ppc/Kconfig +source riscv/Kconfig +source rx/Kconfig +source s390x/Kconfig +source sh4/Kconfig +source sparc/Kconfig +source tricore/Kconfig +source xtensa/Kconfig diff --git a/target/alpha/Kconfig b/target/alpha/Kconfig new file mode 100644 index 0000000000..267222c05b --- /dev/null +++ b/target/alpha/Kconfig @@ -0,0 +1,2 @@ +config ALPHA + bool diff --git a/target/arm/Kconfig b/target/arm/Kconfig new file mode 100644 index 0000000000..3f3394a22b --- /dev/null +++ b/target/arm/Kconfig @@ -0,0 +1,6 @@ +config ARM + bool + +config AARCH64 + bool + select ARM diff --git a/target/avr/Kconfig b/target/avr/Kconfig new file mode 100644 index 0000000000..155592d353 --- /dev/null +++ b/target/avr/Kconfig @@ -0,0 +1,2 @@ +config AVR + bool diff --git a/target/cris/Kconfig b/target/cris/Kconfig new file mode 100644 index 0000000000..3fdc309fbb --- /dev/null +++ b/target/cris/Kconfig @@ -0,0 +1,2 @@ +config CRIS + bool diff --git a/target/hppa/Kconfig b/target/hppa/Kconfig new file mode 100644 index 0000000000..395a35d799 --- /dev/null +++ b/target/hppa/Kconfig @@ -0,0 +1,2 @@ +config HPPA + bool diff --git a/target/i386/Kconfig b/target/i386/Kconfig new file mode 100644 index 0000000000..ce6968906e --- /dev/null +++ b/target/i386/Kconfig @@ -0,0 +1,5 @@ +config I386 + bool + +config X86_64 + bool diff --git a/target/m68k/Kconfig b/target/m68k/Kconfig new file mode 100644 index 0000000000..23debad519 --- /dev/null +++ b/target/m68k/Kconfig @@ -0,0 +1,2 @@ +config M68K + bool diff --git a/target/microblaze/Kconfig b/target/microblaze/Kconfig new file mode 100644 index 0000000000..a5410d9218 --- /dev/null +++ b/target/microblaze/Kconfig @@ -0,0 +1,2 @@ +config MICROBLAZE + bool diff --git a/target/mips/Kconfig b/target/mips/Kconfig new file mode 100644 index 0000000000..6adf145354 --- /dev/null +++ b/target/mips/Kconfig @@ -0,0 +1,6 @@ +config MIPS + bool + +config MIPS64 + bool + select MIPS diff --git a/target/nios2/Kconfig b/target/nios2/Kconfig new file mode 100644 index 0000000000..1529ab8950 --- /dev/null +++ b/target/nios2/Kconfig @@ -0,0 +1,2 @@ +config NIOS2 + bool diff --git a/target/openrisc/Kconfig b/target/openrisc/Kconfig new file mode 100644 index 0000000000..e0da4ac1df --- /dev/null +++ b/target/openrisc/Kconfig @@ -0,0 +1,2 @@ +config OPENRISC + bool diff --git a/target/ppc/Kconfig b/target/ppc/Kconfig new file mode 100644 index 0000000000..3ff152051a --- /dev/null +++ b/target/ppc/Kconfig @@ -0,0 +1,5 @@ +config PPC + bool + +config PPC64 + bool diff --git a/target/riscv/Kconfig b/target/riscv/Kconfig new file mode 100644 index 0000000000..b9e5932f13 --- /dev/null +++ b/target/riscv/Kconfig @@ -0,0 +1,5 @@ +config RISCV32 + bool + +config RISCV64 + bool diff --git a/target/rx/Kconfig b/target/rx/Kconfig new file mode 100644 index 0000000000..aceb5ed28f --- /dev/null +++ b/target/rx/Kconfig @@ -0,0 +1,2 @@ +config RX + bool diff --git a/target/s390x/Kconfig b/target/s390x/Kconfig new file mode 100644 index 0000000000..72da48136c --- /dev/null +++ b/target/s390x/Kconfig @@ -0,0 +1,2 @@ +config S390X + bool diff --git a/target/sh4/Kconfig b/target/sh4/Kconfig new file mode 100644 index 0000000000..2397c86028 --- /dev/null +++ b/target/sh4/Kconfig @@ -0,0 +1,2 @@ +config SH4 + bool diff --git a/target/sparc/Kconfig b/target/sparc/Kconfig new file mode 100644 index 0000000000..70cc0f3a21 --- /dev/null +++ b/target/sparc/Kconfig @@ -0,0 +1,5 @@ +config SPARC + bool + +config SPARC64 + bool diff --git a/target/tricore/Kconfig b/target/tricore/Kconfig new file mode 100644 index 0000000000..9313409309 --- /dev/null +++ b/target/tricore/Kconfig @@ -0,0 +1,2 @@ +config TRICORE + bool diff --git a/target/xtensa/Kconfig b/target/xtensa/Kconfig new file mode 100644 index 0000000000..a3c8dc7f6d --- /dev/null +++ b/target/xtensa/Kconfig @@ -0,0 +1,2 @@ +config XTENSA + bool From d064c19d749bc839ed243d584ba70ba65a999885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 7 Jul 2021 14:17:41 +0100 Subject: [PATCH 196/272] hw/arm: add dependency on OR_IRQ for XLNX_VERSAL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need this functionality due to: /* XRAM IRQs get ORed into a single line. */ object_initialize_child(OBJECT(s), "xram-irq-orgate", &s->lpd.xram.irq_orgate, TYPE_OR_IRQ); Fixes: a55b441b2ca ("hw/arm: versal: Add support for the XRAMs") Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson Reviewed-by: Edgar E. Iglesias Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20210707131744.26027-3-alex.bennee@linaro.org> Signed-off-by: Paolo Bonzini --- hw/arm/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 647b5c8b43..528f71bb9d 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -368,6 +368,7 @@ config XLNX_VERSAL select UNIMP select XLNX_ZDMA select XLNX_ZYNQMP + select OR_IRQ config NPCM7XX bool From cd43648a44f7288261773477d926f60f09abf977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 7 Jul 2021 14:17:42 +0100 Subject: [PATCH 197/272] hw/arm: move CONFIG_V7M out of default-devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We currently select CONFIG_V7M for a bunch of our m-profile devices. The last sticking point is translate.c which cannot be compiled without expecting v7m support. Express this dependency in Kconfig rather than in default devices as a stepping stone to a fully configurable translate.c. While we are at it we also need to select ARM_COMPATIBLE_SEMIHOSTING as that is implied for M profile machines. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Acked-by: Richard Henderson Message-Id: <20210707131744.26027-4-alex.bennee@linaro.org> Signed-off-by: Paolo Bonzini --- default-configs/devices/arm-softmmu.mak | 3 --- hw/arm/Kconfig | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/default-configs/devices/arm-softmmu.mak b/default-configs/devices/arm-softmmu.mak index 0500156a0c..4114aa9e35 100644 --- a/default-configs/devices/arm-softmmu.mak +++ b/default-configs/devices/arm-softmmu.mak @@ -1,8 +1,5 @@ # Default configuration for arm-softmmu -# TODO: ARM_V7M is currently always required - make this more flexible! -CONFIG_ARM_V7M=y - # CONFIG_PCI_DEVICES=n # CONFIG_TEST_DEVICES=n diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 528f71bb9d..062fe94b64 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -296,7 +296,10 @@ config ZYNQ config ARM_V7M bool + # currently v7M must be included in a TCG build due to translate.c + default y if TCG && (ARM || AARCH64) select PTIMER + select ARM_COMPATIBLE_SEMIHOSTING config ALLWINNER_A10 bool From 812b31d3f91507160c367440c17715b62d5e0869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 7 Jul 2021 14:17:43 +0100 Subject: [PATCH 198/272] configs: rename default-configs to configs and reorganise MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation for offering variation to our build configurations lets move everything and rename it to default. Common included base configs are also renamed. During the cleanup the stale usb.mak and pci.mak references were removed from MAINTAINERS. Signed-off-by: Alex Bennée Cc: Paolo Bonzini Reviewed-by: Richard Henderson Message-Id: <20210707131744.26027-5-alex.bennee@linaro.org> Signed-off-by: Paolo Bonzini --- MAINTAINERS | 22 +++++++++---------- .../devices/aarch64-softmmu/default.mak | 2 +- .../devices/alpha-softmmu/default.mak | 0 .../devices/arm-softmmu/default.mak | 0 .../devices/avr-softmmu/default.mak | 0 .../devices/cris-softmmu/default.mak | 0 .../devices/hppa-softmmu/default.mak | 0 .../devices/i386-softmmu/default.mak | 0 .../devices/m68k-softmmu/default.mak | 0 .../devices/microblaze-softmmu/default.mak | 0 .../devices/microblazeel-softmmu/default.mak | 2 +- .../devices/mips-softmmu/common.mak | 0 .../devices/mips-softmmu/default.mak | 2 +- .../devices/mips64-softmmu/default.mak | 2 +- .../devices/mips64el-softmmu/default.mak | 2 +- .../devices/mipsel-softmmu/default.mak | 2 +- .../devices/nios2-softmmu/default.mak | 0 .../devices/or1k-softmmu/default.mak | 0 .../devices/ppc-softmmu/default.mak | 0 .../devices/ppc64-softmmu/default.mak | 2 +- .../devices/riscv32-softmmu/default.mak | 0 .../devices/riscv64-softmmu/default.mak | 0 .../devices/rx-softmmu/default.mak | 0 .../devices/s390x-softmmu/default.mak | 0 .../devices/sh4-softmmu/default.mak | 0 .../devices/sh4eb-softmmu/default.mak | 2 +- .../devices/sparc-softmmu/default.mak | 0 .../devices/sparc64-softmmu/default.mak | 0 .../devices/tricore-softmmu/default.mak | 0 .../devices/x86_64-softmmu/default.mak | 2 +- .../devices/xtensa-softmmu/default.mak | 0 configs/devices/xtensaeb-softmmu/default.mak | 3 +++ .../targets/aarch64-linux-user.mak | 0 .../targets/aarch64-softmmu.mak | 0 .../targets/aarch64_be-linux-user.mak | 0 .../targets/alpha-linux-user.mak | 0 .../targets/alpha-softmmu.mak | 0 .../targets/arm-linux-user.mak | 0 .../targets/arm-softmmu.mak | 0 .../targets/armeb-linux-user.mak | 0 .../targets/avr-softmmu.mak | 0 .../targets/cris-linux-user.mak | 0 .../targets/cris-softmmu.mak | 0 .../targets/hexagon-linux-user.mak | 0 .../targets/hppa-linux-user.mak | 0 .../targets/hppa-softmmu.mak | 0 .../targets/i386-bsd-user.mak | 0 .../targets/i386-linux-user.mak | 0 .../targets/i386-softmmu.mak | 0 .../targets/m68k-linux-user.mak | 0 .../targets/m68k-softmmu.mak | 0 .../targets/microblaze-linux-user.mak | 0 .../targets/microblaze-softmmu.mak | 0 .../targets/microblazeel-linux-user.mak | 0 .../targets/microblazeel-softmmu.mak | 0 .../targets/mips-linux-user.mak | 0 .../targets/mips-softmmu.mak | 0 .../targets/mips64-linux-user.mak | 0 .../targets/mips64-softmmu.mak | 0 .../targets/mips64el-linux-user.mak | 0 .../targets/mips64el-softmmu.mak | 0 .../targets/mipsel-linux-user.mak | 0 .../targets/mipsel-softmmu.mak | 0 .../targets/mipsn32-linux-user.mak | 0 .../targets/mipsn32el-linux-user.mak | 0 .../targets/nios2-linux-user.mak | 0 .../targets/nios2-softmmu.mak | 0 .../targets/or1k-linux-user.mak | 0 .../targets/or1k-softmmu.mak | 0 .../targets/ppc-linux-user.mak | 0 .../targets/ppc-softmmu.mak | 0 .../targets/ppc64-linux-user.mak | 0 .../targets/ppc64-softmmu.mak | 0 .../targets/ppc64abi32-linux-user.mak | 0 .../targets/ppc64le-linux-user.mak | 0 .../targets/riscv32-linux-user.mak | 0 .../targets/riscv32-softmmu.mak | 0 .../targets/riscv64-linux-user.mak | 0 .../targets/riscv64-softmmu.mak | 0 .../targets/rx-softmmu.mak | 0 .../targets/s390x-linux-user.mak | 0 .../targets/s390x-softmmu.mak | 0 .../targets/sh4-linux-user.mak | 0 .../targets/sh4-softmmu.mak | 0 .../targets/sh4eb-linux-user.mak | 0 .../targets/sh4eb-softmmu.mak | 0 .../targets/sparc-linux-user.mak | 0 .../targets/sparc-softmmu.mak | 0 .../targets/sparc32plus-linux-user.mak | 0 .../targets/sparc64-linux-user.mak | 0 .../targets/sparc64-softmmu.mak | 0 .../targets/tricore-softmmu.mak | 0 .../targets/x86_64-bsd-user.mak | 0 .../targets/x86_64-linux-user.mak | 0 .../targets/x86_64-softmmu.mak | 0 .../targets/xtensa-linux-user.mak | 0 .../targets/xtensa-softmmu.mak | 0 .../targets/xtensaeb-linux-user.mak | 0 .../targets/xtensaeb-softmmu.mak | 0 configure | 6 ++--- default-configs/devices/xtensaeb-softmmu.mak | 3 --- meson.build | 4 ++-- tests/Makefile.include | 2 +- 103 files changed, 28 insertions(+), 30 deletions(-) rename default-configs/devices/aarch64-softmmu.mak => configs/devices/aarch64-softmmu/default.mak (82%) rename default-configs/devices/alpha-softmmu.mak => configs/devices/alpha-softmmu/default.mak (100%) rename default-configs/devices/arm-softmmu.mak => configs/devices/arm-softmmu/default.mak (100%) rename default-configs/devices/avr-softmmu.mak => configs/devices/avr-softmmu/default.mak (100%) rename default-configs/devices/cris-softmmu.mak => configs/devices/cris-softmmu/default.mak (100%) rename default-configs/devices/hppa-softmmu.mak => configs/devices/hppa-softmmu/default.mak (100%) rename default-configs/devices/i386-softmmu.mak => configs/devices/i386-softmmu/default.mak (100%) rename default-configs/devices/m68k-softmmu.mak => configs/devices/m68k-softmmu/default.mak (100%) rename default-configs/devices/microblaze-softmmu.mak => configs/devices/microblaze-softmmu/default.mak (100%) rename default-configs/devices/microblazeel-softmmu.mak => configs/devices/microblazeel-softmmu/default.mak (54%) rename default-configs/devices/mips-softmmu-common.mak => configs/devices/mips-softmmu/common.mak (100%) rename default-configs/devices/mips-softmmu.mak => configs/devices/mips-softmmu/default.mak (56%) rename default-configs/devices/mips64-softmmu.mak => configs/devices/mips64-softmmu/default.mak (62%) rename default-configs/devices/mips64el-softmmu.mak => configs/devices/mips64el-softmmu/default.mak (88%) rename default-configs/devices/mipsel-softmmu.mak => configs/devices/mipsel-softmmu/default.mak (55%) rename default-configs/devices/nios2-softmmu.mak => configs/devices/nios2-softmmu/default.mak (100%) rename default-configs/devices/or1k-softmmu.mak => configs/devices/or1k-softmmu/default.mak (100%) rename default-configs/devices/ppc-softmmu.mak => configs/devices/ppc-softmmu/default.mak (100%) rename default-configs/devices/ppc64-softmmu.mak => configs/devices/ppc64-softmmu/default.mak (79%) rename default-configs/devices/riscv32-softmmu.mak => configs/devices/riscv32-softmmu/default.mak (100%) rename default-configs/devices/riscv64-softmmu.mak => configs/devices/riscv64-softmmu/default.mak (100%) rename default-configs/devices/rx-softmmu.mak => configs/devices/rx-softmmu/default.mak (100%) rename default-configs/devices/s390x-softmmu.mak => configs/devices/s390x-softmmu/default.mak (100%) rename default-configs/devices/sh4-softmmu.mak => configs/devices/sh4-softmmu/default.mak (100%) rename default-configs/devices/sh4eb-softmmu.mak => configs/devices/sh4eb-softmmu/default.mak (55%) rename default-configs/devices/sparc-softmmu.mak => configs/devices/sparc-softmmu/default.mak (100%) rename default-configs/devices/sparc64-softmmu.mak => configs/devices/sparc64-softmmu/default.mak (100%) rename default-configs/devices/tricore-softmmu.mak => configs/devices/tricore-softmmu/default.mak (100%) rename default-configs/devices/x86_64-softmmu.mak => configs/devices/x86_64-softmmu/default.mak (55%) rename default-configs/devices/xtensa-softmmu.mak => configs/devices/xtensa-softmmu/default.mak (100%) create mode 100644 configs/devices/xtensaeb-softmmu/default.mak rename {default-configs => configs}/targets/aarch64-linux-user.mak (100%) rename {default-configs => configs}/targets/aarch64-softmmu.mak (100%) rename {default-configs => configs}/targets/aarch64_be-linux-user.mak (100%) rename {default-configs => configs}/targets/alpha-linux-user.mak (100%) rename {default-configs => configs}/targets/alpha-softmmu.mak (100%) rename {default-configs => configs}/targets/arm-linux-user.mak (100%) rename {default-configs => configs}/targets/arm-softmmu.mak (100%) rename {default-configs => configs}/targets/armeb-linux-user.mak (100%) rename {default-configs => configs}/targets/avr-softmmu.mak (100%) rename {default-configs => configs}/targets/cris-linux-user.mak (100%) rename {default-configs => configs}/targets/cris-softmmu.mak (100%) rename {default-configs => configs}/targets/hexagon-linux-user.mak (100%) rename {default-configs => configs}/targets/hppa-linux-user.mak (100%) rename {default-configs => configs}/targets/hppa-softmmu.mak (100%) rename {default-configs => configs}/targets/i386-bsd-user.mak (100%) rename {default-configs => configs}/targets/i386-linux-user.mak (100%) rename {default-configs => configs}/targets/i386-softmmu.mak (100%) rename {default-configs => configs}/targets/m68k-linux-user.mak (100%) rename {default-configs => configs}/targets/m68k-softmmu.mak (100%) rename {default-configs => configs}/targets/microblaze-linux-user.mak (100%) rename {default-configs => configs}/targets/microblaze-softmmu.mak (100%) rename {default-configs => configs}/targets/microblazeel-linux-user.mak (100%) rename {default-configs => configs}/targets/microblazeel-softmmu.mak (100%) rename {default-configs => configs}/targets/mips-linux-user.mak (100%) rename {default-configs => configs}/targets/mips-softmmu.mak (100%) rename {default-configs => configs}/targets/mips64-linux-user.mak (100%) rename {default-configs => configs}/targets/mips64-softmmu.mak (100%) rename {default-configs => configs}/targets/mips64el-linux-user.mak (100%) rename {default-configs => configs}/targets/mips64el-softmmu.mak (100%) rename {default-configs => configs}/targets/mipsel-linux-user.mak (100%) rename {default-configs => configs}/targets/mipsel-softmmu.mak (100%) rename {default-configs => configs}/targets/mipsn32-linux-user.mak (100%) rename {default-configs => configs}/targets/mipsn32el-linux-user.mak (100%) rename {default-configs => configs}/targets/nios2-linux-user.mak (100%) rename {default-configs => configs}/targets/nios2-softmmu.mak (100%) rename {default-configs => configs}/targets/or1k-linux-user.mak (100%) rename {default-configs => configs}/targets/or1k-softmmu.mak (100%) rename {default-configs => configs}/targets/ppc-linux-user.mak (100%) rename {default-configs => configs}/targets/ppc-softmmu.mak (100%) rename {default-configs => configs}/targets/ppc64-linux-user.mak (100%) rename {default-configs => configs}/targets/ppc64-softmmu.mak (100%) rename {default-configs => configs}/targets/ppc64abi32-linux-user.mak (100%) rename {default-configs => configs}/targets/ppc64le-linux-user.mak (100%) rename {default-configs => configs}/targets/riscv32-linux-user.mak (100%) rename {default-configs => configs}/targets/riscv32-softmmu.mak (100%) rename {default-configs => configs}/targets/riscv64-linux-user.mak (100%) rename {default-configs => configs}/targets/riscv64-softmmu.mak (100%) rename {default-configs => configs}/targets/rx-softmmu.mak (100%) rename {default-configs => configs}/targets/s390x-linux-user.mak (100%) rename {default-configs => configs}/targets/s390x-softmmu.mak (100%) rename {default-configs => configs}/targets/sh4-linux-user.mak (100%) rename {default-configs => configs}/targets/sh4-softmmu.mak (100%) rename {default-configs => configs}/targets/sh4eb-linux-user.mak (100%) rename {default-configs => configs}/targets/sh4eb-softmmu.mak (100%) rename {default-configs => configs}/targets/sparc-linux-user.mak (100%) rename {default-configs => configs}/targets/sparc-softmmu.mak (100%) rename {default-configs => configs}/targets/sparc32plus-linux-user.mak (100%) rename {default-configs => configs}/targets/sparc64-linux-user.mak (100%) rename {default-configs => configs}/targets/sparc64-softmmu.mak (100%) rename {default-configs => configs}/targets/tricore-softmmu.mak (100%) rename {default-configs => configs}/targets/x86_64-bsd-user.mak (100%) rename {default-configs => configs}/targets/x86_64-linux-user.mak (100%) rename {default-configs => configs}/targets/x86_64-softmmu.mak (100%) rename {default-configs => configs}/targets/xtensa-linux-user.mak (100%) rename {default-configs => configs}/targets/xtensa-softmmu.mak (100%) rename {default-configs => configs}/targets/xtensaeb-linux-user.mak (100%) rename {default-configs => configs}/targets/xtensaeb-softmmu.mak (100%) delete mode 100644 default-configs/devices/xtensaeb-softmmu.mak diff --git a/MAINTAINERS b/MAINTAINERS index 809830c655..cd17c72e8d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -87,7 +87,7 @@ S390 general architecture support M: Cornelia Huck M: Thomas Huth S: Supported -F: default-configs/*/s390x-softmmu.mak +F: configs/devices/s390x-softmmu/default.mak F: gdb-xml/s390*.xml F: hw/char/sclp*.[hc] F: hw/char/terminal3270.c @@ -196,7 +196,7 @@ F: target/hexagon/ F: linux-user/hexagon/ F: tests/tcg/hexagon/ F: disas/hexagon.c -F: default-configs/targets/hexagon-linux-user.mak +F: configs/targets/hexagon-linux-user/default.mak F: docker/dockerfiles/debian-hexagon-cross.docker F: docker/dockerfiles/debian-hexagon-cross.docker.d/build-toolchain.sh @@ -229,7 +229,7 @@ R: Jiaxun Yang R: Aleksandar Rikalo S: Odd Fixes F: target/mips/ -F: default-configs/*/*mips* +F: configs/devices/mips*/* F: disas/mips.c F: docs/system/cpu-models-mips.rst.inc F: hw/intc/mips_gic.c @@ -255,7 +255,7 @@ S: Maintained F: target/nios2/ F: hw/nios2/ F: disas/nios2.c -F: default-configs/*/nios2-softmmu.mak +F: configs/devices/nios2-softmmu/default.mak OpenRISC TCG CPUs M: Stafford Horne @@ -342,7 +342,7 @@ F: hw/xtensa/ F: tests/tcg/xtensa/ F: disas/xtensa.c F: include/hw/xtensa/xtensa-isa.h -F: default-configs/*/xtensa*.mak +F: configs/devices/xtensa*/default.mak TriCore TCG CPUs M: Bastian Koppelmann @@ -1041,7 +1041,7 @@ AVR Machines AVR MCUs M: Michael Rolnik S: Maintained -F: default-configs/*/avr-softmmu.mak +F: configs/devices/avr-softmmu/default.mak F: hw/avr/ F: include/hw/char/avr_usart.h F: hw/char/avr_usart.c @@ -1069,7 +1069,7 @@ HP B160L M: Richard Henderson R: Helge Deller S: Odd Fixes -F: default-configs/*/hppa-softmmu.mak +F: configs/devices/hppa-softmmu/default.mak F: hw/hppa/ F: pc-bios/hppa-firmware.img @@ -1496,7 +1496,7 @@ F: hw/s390x/ F: include/hw/s390x/ F: hw/watchdog/wdt_diag288.c F: include/hw/watchdog/wdt_diag288.h -F: default-configs/*/s390x-softmmu.mak +F: configs/devices/s390x-softmmu/default.mak F: tests/acceptance/machine_s390_ccw_virtio.py T: git https://gitlab.com/cohuck/qemu.git s390-next T: git https://github.com/borntraeger/qemu.git s390-next @@ -1703,7 +1703,6 @@ F: hw/pci-bridge/* F: qapi/pci.json F: docs/pci* F: docs/specs/*pci* -F: default-configs/pci.mak ACPI/SMBIOS M: Michael S. Tsirkin @@ -1808,7 +1807,6 @@ F: docs/usb2.txt F: docs/usb-storage.txt F: include/hw/usb.h F: include/hw/usb/ -F: default-configs/usb.mak USB (serial adapter) M: Gerd Hoffmann @@ -2967,14 +2965,14 @@ M: Warner Losh R: Kyle Evans S: Maintained F: bsd-user/ -F: default-configs/targets/*-bsd-user.mak +F: configs/targets/*-bsd-user.mak T: git https://github.com/qemu-bsd-user/qemu-bsd-user bsd-user-rebase-3.1 Linux user M: Laurent Vivier S: Maintained F: linux-user/ -F: default-configs/targets/*linux-user.mak +F: configs/targets/*linux-user.mak F: scripts/qemu-binfmt-conf.sh F: scripts/update-syscalltbl.sh F: scripts/update-mips-syscall-args.sh diff --git a/default-configs/devices/aarch64-softmmu.mak b/configs/devices/aarch64-softmmu/default.mak similarity index 82% rename from default-configs/devices/aarch64-softmmu.mak rename to configs/devices/aarch64-softmmu/default.mak index 958b1e08e4..cf43ac8da1 100644 --- a/default-configs/devices/aarch64-softmmu.mak +++ b/configs/devices/aarch64-softmmu/default.mak @@ -1,7 +1,7 @@ # Default configuration for aarch64-softmmu # We support all the 32 bit boards so need all their config -include arm-softmmu.mak +include ../arm-softmmu/default.mak CONFIG_XLNX_ZYNQMP_ARM=y CONFIG_XLNX_VERSAL=y diff --git a/default-configs/devices/alpha-softmmu.mak b/configs/devices/alpha-softmmu/default.mak similarity index 100% rename from default-configs/devices/alpha-softmmu.mak rename to configs/devices/alpha-softmmu/default.mak diff --git a/default-configs/devices/arm-softmmu.mak b/configs/devices/arm-softmmu/default.mak similarity index 100% rename from default-configs/devices/arm-softmmu.mak rename to configs/devices/arm-softmmu/default.mak diff --git a/default-configs/devices/avr-softmmu.mak b/configs/devices/avr-softmmu/default.mak similarity index 100% rename from default-configs/devices/avr-softmmu.mak rename to configs/devices/avr-softmmu/default.mak diff --git a/default-configs/devices/cris-softmmu.mak b/configs/devices/cris-softmmu/default.mak similarity index 100% rename from default-configs/devices/cris-softmmu.mak rename to configs/devices/cris-softmmu/default.mak diff --git a/default-configs/devices/hppa-softmmu.mak b/configs/devices/hppa-softmmu/default.mak similarity index 100% rename from default-configs/devices/hppa-softmmu.mak rename to configs/devices/hppa-softmmu/default.mak diff --git a/default-configs/devices/i386-softmmu.mak b/configs/devices/i386-softmmu/default.mak similarity index 100% rename from default-configs/devices/i386-softmmu.mak rename to configs/devices/i386-softmmu/default.mak diff --git a/default-configs/devices/m68k-softmmu.mak b/configs/devices/m68k-softmmu/default.mak similarity index 100% rename from default-configs/devices/m68k-softmmu.mak rename to configs/devices/m68k-softmmu/default.mak diff --git a/default-configs/devices/microblaze-softmmu.mak b/configs/devices/microblaze-softmmu/default.mak similarity index 100% rename from default-configs/devices/microblaze-softmmu.mak rename to configs/devices/microblaze-softmmu/default.mak diff --git a/default-configs/devices/microblazeel-softmmu.mak b/configs/devices/microblazeel-softmmu/default.mak similarity index 54% rename from default-configs/devices/microblazeel-softmmu.mak rename to configs/devices/microblazeel-softmmu/default.mak index 2fcf442fc7..29f7f13816 100644 --- a/default-configs/devices/microblazeel-softmmu.mak +++ b/configs/devices/microblazeel-softmmu/default.mak @@ -1,3 +1,3 @@ # Default configuration for microblazeel-softmmu -include microblaze-softmmu.mak +include ../microblaze-softmmu/default.mak diff --git a/default-configs/devices/mips-softmmu-common.mak b/configs/devices/mips-softmmu/common.mak similarity index 100% rename from default-configs/devices/mips-softmmu-common.mak rename to configs/devices/mips-softmmu/common.mak diff --git a/default-configs/devices/mips-softmmu.mak b/configs/devices/mips-softmmu/default.mak similarity index 56% rename from default-configs/devices/mips-softmmu.mak rename to configs/devices/mips-softmmu/default.mak index 9fede6e00f..c23d95a83a 100644 --- a/default-configs/devices/mips-softmmu.mak +++ b/configs/devices/mips-softmmu/default.mak @@ -1,3 +1,3 @@ # Default configuration for mips-softmmu -include mips-softmmu-common.mak +include common.mak diff --git a/default-configs/devices/mips64-softmmu.mak b/configs/devices/mips64-softmmu/default.mak similarity index 62% rename from default-configs/devices/mips64-softmmu.mak rename to configs/devices/mips64-softmmu/default.mak index a169738635..566672f3c2 100644 --- a/default-configs/devices/mips64-softmmu.mak +++ b/configs/devices/mips64-softmmu/default.mak @@ -1,4 +1,4 @@ # Default configuration for mips64-softmmu -include mips-softmmu-common.mak +include ../mips-softmmu/common.mak CONFIG_JAZZ=y diff --git a/default-configs/devices/mips64el-softmmu.mak b/configs/devices/mips64el-softmmu/default.mak similarity index 88% rename from default-configs/devices/mips64el-softmmu.mak rename to configs/devices/mips64el-softmmu/default.mak index 26c660a05c..c511a061ba 100644 --- a/default-configs/devices/mips64el-softmmu.mak +++ b/configs/devices/mips64el-softmmu/default.mak @@ -1,6 +1,6 @@ # Default configuration for mips64el-softmmu -include mips-softmmu-common.mak +include ../mips-softmmu/common.mak CONFIG_IDE_VIA=y CONFIG_FULOONG=y CONFIG_LOONGSON3V=y diff --git a/default-configs/devices/mipsel-softmmu.mak b/configs/devices/mipsel-softmmu/default.mak similarity index 55% rename from default-configs/devices/mipsel-softmmu.mak rename to configs/devices/mipsel-softmmu/default.mak index a7f6059484..009ccb0e2d 100644 --- a/default-configs/devices/mipsel-softmmu.mak +++ b/configs/devices/mipsel-softmmu/default.mak @@ -1,3 +1,3 @@ # Default configuration for mipsel-softmmu -include mips-softmmu-common.mak +include ../mips-softmmu/common.mak diff --git a/default-configs/devices/nios2-softmmu.mak b/configs/devices/nios2-softmmu/default.mak similarity index 100% rename from default-configs/devices/nios2-softmmu.mak rename to configs/devices/nios2-softmmu/default.mak diff --git a/default-configs/devices/or1k-softmmu.mak b/configs/devices/or1k-softmmu/default.mak similarity index 100% rename from default-configs/devices/or1k-softmmu.mak rename to configs/devices/or1k-softmmu/default.mak diff --git a/default-configs/devices/ppc-softmmu.mak b/configs/devices/ppc-softmmu/default.mak similarity index 100% rename from default-configs/devices/ppc-softmmu.mak rename to configs/devices/ppc-softmmu/default.mak diff --git a/default-configs/devices/ppc64-softmmu.mak b/configs/devices/ppc64-softmmu/default.mak similarity index 79% rename from default-configs/devices/ppc64-softmmu.mak rename to configs/devices/ppc64-softmmu/default.mak index cca52665d9..b90e5bf455 100644 --- a/default-configs/devices/ppc64-softmmu.mak +++ b/configs/devices/ppc64-softmmu/default.mak @@ -1,7 +1,7 @@ # Default configuration for ppc64-softmmu # Include all 32-bit boards -include ppc-softmmu.mak +include ../ppc-softmmu/default.mak # For PowerNV CONFIG_POWERNV=y diff --git a/default-configs/devices/riscv32-softmmu.mak b/configs/devices/riscv32-softmmu/default.mak similarity index 100% rename from default-configs/devices/riscv32-softmmu.mak rename to configs/devices/riscv32-softmmu/default.mak diff --git a/default-configs/devices/riscv64-softmmu.mak b/configs/devices/riscv64-softmmu/default.mak similarity index 100% rename from default-configs/devices/riscv64-softmmu.mak rename to configs/devices/riscv64-softmmu/default.mak diff --git a/default-configs/devices/rx-softmmu.mak b/configs/devices/rx-softmmu/default.mak similarity index 100% rename from default-configs/devices/rx-softmmu.mak rename to configs/devices/rx-softmmu/default.mak diff --git a/default-configs/devices/s390x-softmmu.mak b/configs/devices/s390x-softmmu/default.mak similarity index 100% rename from default-configs/devices/s390x-softmmu.mak rename to configs/devices/s390x-softmmu/default.mak diff --git a/default-configs/devices/sh4-softmmu.mak b/configs/devices/sh4-softmmu/default.mak similarity index 100% rename from default-configs/devices/sh4-softmmu.mak rename to configs/devices/sh4-softmmu/default.mak diff --git a/default-configs/devices/sh4eb-softmmu.mak b/configs/devices/sh4eb-softmmu/default.mak similarity index 55% rename from default-configs/devices/sh4eb-softmmu.mak rename to configs/devices/sh4eb-softmmu/default.mak index 522a7a50fa..f18d1f6519 100644 --- a/default-configs/devices/sh4eb-softmmu.mak +++ b/configs/devices/sh4eb-softmmu/default.mak @@ -1,3 +1,3 @@ # Default configuration for sh4eb-softmmu -include sh4-softmmu.mak +include ../sh4-softmmu/default.mak diff --git a/default-configs/devices/sparc-softmmu.mak b/configs/devices/sparc-softmmu/default.mak similarity index 100% rename from default-configs/devices/sparc-softmmu.mak rename to configs/devices/sparc-softmmu/default.mak diff --git a/default-configs/devices/sparc64-softmmu.mak b/configs/devices/sparc64-softmmu/default.mak similarity index 100% rename from default-configs/devices/sparc64-softmmu.mak rename to configs/devices/sparc64-softmmu/default.mak diff --git a/default-configs/devices/tricore-softmmu.mak b/configs/devices/tricore-softmmu/default.mak similarity index 100% rename from default-configs/devices/tricore-softmmu.mak rename to configs/devices/tricore-softmmu/default.mak diff --git a/default-configs/devices/x86_64-softmmu.mak b/configs/devices/x86_64-softmmu/default.mak similarity index 55% rename from default-configs/devices/x86_64-softmmu.mak rename to configs/devices/x86_64-softmmu/default.mak index 64b2ee2960..ddfc2ea626 100644 --- a/default-configs/devices/x86_64-softmmu.mak +++ b/configs/devices/x86_64-softmmu/default.mak @@ -1,3 +1,3 @@ # Default configuration for x86_64-softmmu -include i386-softmmu.mak +include ../i386-softmmu/default.mak diff --git a/default-configs/devices/xtensa-softmmu.mak b/configs/devices/xtensa-softmmu/default.mak similarity index 100% rename from default-configs/devices/xtensa-softmmu.mak rename to configs/devices/xtensa-softmmu/default.mak diff --git a/configs/devices/xtensaeb-softmmu/default.mak b/configs/devices/xtensaeb-softmmu/default.mak new file mode 100644 index 0000000000..00eafcc292 --- /dev/null +++ b/configs/devices/xtensaeb-softmmu/default.mak @@ -0,0 +1,3 @@ +# Default configuration for Xtensa + +include ../xtensa-softmmu/default.mak diff --git a/default-configs/targets/aarch64-linux-user.mak b/configs/targets/aarch64-linux-user.mak similarity index 100% rename from default-configs/targets/aarch64-linux-user.mak rename to configs/targets/aarch64-linux-user.mak diff --git a/default-configs/targets/aarch64-softmmu.mak b/configs/targets/aarch64-softmmu.mak similarity index 100% rename from default-configs/targets/aarch64-softmmu.mak rename to configs/targets/aarch64-softmmu.mak diff --git a/default-configs/targets/aarch64_be-linux-user.mak b/configs/targets/aarch64_be-linux-user.mak similarity index 100% rename from default-configs/targets/aarch64_be-linux-user.mak rename to configs/targets/aarch64_be-linux-user.mak diff --git a/default-configs/targets/alpha-linux-user.mak b/configs/targets/alpha-linux-user.mak similarity index 100% rename from default-configs/targets/alpha-linux-user.mak rename to configs/targets/alpha-linux-user.mak diff --git a/default-configs/targets/alpha-softmmu.mak b/configs/targets/alpha-softmmu.mak similarity index 100% rename from default-configs/targets/alpha-softmmu.mak rename to configs/targets/alpha-softmmu.mak diff --git a/default-configs/targets/arm-linux-user.mak b/configs/targets/arm-linux-user.mak similarity index 100% rename from default-configs/targets/arm-linux-user.mak rename to configs/targets/arm-linux-user.mak diff --git a/default-configs/targets/arm-softmmu.mak b/configs/targets/arm-softmmu.mak similarity index 100% rename from default-configs/targets/arm-softmmu.mak rename to configs/targets/arm-softmmu.mak diff --git a/default-configs/targets/armeb-linux-user.mak b/configs/targets/armeb-linux-user.mak similarity index 100% rename from default-configs/targets/armeb-linux-user.mak rename to configs/targets/armeb-linux-user.mak diff --git a/default-configs/targets/avr-softmmu.mak b/configs/targets/avr-softmmu.mak similarity index 100% rename from default-configs/targets/avr-softmmu.mak rename to configs/targets/avr-softmmu.mak diff --git a/default-configs/targets/cris-linux-user.mak b/configs/targets/cris-linux-user.mak similarity index 100% rename from default-configs/targets/cris-linux-user.mak rename to configs/targets/cris-linux-user.mak diff --git a/default-configs/targets/cris-softmmu.mak b/configs/targets/cris-softmmu.mak similarity index 100% rename from default-configs/targets/cris-softmmu.mak rename to configs/targets/cris-softmmu.mak diff --git a/default-configs/targets/hexagon-linux-user.mak b/configs/targets/hexagon-linux-user.mak similarity index 100% rename from default-configs/targets/hexagon-linux-user.mak rename to configs/targets/hexagon-linux-user.mak diff --git a/default-configs/targets/hppa-linux-user.mak b/configs/targets/hppa-linux-user.mak similarity index 100% rename from default-configs/targets/hppa-linux-user.mak rename to configs/targets/hppa-linux-user.mak diff --git a/default-configs/targets/hppa-softmmu.mak b/configs/targets/hppa-softmmu.mak similarity index 100% rename from default-configs/targets/hppa-softmmu.mak rename to configs/targets/hppa-softmmu.mak diff --git a/default-configs/targets/i386-bsd-user.mak b/configs/targets/i386-bsd-user.mak similarity index 100% rename from default-configs/targets/i386-bsd-user.mak rename to configs/targets/i386-bsd-user.mak diff --git a/default-configs/targets/i386-linux-user.mak b/configs/targets/i386-linux-user.mak similarity index 100% rename from default-configs/targets/i386-linux-user.mak rename to configs/targets/i386-linux-user.mak diff --git a/default-configs/targets/i386-softmmu.mak b/configs/targets/i386-softmmu.mak similarity index 100% rename from default-configs/targets/i386-softmmu.mak rename to configs/targets/i386-softmmu.mak diff --git a/default-configs/targets/m68k-linux-user.mak b/configs/targets/m68k-linux-user.mak similarity index 100% rename from default-configs/targets/m68k-linux-user.mak rename to configs/targets/m68k-linux-user.mak diff --git a/default-configs/targets/m68k-softmmu.mak b/configs/targets/m68k-softmmu.mak similarity index 100% rename from default-configs/targets/m68k-softmmu.mak rename to configs/targets/m68k-softmmu.mak diff --git a/default-configs/targets/microblaze-linux-user.mak b/configs/targets/microblaze-linux-user.mak similarity index 100% rename from default-configs/targets/microblaze-linux-user.mak rename to configs/targets/microblaze-linux-user.mak diff --git a/default-configs/targets/microblaze-softmmu.mak b/configs/targets/microblaze-softmmu.mak similarity index 100% rename from default-configs/targets/microblaze-softmmu.mak rename to configs/targets/microblaze-softmmu.mak diff --git a/default-configs/targets/microblazeel-linux-user.mak b/configs/targets/microblazeel-linux-user.mak similarity index 100% rename from default-configs/targets/microblazeel-linux-user.mak rename to configs/targets/microblazeel-linux-user.mak diff --git a/default-configs/targets/microblazeel-softmmu.mak b/configs/targets/microblazeel-softmmu.mak similarity index 100% rename from default-configs/targets/microblazeel-softmmu.mak rename to configs/targets/microblazeel-softmmu.mak diff --git a/default-configs/targets/mips-linux-user.mak b/configs/targets/mips-linux-user.mak similarity index 100% rename from default-configs/targets/mips-linux-user.mak rename to configs/targets/mips-linux-user.mak diff --git a/default-configs/targets/mips-softmmu.mak b/configs/targets/mips-softmmu.mak similarity index 100% rename from default-configs/targets/mips-softmmu.mak rename to configs/targets/mips-softmmu.mak diff --git a/default-configs/targets/mips64-linux-user.mak b/configs/targets/mips64-linux-user.mak similarity index 100% rename from default-configs/targets/mips64-linux-user.mak rename to configs/targets/mips64-linux-user.mak diff --git a/default-configs/targets/mips64-softmmu.mak b/configs/targets/mips64-softmmu.mak similarity index 100% rename from default-configs/targets/mips64-softmmu.mak rename to configs/targets/mips64-softmmu.mak diff --git a/default-configs/targets/mips64el-linux-user.mak b/configs/targets/mips64el-linux-user.mak similarity index 100% rename from default-configs/targets/mips64el-linux-user.mak rename to configs/targets/mips64el-linux-user.mak diff --git a/default-configs/targets/mips64el-softmmu.mak b/configs/targets/mips64el-softmmu.mak similarity index 100% rename from default-configs/targets/mips64el-softmmu.mak rename to configs/targets/mips64el-softmmu.mak diff --git a/default-configs/targets/mipsel-linux-user.mak b/configs/targets/mipsel-linux-user.mak similarity index 100% rename from default-configs/targets/mipsel-linux-user.mak rename to configs/targets/mipsel-linux-user.mak diff --git a/default-configs/targets/mipsel-softmmu.mak b/configs/targets/mipsel-softmmu.mak similarity index 100% rename from default-configs/targets/mipsel-softmmu.mak rename to configs/targets/mipsel-softmmu.mak diff --git a/default-configs/targets/mipsn32-linux-user.mak b/configs/targets/mipsn32-linux-user.mak similarity index 100% rename from default-configs/targets/mipsn32-linux-user.mak rename to configs/targets/mipsn32-linux-user.mak diff --git a/default-configs/targets/mipsn32el-linux-user.mak b/configs/targets/mipsn32el-linux-user.mak similarity index 100% rename from default-configs/targets/mipsn32el-linux-user.mak rename to configs/targets/mipsn32el-linux-user.mak diff --git a/default-configs/targets/nios2-linux-user.mak b/configs/targets/nios2-linux-user.mak similarity index 100% rename from default-configs/targets/nios2-linux-user.mak rename to configs/targets/nios2-linux-user.mak diff --git a/default-configs/targets/nios2-softmmu.mak b/configs/targets/nios2-softmmu.mak similarity index 100% rename from default-configs/targets/nios2-softmmu.mak rename to configs/targets/nios2-softmmu.mak diff --git a/default-configs/targets/or1k-linux-user.mak b/configs/targets/or1k-linux-user.mak similarity index 100% rename from default-configs/targets/or1k-linux-user.mak rename to configs/targets/or1k-linux-user.mak diff --git a/default-configs/targets/or1k-softmmu.mak b/configs/targets/or1k-softmmu.mak similarity index 100% rename from default-configs/targets/or1k-softmmu.mak rename to configs/targets/or1k-softmmu.mak diff --git a/default-configs/targets/ppc-linux-user.mak b/configs/targets/ppc-linux-user.mak similarity index 100% rename from default-configs/targets/ppc-linux-user.mak rename to configs/targets/ppc-linux-user.mak diff --git a/default-configs/targets/ppc-softmmu.mak b/configs/targets/ppc-softmmu.mak similarity index 100% rename from default-configs/targets/ppc-softmmu.mak rename to configs/targets/ppc-softmmu.mak diff --git a/default-configs/targets/ppc64-linux-user.mak b/configs/targets/ppc64-linux-user.mak similarity index 100% rename from default-configs/targets/ppc64-linux-user.mak rename to configs/targets/ppc64-linux-user.mak diff --git a/default-configs/targets/ppc64-softmmu.mak b/configs/targets/ppc64-softmmu.mak similarity index 100% rename from default-configs/targets/ppc64-softmmu.mak rename to configs/targets/ppc64-softmmu.mak diff --git a/default-configs/targets/ppc64abi32-linux-user.mak b/configs/targets/ppc64abi32-linux-user.mak similarity index 100% rename from default-configs/targets/ppc64abi32-linux-user.mak rename to configs/targets/ppc64abi32-linux-user.mak diff --git a/default-configs/targets/ppc64le-linux-user.mak b/configs/targets/ppc64le-linux-user.mak similarity index 100% rename from default-configs/targets/ppc64le-linux-user.mak rename to configs/targets/ppc64le-linux-user.mak diff --git a/default-configs/targets/riscv32-linux-user.mak b/configs/targets/riscv32-linux-user.mak similarity index 100% rename from default-configs/targets/riscv32-linux-user.mak rename to configs/targets/riscv32-linux-user.mak diff --git a/default-configs/targets/riscv32-softmmu.mak b/configs/targets/riscv32-softmmu.mak similarity index 100% rename from default-configs/targets/riscv32-softmmu.mak rename to configs/targets/riscv32-softmmu.mak diff --git a/default-configs/targets/riscv64-linux-user.mak b/configs/targets/riscv64-linux-user.mak similarity index 100% rename from default-configs/targets/riscv64-linux-user.mak rename to configs/targets/riscv64-linux-user.mak diff --git a/default-configs/targets/riscv64-softmmu.mak b/configs/targets/riscv64-softmmu.mak similarity index 100% rename from default-configs/targets/riscv64-softmmu.mak rename to configs/targets/riscv64-softmmu.mak diff --git a/default-configs/targets/rx-softmmu.mak b/configs/targets/rx-softmmu.mak similarity index 100% rename from default-configs/targets/rx-softmmu.mak rename to configs/targets/rx-softmmu.mak diff --git a/default-configs/targets/s390x-linux-user.mak b/configs/targets/s390x-linux-user.mak similarity index 100% rename from default-configs/targets/s390x-linux-user.mak rename to configs/targets/s390x-linux-user.mak diff --git a/default-configs/targets/s390x-softmmu.mak b/configs/targets/s390x-softmmu.mak similarity index 100% rename from default-configs/targets/s390x-softmmu.mak rename to configs/targets/s390x-softmmu.mak diff --git a/default-configs/targets/sh4-linux-user.mak b/configs/targets/sh4-linux-user.mak similarity index 100% rename from default-configs/targets/sh4-linux-user.mak rename to configs/targets/sh4-linux-user.mak diff --git a/default-configs/targets/sh4-softmmu.mak b/configs/targets/sh4-softmmu.mak similarity index 100% rename from default-configs/targets/sh4-softmmu.mak rename to configs/targets/sh4-softmmu.mak diff --git a/default-configs/targets/sh4eb-linux-user.mak b/configs/targets/sh4eb-linux-user.mak similarity index 100% rename from default-configs/targets/sh4eb-linux-user.mak rename to configs/targets/sh4eb-linux-user.mak diff --git a/default-configs/targets/sh4eb-softmmu.mak b/configs/targets/sh4eb-softmmu.mak similarity index 100% rename from default-configs/targets/sh4eb-softmmu.mak rename to configs/targets/sh4eb-softmmu.mak diff --git a/default-configs/targets/sparc-linux-user.mak b/configs/targets/sparc-linux-user.mak similarity index 100% rename from default-configs/targets/sparc-linux-user.mak rename to configs/targets/sparc-linux-user.mak diff --git a/default-configs/targets/sparc-softmmu.mak b/configs/targets/sparc-softmmu.mak similarity index 100% rename from default-configs/targets/sparc-softmmu.mak rename to configs/targets/sparc-softmmu.mak diff --git a/default-configs/targets/sparc32plus-linux-user.mak b/configs/targets/sparc32plus-linux-user.mak similarity index 100% rename from default-configs/targets/sparc32plus-linux-user.mak rename to configs/targets/sparc32plus-linux-user.mak diff --git a/default-configs/targets/sparc64-linux-user.mak b/configs/targets/sparc64-linux-user.mak similarity index 100% rename from default-configs/targets/sparc64-linux-user.mak rename to configs/targets/sparc64-linux-user.mak diff --git a/default-configs/targets/sparc64-softmmu.mak b/configs/targets/sparc64-softmmu.mak similarity index 100% rename from default-configs/targets/sparc64-softmmu.mak rename to configs/targets/sparc64-softmmu.mak diff --git a/default-configs/targets/tricore-softmmu.mak b/configs/targets/tricore-softmmu.mak similarity index 100% rename from default-configs/targets/tricore-softmmu.mak rename to configs/targets/tricore-softmmu.mak diff --git a/default-configs/targets/x86_64-bsd-user.mak b/configs/targets/x86_64-bsd-user.mak similarity index 100% rename from default-configs/targets/x86_64-bsd-user.mak rename to configs/targets/x86_64-bsd-user.mak diff --git a/default-configs/targets/x86_64-linux-user.mak b/configs/targets/x86_64-linux-user.mak similarity index 100% rename from default-configs/targets/x86_64-linux-user.mak rename to configs/targets/x86_64-linux-user.mak diff --git a/default-configs/targets/x86_64-softmmu.mak b/configs/targets/x86_64-softmmu.mak similarity index 100% rename from default-configs/targets/x86_64-softmmu.mak rename to configs/targets/x86_64-softmmu.mak diff --git a/default-configs/targets/xtensa-linux-user.mak b/configs/targets/xtensa-linux-user.mak similarity index 100% rename from default-configs/targets/xtensa-linux-user.mak rename to configs/targets/xtensa-linux-user.mak diff --git a/default-configs/targets/xtensa-softmmu.mak b/configs/targets/xtensa-softmmu.mak similarity index 100% rename from default-configs/targets/xtensa-softmmu.mak rename to configs/targets/xtensa-softmmu.mak diff --git a/default-configs/targets/xtensaeb-linux-user.mak b/configs/targets/xtensaeb-linux-user.mak similarity index 100% rename from default-configs/targets/xtensaeb-linux-user.mak rename to configs/targets/xtensaeb-linux-user.mak diff --git a/default-configs/targets/xtensaeb-softmmu.mak b/configs/targets/xtensaeb-softmmu.mak similarity index 100% rename from default-configs/targets/xtensaeb-softmmu.mak rename to configs/targets/xtensaeb-softmmu.mak diff --git a/configure b/configure index a04b1e075c..4eba89f823 100755 --- a/configure +++ b/configure @@ -1677,13 +1677,13 @@ deprecated_features="" mak_wilds="" if [ "$softmmu" = "yes" ]; then - mak_wilds="${mak_wilds} $source_path/default-configs/targets/*-softmmu.mak" + mak_wilds="${mak_wilds} $source_path/configs/targets/*-softmmu.mak" fi if [ "$linux_user" = "yes" ]; then - mak_wilds="${mak_wilds} $source_path/default-configs/targets/*-linux-user.mak" + mak_wilds="${mak_wilds} $source_path/configs/targets/*-linux-user.mak" fi if [ "$bsd_user" = "yes" ]; then - mak_wilds="${mak_wilds} $source_path/default-configs/targets/*-bsd-user.mak" + mak_wilds="${mak_wilds} $source_path/configs/targets/*-bsd-user.mak" fi # If the user doesn't explicitly specify a deprecated target we will diff --git a/default-configs/devices/xtensaeb-softmmu.mak b/default-configs/devices/xtensaeb-softmmu.mak deleted file mode 100644 index f7e48c750c..0000000000 --- a/default-configs/devices/xtensaeb-softmmu.mak +++ /dev/null @@ -1,3 +0,0 @@ -# Default configuration for Xtensa - -include xtensa-softmmu.mak diff --git a/meson.build b/meson.build index d82f7a789d..71c7462e89 100644 --- a/meson.build +++ b/meson.build @@ -1549,7 +1549,7 @@ foreach target : target_dirs endif actual_target_dirs += target - config_target += keyval.load('default-configs/targets' / target + '.mak') + config_target += keyval.load('configs/targets' / target + '.mak') config_target += { 'TARGET_' + config_target['TARGET_ARCH'].to_upper(): 'y' } if 'TARGET_NEED_FDT' in config_target @@ -1597,7 +1597,7 @@ foreach target : target_dirs if target.endswith('-softmmu') config_devices_mak = target + '-config-devices.mak' config_devices_mak = configure_file( - input: ['default-configs/devices' / target + '.mak', 'Kconfig'], + input: ['configs/devices' / target / 'default.mak', 'Kconfig'], output: config_devices_mak, depfile: config_devices_mak + '.d', capture: true, diff --git a/tests/Makefile.include b/tests/Makefile.include index 8f220e15d1..e4dcb17329 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -36,7 +36,7 @@ export SRC_PATH # Get the list of all supported sysemu targets SYSEMU_TARGET_LIST := $(subst -softmmu.mak,,$(notdir \ - $(wildcard $(SRC_PATH)/default-configs/*-softmmu.mak))) + $(wildcard $(SRC_PATH)/configs/*-softmmu.mak))) SPEED = quick From d1d5e9eefd7f0165884998f3054836580a69e1a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 7 Jul 2021 14:17:44 +0100 Subject: [PATCH 199/272] configure: allow the selection of alternate config in the build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While the default config works well enough it does end up enabling a lot of stuff. For more minimal builds we can select a different list of devices and let Kconfig work out what we want. For example: ../../configure --without-default-features \ --target-list=arm-softmmu,aarch64-softmmu \ --with-devices-aarch64=minimal will override the aarch64-softmmu default set of devices with a more minimal set of devices that just enables the virt and sbsa-ref models. Signed-off-by: Alex Bennée Cc: Philippe Mathieu-Daudé Cc: Paolo Bonzini Reviewed-by: Richard Henderson Message-Id: <20210707131744.26027-6-alex.bennee@linaro.org> Signed-off-by: Paolo Bonzini --- configs/devices/aarch64-softmmu/minimal.mak | 9 ++++++++ configure | 24 ++++++++++++++++++++- meson.build | 3 ++- 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 configs/devices/aarch64-softmmu/minimal.mak diff --git a/configs/devices/aarch64-softmmu/minimal.mak b/configs/devices/aarch64-softmmu/minimal.mak new file mode 100644 index 0000000000..0ebc1dca56 --- /dev/null +++ b/configs/devices/aarch64-softmmu/minimal.mak @@ -0,0 +1,9 @@ +# +# A minimal version of the config that only supports only a few +# virtual machines. This avoids bringing in any of numerous legacy +# features from the 32bit platform (although virt still supports 32bit +# itself) +# + +CONFIG_ARM_VIRT=y +CONFIG_SBSA_REF=y diff --git a/configure b/configure index 4eba89f823..85db248ac1 100755 --- a/configure +++ b/configure @@ -915,6 +915,18 @@ for opt do ;; --without-default-devices) default_devices="false" ;; + --with-devices-*[!a-zA-Z0-9_-]*=*) error_exit "Passed bad --with-devices-FOO option" + ;; + --with-devices-*) device_arch=${opt#--with-devices-}; + device_arch=${device_arch%%=*} + cf=$source_path/configs/devices/$device_arch-softmmu/$optarg.mak + if test -f "$cf"; then + device_archs="$device_archs $device_arch" + eval "devices_${device_arch}=\$optarg" + else + error_exit "File $cf does not exist" + fi + ;; --without-default-features) # processed above ;; --enable-gprof) gprof="yes" @@ -1764,7 +1776,8 @@ Advanced options (experts only): --without-default-features default all --enable-* options to "disabled" --without-default-devices do not include any device that is not needed to start the emulator (only use if you are including - desired devices in default-configs/devices/) + desired devices in configs/devices/) + --with-devices-ARCH=NAME override default configs/devices --enable-debug enable common debug build options --enable-sanitizers enable default sanitizers --enable-tsan enable thread sanitizer @@ -5082,6 +5095,15 @@ if test "$skip_meson" = no; then echo "# Automatically generated by configure - do not modify" > $cross echo "[properties]" >> $cross + + # unroll any custom device configs + if test -n "$device_archs"; then + for a in $device_archs; do + eval "c=\$devices_${a}" + echo "${a}-softmmu = '$c'" >> $cross + done + fi + test -z "$cxx" && echo "link_language = 'c'" >> $cross echo "[built-in options]" >> $cross echo "c_args = [${CFLAGS:+$(meson_quote $CFLAGS)}]" >> $cross diff --git a/meson.build b/meson.build index 71c7462e89..651c3b114b 100644 --- a/meson.build +++ b/meson.build @@ -1595,9 +1595,10 @@ foreach target : target_dirs configuration: config_target_data)} if target.endswith('-softmmu') + config_input = meson.get_external_property(target, 'default') config_devices_mak = target + '-config-devices.mak' config_devices_mak = configure_file( - input: ['configs/devices' / target / 'default.mak', 'Kconfig'], + input: ['configs/devices' / target / config_input + '.mak', 'Kconfig'], output: config_devices_mak, depfile: config_devices_mak + '.d', capture: true, From 411ad8dd80077e98ed465775b044caf1a9482f6c Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 9 Jul 2021 10:25:33 +0900 Subject: [PATCH 200/272] meson: Use input/output for entitlements target input/output parameters respect dependencies. Signed-off-by: Akihiko Odaki Message-Id: <20210709012533.58262-1-akihiko.odaki@gmail.com> Signed-off-by: Paolo Bonzini --- meson.build | 30 +++++++++++++++++------------- scripts/entitlement.sh | 10 +++++----- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/meson.build b/meson.build index 651c3b114b..b2e8731410 100644 --- a/meson.build +++ b/meson.build @@ -2609,28 +2609,32 @@ foreach target : target_dirs link_args: link_args, gui_app: exe['gui']) - if 'CONFIG_HVF' in config_target - entitlements = meson.current_source_dir() / 'accel/hvf/entitlements.plist' - else - entitlements = '/dev/null' - endif if targetos == 'darwin' - icon = meson.current_source_dir() / 'pc-bios/qemu.rsrc' + icon = 'pc-bios/qemu.rsrc' + build_input = [emulator, files(icon)] + install_input = [ + get_option('bindir') / exe_name, + meson.current_source_dir() / icon + ] + if 'CONFIG_HVF' in config_target + entitlements = 'accel/hvf/entitlements.plist' + build_input += files(entitlements) + install_input += meson.current_source_dir() / entitlements + endif + emulators += {exe['name'] : custom_target(exe['name'], - depends: emulator, + input: build_input, output: exe['name'], command: [ - meson.current_source_dir() / 'scripts/entitlement.sh', - meson.current_build_dir() / exe_name, - meson.current_build_dir() / exe['name'], - entitlements, icon + files('scripts/entitlement.sh'), + '@OUTPUT@', + '@INPUT@' ]) } meson.add_install_script('scripts/entitlement.sh', '--install', - get_option('bindir') / exe_name, get_option('bindir') / exe['name'], - entitlements, icon) + install_input) else emulators += {exe['name']: emulator} endif diff --git a/scripts/entitlement.sh b/scripts/entitlement.sh index d2a7079ce3..e2c956a3ac 100755 --- a/scripts/entitlement.sh +++ b/scripts/entitlement.sh @@ -8,10 +8,10 @@ if [ "$1" = --install ]; then in_place=false fi -SRC="$1" -DST="$2" -ENTITLEMENT="$3" -ICON="$4" +DST="$1" +SRC="$2" +ICON="$3" +ENTITLEMENT="$4" if $in_place; then trap 'rm "$DST.tmp"' exit @@ -21,7 +21,7 @@ else cd "$MESON_INSTALL_DESTDIR_PREFIX" fi -if test "$ENTITLEMENT" != '/dev/null'; then +if test -n "$ENTITLEMENT"; then codesign --entitlements "$ENTITLEMENT" --force -s - "$SRC" fi From 8973fe43bb6d80f01ea11686c29f98fc4dcae3a6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jul 2021 17:03:32 -0700 Subject: [PATCH 201/272] tcg: Add separator in INDEX_op_call dump MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We lost the ',' following the called function name. Fixes: 3e92aa34434 Reviewed-by: Philippe Mathieu-Daudé 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 5150ed700e..4dd4084419 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1849,7 +1849,7 @@ static void tcg_dump_ops(TCGContext *s, bool have_prefs) col += qemu_log("plugin(%p)", func); } - col += qemu_log("$0x%x,$%d", info->flags, nb_oargs); + col += qemu_log(",$0x%x,$%d", info->flags, nb_oargs); for (i = 0; i < nb_oargs; i++) { col += qemu_log(",%s", tcg_get_arg_str(s, buf, sizeof(buf), op->args[i])); From 1797b08d244ce496d0b0f5027a75542a82c29038 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 29 Jun 2021 07:09:35 +0200 Subject: [PATCH 202/272] tcg: Avoid including 'trace-tcg.h' in target translate.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The root trace-events only declares a single TCG event: $ git grep -w tcg trace-events trace-events:115:# tcg/tcg-op.c trace-events:137:vcpu tcg guest_mem_before(TCGv vaddr, uint16_t info) "info=%d", "vaddr=0x%016"PRIx64" info=%d" and only a tcg/tcg-op.c uses it: $ git grep -l trace_guest_mem_before_tcg tcg/tcg-op.c therefore it is pointless to include "trace-tcg.h" in each target (because it is not used). Remove it. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20210629050935.2570721-1-f4bug@amsat.org> Signed-off-by: Richard Henderson --- target/alpha/translate.c | 1 - target/arm/translate-a64.c | 1 - target/arm/translate-sve.c | 1 - target/arm/translate.c | 1 - target/cris/translate.c | 1 - target/hppa/translate.c | 1 - target/i386/tcg/translate.c | 1 - target/m68k/translate.c | 1 - target/microblaze/translate.c | 1 - target/mips/tcg/translate.c | 1 - target/openrisc/translate.c | 1 - target/ppc/translate.c | 1 - target/rx/translate.c | 1 - target/s390x/translate.c | 1 - target/sh4/translate.c | 1 - target/sparc/translate.c | 1 - target/xtensa/translate.c | 1 - 17 files changed, 17 deletions(-) diff --git a/target/alpha/translate.c b/target/alpha/translate.c index f2922f5f8c..a607c898f4 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -28,7 +28,6 @@ #include "exec/cpu_ldst.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" -#include "trace-tcg.h" #include "exec/translator.h" #include "exec/log.h" diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index e81cc20d04..a6dd9ec701 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -35,7 +35,6 @@ #include "exec/helper-gen.h" #include "exec/log.h" -#include "trace-tcg.h" #include "translate-a64.h" #include "qemu/atomic128.h" diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 46210eb696..35d838aa06 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -30,7 +30,6 @@ #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "exec/log.h" -#include "trace-tcg.h" #include "translate-a64.h" #include "fpu/softfloat.h" diff --git a/target/arm/translate.c b/target/arm/translate.c index 28e478927d..fdf2b3d1c8 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -34,7 +34,6 @@ #include "exec/helper-proto.h" #include "exec/helper-gen.h" -#include "trace-tcg.h" #include "exec/log.h" diff --git a/target/cris/translate.c b/target/cris/translate.c index 4cfe5c86d9..a6796c83b9 100644 --- a/target/cris/translate.c +++ b/target/cris/translate.c @@ -37,7 +37,6 @@ #include "exec/helper-gen.h" -#include "trace-tcg.h" #include "exec/log.h" diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 64af1e0d5c..424ec3252e 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -27,7 +27,6 @@ #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "exec/translator.h" -#include "trace-tcg.h" #include "exec/log.h" /* Since we have a distinction between register size and address size, diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index b21873ed23..85b00a6945 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -30,7 +30,6 @@ #include "exec/helper-gen.h" #include "helper-tcg.h" -#include "trace-tcg.h" #include "exec/log.h" #define PREFIX_REPZ 0x01 diff --git a/target/m68k/translate.c b/target/m68k/translate.c index f0c5bf9154..348fc6e844 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -31,7 +31,6 @@ #include "exec/helper-proto.h" #include "exec/helper-gen.h" -#include "trace-tcg.h" #include "exec/log.h" #include "fpu/softfloat.h" diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index c1b13f4c7d..5dfb08d49f 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -29,7 +29,6 @@ #include "exec/translator.h" #include "qemu/qemu-print.h" -#include "trace-tcg.h" #include "exec/log.h" #define EXTRACT_FIELD(src, start, end) \ diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index ae33c75f08..cb82426f66 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -32,7 +32,6 @@ #include "semihosting/semihost.h" #include "trace.h" -#include "trace-tcg.h" #include "exec/translator.h" #include "exec/log.h" #include "qemu/qemu-print.h" diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index a9c81f8bd5..5db63d7609 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -33,7 +33,6 @@ #include "exec/helper-gen.h" #include "exec/gen-icount.h" -#include "trace-tcg.h" #include "exec/log.h" /* is_jmp field values */ diff --git a/target/ppc/translate.c b/target/ppc/translate.c index f65d1e81ea..07d79acc08 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -32,7 +32,6 @@ #include "exec/helper-proto.h" #include "exec/helper-gen.h" -#include "trace-tcg.h" #include "exec/translator.h" #include "exec/log.h" #include "qemu/atomic128.h" diff --git a/target/rx/translate.c b/target/rx/translate.c index 9ea941c630..22a15ee11d 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -26,7 +26,6 @@ #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "exec/translator.h" -#include "trace-tcg.h" #include "exec/log.h" typedef struct DisasContext { diff --git a/target/s390x/translate.c b/target/s390x/translate.c index 03dab9f350..5af68e01c6 100644 --- a/target/s390x/translate.c +++ b/target/s390x/translate.c @@ -42,7 +42,6 @@ #include "exec/helper-proto.h" #include "exec/helper-gen.h" -#include "trace-tcg.h" #include "exec/translator.h" #include "exec/log.h" #include "qemu/atomic128.h" diff --git a/target/sh4/translate.c b/target/sh4/translate.c index 4dcfff81f6..8a25a4362e 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -28,7 +28,6 @@ #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "exec/translator.h" -#include "trace-tcg.h" #include "exec/log.h" #include "qemu/qemu-print.h" diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 4bfa3179f8..f3fe7a0369 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -29,7 +29,6 @@ #include "exec/helper-gen.h" -#include "trace-tcg.h" #include "exec/translator.h" #include "exec/log.h" #include "asi.h" diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index 14028d307d..d5da35f4fc 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -43,7 +43,6 @@ #include "exec/helper-proto.h" #include "exec/helper-gen.h" -#include "trace-tcg.h" #include "exec/log.h" From f4cba756cb6c6173321c4aad2035d5a86100eef7 Mon Sep 17 00:00:00 2001 From: Liren Wei Date: Sun, 4 Jul 2021 22:31:26 +0800 Subject: [PATCH 203/272] accel/tcg: Hoist tcg_tb_insert() up above tb_link_page() TranslationBlocks not inserted into the corresponding region tree shall be regarded as partially initialized objects, and needs to be finalized first before inserting into QHT. Signed-off-by: Liren Wei Message-Id: Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 7929a7e320..75e4d06557 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -1657,6 +1657,13 @@ TranslationBlock *tb_gen_code(CPUState *cpu, return tb; } + /* + * Insert TB into the corresponding region tree before publishing it + * through QHT. Otherwise rewinding happened in the TB might fail to + * lookup itself using host PC. + */ + tcg_tb_insert(tb); + /* check next page if needed */ virt_page2 = (pc + tb->size - 1) & TARGET_PAGE_MASK; phys_page2 = -1; @@ -1675,9 +1682,9 @@ TranslationBlock *tb_gen_code(CPUState *cpu, orig_aligned -= ROUND_UP(sizeof(*tb), qemu_icache_linesize); qatomic_set(&tcg_ctx->code_gen_ptr, (void *)orig_aligned); tb_destroy(tb); + tcg_tb_remove(tb); return existing_tb; } - tcg_tb_insert(tb); return tb; } From 834361efd9d52947663aa5b297693f8e352bef2a Mon Sep 17 00:00:00 2001 From: Liren Wei Date: Sun, 4 Jul 2021 22:31:27 +0800 Subject: [PATCH 204/272] tcg: Bake tb_destroy() into tcg_region_tree The function is called only at tcg_gen_code() when duplicated TBs are translated by different threads, and when the tcg_region_tree is reset. Bake it into the underlying GTree as its value destroy function to unite these situations. Also remove tcg_region_tree_traverse() which now becomes useless. Signed-off-by: Liren Wei Message-Id: <8dc352f08d038c4e7a1f5f56962398cdc700c3aa.1625404483.git.lrwei@bupt.edu.cn> [rth: Name the new tb_tc_cmp parameter correctly.] Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 6 ------ include/tcg/tcg.h | 1 - tcg/region.c | 19 ++++++++----------- 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 75e4d06557..57455d8639 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -378,11 +378,6 @@ static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb, return 0; } -void tb_destroy(TranslationBlock *tb) -{ - qemu_spin_destroy(&tb->jmp_lock); -} - bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc, bool will_exit) { /* @@ -1681,7 +1676,6 @@ TranslationBlock *tb_gen_code(CPUState *cpu, orig_aligned -= ROUND_UP(sizeof(*tb), qemu_icache_linesize); qatomic_set(&tcg_ctx->code_gen_ptr, (void *)orig_aligned); - tb_destroy(tb); tcg_tb_remove(tb); return existing_tb; } diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 899493701c..dedb86939a 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -808,7 +808,6 @@ void *tcg_malloc_internal(TCGContext *s, int size); void tcg_pool_reset(TCGContext *s); TranslationBlock *tcg_tb_alloc(TCGContext *s); -void tb_destroy(TranslationBlock *tb); void tcg_region_reset_all(void); size_t tcg_code_size(void); diff --git a/tcg/region.c b/tcg/region.c index 00b0c3b091..d3a3658e81 100644 --- a/tcg/region.c +++ b/tcg/region.c @@ -112,7 +112,7 @@ static int ptr_cmp_tb_tc(const void *ptr, const struct tb_tc *s) return 0; } -static gint tb_tc_cmp(gconstpointer ap, gconstpointer bp) +static gint tb_tc_cmp(gconstpointer ap, gconstpointer bp, gpointer userdata) { const struct tb_tc *a = ap; const struct tb_tc *b = bp; @@ -143,6 +143,12 @@ static gint tb_tc_cmp(gconstpointer ap, gconstpointer bp) return ptr_cmp_tb_tc(b->ptr, a); } +static void tb_destroy(gpointer value) +{ + TranslationBlock *tb = value; + qemu_spin_destroy(&tb->jmp_lock); +} + static void tcg_region_trees_init(void) { size_t i; @@ -153,7 +159,7 @@ static void tcg_region_trees_init(void) struct tcg_region_tree *rt = region_trees + i * tree_size; qemu_mutex_init(&rt->lock); - rt->tree = g_tree_new(tb_tc_cmp); + rt->tree = g_tree_new_full(tb_tc_cmp, NULL, NULL, tb_destroy); } } @@ -277,14 +283,6 @@ size_t tcg_nb_tbs(void) return nb_tbs; } -static gboolean tcg_region_tree_traverse(gpointer k, gpointer v, gpointer data) -{ - TranslationBlock *tb = v; - - tb_destroy(tb); - return FALSE; -} - static void tcg_region_tree_reset_all(void) { size_t i; @@ -293,7 +291,6 @@ static void tcg_region_tree_reset_all(void) for (i = 0; i < region.n; i++) { struct tcg_region_tree *rt = region_trees + i * tree_size; - g_tree_foreach(rt->tree, tcg_region_tree_traverse, NULL); /* Increment the refcount first so that destroy acts as a reset */ g_tree_ref(rt->tree); g_tree_destroy(rt->tree); From a4390647f7af6b8a539571b44ab537478f0ae548 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 Jul 2021 20:54:56 -0700 Subject: [PATCH 205/272] tcg: Move tb_phys_invalidate_count to tb_ctx We can call do_tb_phys_invalidate from an iocontext, which has no per-thread tcg_ctx. Move this to tb_ctx, which is global. The actual update still takes place with a lock held, so only an atomic set is required, not an atomic increment. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/457 Tested-by: Viktor Ashirov Signed-off-by: Richard Henderson --- accel/tcg/tb-context.h | 1 + accel/tcg/translate-all.c | 8 ++++---- include/tcg/tcg.h | 3 --- tcg/region.c | 14 -------------- 4 files changed, 5 insertions(+), 21 deletions(-) diff --git a/accel/tcg/tb-context.h b/accel/tcg/tb-context.h index cc33979113..cac62d9749 100644 --- a/accel/tcg/tb-context.h +++ b/accel/tcg/tb-context.h @@ -34,6 +34,7 @@ struct TBContext { /* statistics */ unsigned tb_flush_count; + unsigned tb_phys_invalidate_count; }; extern TBContext tb_ctx; diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 57455d8639..4df26de858 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -1219,8 +1219,8 @@ static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list) /* suppress any remaining jumps to this TB */ tb_jmp_unlink(tb); - qatomic_set(&tcg_ctx->tb_phys_invalidate_count, - tcg_ctx->tb_phys_invalidate_count + 1); + qatomic_set(&tb_ctx.tb_phys_invalidate_count, + tb_ctx.tb_phys_invalidate_count + 1); } static void tb_phys_invalidate__locked(TranslationBlock *tb) @@ -2128,8 +2128,8 @@ void dump_exec_info(void) qemu_printf("\nStatistics:\n"); qemu_printf("TB flush count %u\n", qatomic_read(&tb_ctx.tb_flush_count)); - qemu_printf("TB invalidate count %zu\n", - tcg_tb_phys_invalidate_count()); + qemu_printf("TB invalidate count %u\n", + qatomic_read(&tb_ctx.tb_phys_invalidate_count)); tlb_flush_counts(&flush_full, &flush_part, &flush_elide); qemu_printf("TLB full flushes %zu\n", flush_full); diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index dedb86939a..25dd19d6e1 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -579,8 +579,6 @@ struct TCGContext { /* Threshold to flush the translated code buffer. */ void *code_gen_highwater; - size_t tb_phys_invalidate_count; - /* Track which vCPU triggers events */ CPUState *cpu; /* *_trans */ @@ -815,7 +813,6 @@ size_t tcg_code_capacity(void); void tcg_tb_insert(TranslationBlock *tb); void tcg_tb_remove(TranslationBlock *tb); -size_t tcg_tb_phys_invalidate_count(void); TranslationBlock *tcg_tb_lookup(uintptr_t tc_ptr); void tcg_tb_foreach(GTraverseFunc func, gpointer user_data); size_t tcg_nb_tbs(void); diff --git a/tcg/region.c b/tcg/region.c index d3a3658e81..e64c3ea230 100644 --- a/tcg/region.c +++ b/tcg/region.c @@ -980,17 +980,3 @@ size_t tcg_code_capacity(void) return capacity; } - -size_t tcg_tb_phys_invalidate_count(void) -{ - unsigned int n_ctxs = qatomic_read(&tcg_cur_ctxs); - unsigned int i; - size_t total = 0; - - for (i = 0; i < n_ctxs; i++) { - const TCGContext *s = qatomic_read(&tcg_ctxs[i]); - - total += qatomic_read(&s->tb_phys_invalidate_count); - } - return total; -} From d3a2a1d80331b437bcfa0dc43f2c447d3104898e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 20 Jun 2021 15:28:18 -0700 Subject: [PATCH 206/272] accel/tcg: Introduce translator_use_goto_tb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a generic version of the common use_goto_tb test. Various targets avoid the page crossing test for CONFIG_USER_ONLY, but that is wrong: mmap and mprotect can change page permissions. Reviewed-by: Max Filippov Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/translator.c | 11 +++++++++++ include/exec/translator.h | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 1d32732198..59804af37b 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -31,6 +31,17 @@ void translator_loop_temp_check(DisasContextBase *db) } } +bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest) +{ + /* Suppress goto_tb in the case of single-steping. */ + if (db->singlestep_enabled || singlestep) { + return false; + } + + /* Check for the dest on the same page as the start of the TB. */ + return ((db->pc_first ^ dest) & TARGET_PAGE_MASK) == 0; +} + void translator_loop(const TranslatorOps *ops, DisasContextBase *db, CPUState *cpu, TranslationBlock *tb, int max_insns) { diff --git a/include/exec/translator.h b/include/exec/translator.h index 24232ead41..dd9c06d40d 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -145,6 +145,16 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, void translator_loop_temp_check(DisasContextBase *db); +/** + * translator_use_goto_tb + * @db: Disassembly context + * @dest: target pc of the goto + * + * Return true if goto_tb is allowed between the current TB + * and the destination PC. + */ +bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest); + /* * Translator Load Functions * From cba201f4c74a99b6ee0a43d66f5dae8f901b4006 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 20 Jun 2021 15:40:42 -0700 Subject: [PATCH 207/272] target/alpha: Remove use_exit_tb We have not needed to end a TB for I/O since ba3e7926691 ("icount: clean up cpu_can_io at the entry to the block"). We do not need to use exit_tb for singlestep, which only means generate one insn per TB. Which leaves only singlestep_enabled, which means raise a debug trap after every TB, which does not use exit_tb, which would leave the function mis-named. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- target/alpha/translate.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/target/alpha/translate.c b/target/alpha/translate.c index a607c898f4..cb2cb2de6b 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -449,19 +449,8 @@ static bool in_superpage(DisasContext *ctx, int64_t addr) #endif } -static bool use_exit_tb(DisasContext *ctx) -{ - return ((tb_cflags(ctx->base.tb) & CF_LAST_IO) - || ctx->base.singlestep_enabled - || singlestep); -} - static bool use_goto_tb(DisasContext *ctx, uint64_t dest) { - /* Suppress goto_tb in the case of single-steping and IO. */ - if (unlikely(use_exit_tb(ctx))) { - return false; - } #ifndef CONFIG_USER_ONLY /* If the destination is in the superpage, the page perms can't change. */ if (in_superpage(ctx, dest)) { @@ -1270,7 +1259,7 @@ static DisasJumpType gen_call_pal(DisasContext *ctx, int palcode) need the page permissions check. We'll see the existence of the page when we create the TB, and we'll flush all TBs if we change the PAL base register. */ - if (!use_exit_tb(ctx)) { + if (!ctx->base.singlestep_enabled) { tcg_gen_goto_tb(0); tcg_gen_movi_i64(cpu_pc, entry); tcg_gen_exit_tb(ctx->base.tb, 0); @@ -3094,7 +3083,7 @@ static void alpha_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next); /* FALLTHRU */ case DISAS_PC_UPDATED: - if (!use_exit_tb(ctx)) { + if (!ctx->base.singlestep_enabled) { tcg_gen_lookup_and_goto_ptr(); break; } From 3fd3442abe24f8cabcbb40b73ffe7de81e5db446 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 20 Jun 2021 15:44:28 -0700 Subject: [PATCH 208/272] target/alpha: Remove in_superpage The number of links across (normal) pages using this is low, and it will shortly violate the contract for breakpoints. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- target/alpha/translate.c | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/target/alpha/translate.c b/target/alpha/translate.c index cb2cb2de6b..bb7b5ce994 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -438,24 +438,9 @@ static DisasJumpType gen_store_conditional(DisasContext *ctx, int ra, int rb, return DISAS_NEXT; } -static bool in_superpage(DisasContext *ctx, int64_t addr) -{ -#ifndef CONFIG_USER_ONLY - return ((ctx->tbflags & ENV_FLAG_PS_USER) == 0 - && addr >> TARGET_VIRT_ADDR_SPACE_BITS == -1 - && ((addr >> 41) & 3) == 2); -#else - return false; -#endif -} - static bool use_goto_tb(DisasContext *ctx, uint64_t dest) { #ifndef CONFIG_USER_ONLY - /* If the destination is in the superpage, the page perms can't change. */ - if (in_superpage(ctx, dest)) { - return true; - } /* Check for the dest on the same page as the start of the TB. */ return ((ctx->base.tb->pc ^ dest) & TARGET_PAGE_MASK) == 0; #else @@ -2990,7 +2975,7 @@ static void alpha_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *ctx = container_of(dcbase, DisasContext, base); CPUAlphaState *env = cpu->env_ptr; - int64_t bound, mask; + int64_t bound; ctx->tbflags = ctx->base.tb->flags; ctx->mem_idx = cpu_mmu_index(env, false); @@ -3019,12 +3004,7 @@ static void alpha_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) ctx->lit = NULL; /* Bound the number of insns to execute to those left on the page. */ - if (in_superpage(ctx, ctx->base.pc_first)) { - mask = -1ULL << 41; - } else { - mask = TARGET_PAGE_MASK; - } - bound = -(ctx->base.pc_first | mask) / 4; + bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4; ctx->base.max_insns = MIN(ctx->base.max_insns, bound); } From 21a7e89eced0372cb465e0c9b0a19ffc4e7b75b1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 20 Jun 2021 15:46:27 -0700 Subject: [PATCH 209/272] target/alpha: Use translator_use_goto_tb Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- target/alpha/translate.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/target/alpha/translate.c b/target/alpha/translate.c index bb7b5ce994..833d3baa7b 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -440,12 +440,7 @@ static DisasJumpType gen_store_conditional(DisasContext *ctx, int ra, int rb, static bool use_goto_tb(DisasContext *ctx, uint64_t dest) { -#ifndef CONFIG_USER_ONLY - /* Check for the dest on the same page as the start of the TB. */ - return ((ctx->base.tb->pc ^ dest) & TARGET_PAGE_MASK) == 0; -#else - return true; -#endif + return translator_use_goto_tb(&ctx->base, dest); } static DisasJumpType gen_bdirect(DisasContext *ctx, int ra, int32_t disp) From 73fce314dbbf2d1c3cd411a34535ebe5d20e1e45 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jun 2021 11:01:05 -0700 Subject: [PATCH 210/272] target/arm: Use DISAS_TOO_MANY for ISB and SB Using gen_goto_tb directly misses the single-step check. Let the branch or debug exception be emitted by arm_tr_tb_stop. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- target/arm/translate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/translate.c b/target/arm/translate.c index fdf2b3d1c8..6d2867be1d 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -8904,7 +8904,7 @@ static bool trans_ISB(DisasContext *s, arg_ISB *a) * self-modifying code correctly and also to take * any pending interrupts immediately. */ - gen_goto_tb(s, 0, s->base.pc_next); + s->base.is_jmp = DISAS_TOO_MANY; return true; } @@ -8918,7 +8918,7 @@ static bool trans_SB(DisasContext *s, arg_SB *a) * for TCG; MB and end the TB instead. */ tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); - gen_goto_tb(s, 0, s->base.pc_next); + s->base.is_jmp = DISAS_TOO_MANY; return true; } From 0285162bdf5f35c5c80df43cfb8941c9105ccfb1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jun 2021 11:12:40 -0700 Subject: [PATCH 211/272] target/arm: Use translator_use_goto_tb for aarch64 We have not needed to end a TB for I/O since ba3e7926691 ("icount: clean up cpu_can_io at the entry to the block"), and gdbstub singlestep is handled by the generic function. Drop the unused 'n' argument to use_goto_tb. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- target/arm/translate-a64.c | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index a6dd9ec701..ca11a5fecd 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -385,35 +385,20 @@ static void gen_step_complete_exception(DisasContext *s) s->base.is_jmp = DISAS_NORETURN; } -static inline bool use_goto_tb(DisasContext *s, int n, uint64_t dest) +static inline bool use_goto_tb(DisasContext *s, uint64_t dest) { - /* No direct tb linking with singlestep (either QEMU's or the ARM - * debug architecture kind) or deterministic io - */ - if (s->base.singlestep_enabled || s->ss_active || - (tb_cflags(s->base.tb) & CF_LAST_IO)) { + if (s->ss_active) { return false; } - -#ifndef CONFIG_USER_ONLY - /* Only link tbs from inside the same guest page */ - if ((s->base.tb->pc & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) { - return false; - } -#endif - - return true; + return translator_use_goto_tb(&s->base, dest); } static inline void gen_goto_tb(DisasContext *s, int n, uint64_t dest) { - const TranslationBlock *tb; - - tb = s->base.tb; - if (use_goto_tb(s, n, dest)) { + if (use_goto_tb(s, dest)) { tcg_gen_goto_tb(n); gen_a64_set_pc_im(dest); - tcg_gen_exit_tb(tb, n); + tcg_gen_exit_tb(s->base.tb, n); s->base.is_jmp = DISAS_NORETURN; } else { gen_a64_set_pc_im(dest); From 97f11c81699a2ca5b68cd33f53e4e4997910dd60 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jun 2021 11:19:33 -0700 Subject: [PATCH 212/272] target/arm: Use translator_use_goto_tb for aarch32 Just use translator_use_goto_tb directly at the one call site, rather than maintaining a local wrapper. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- target/arm/translate.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/target/arm/translate.c b/target/arm/translate.c index 6d2867be1d..e1a8152598 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -2578,16 +2578,6 @@ static int disas_dsp_insn(DisasContext *s, uint32_t insn) return 1; } -static inline bool use_goto_tb(DisasContext *s, target_ulong dest) -{ -#ifndef CONFIG_USER_ONLY - return (s->base.tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) || - ((s->base.pc_next - 1) & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); -#else - return true; -#endif -} - static void gen_goto_ptr(void) { tcg_gen_lookup_and_goto_ptr(); @@ -2599,7 +2589,7 @@ static void gen_goto_ptr(void) */ static void gen_goto_tb(DisasContext *s, int n, target_ulong dest) { - if (use_goto_tb(s, dest)) { + if (translator_use_goto_tb(&s->base, dest)) { tcg_gen_goto_tb(n); gen_set_pc_im(s, dest); tcg_gen_exit_tb(s->base.tb, n); From a50d52bc3bea94f6c815692b27b6d3649a69c787 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 20 Jun 2021 15:58:37 -0700 Subject: [PATCH 213/272] target/avr: Use translator_use_goto_tb Single stepping is not the only reason not to use goto_tb. If goto_tb is disallowed, and single-stepping is not enabled, then use tcg_gen_lookup_and_goto_tb to indirectly chain. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- target/avr/translate.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/target/avr/translate.c b/target/avr/translate.c index c06ce45bc7..8237a03c23 100644 --- a/target/avr/translate.c +++ b/target/avr/translate.c @@ -1083,14 +1083,17 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) { const TranslationBlock *tb = ctx->base.tb; - if (!ctx->base.singlestep_enabled) { + if (translator_use_goto_tb(&ctx->base, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_i32(cpu_pc, dest); tcg_gen_exit_tb(tb, n); } else { tcg_gen_movi_i32(cpu_pc, dest); - gen_helper_debug(cpu_env); - tcg_gen_exit_tb(NULL, 0); + if (ctx->base.singlestep_enabled) { + gen_helper_debug(cpu_env); + } else { + tcg_gen_lookup_and_goto_ptr(); + } } ctx->base.is_jmp = DISAS_NORETURN; } From 1810afd56a947f4b201a6211da69100403402026 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 20 Jun 2021 16:03:06 -0700 Subject: [PATCH 214/272] target/avr: Mark some helpers noreturn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All of these helpers end with cpu_loop_exit. Reviewed-by: Michael Rolnik Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/avr/helper.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/avr/helper.h b/target/avr/helper.h index 8e1ae7fda0..4d02e648fa 100644 --- a/target/avr/helper.h +++ b/target/avr/helper.h @@ -19,10 +19,10 @@ */ DEF_HELPER_1(wdr, void, env) -DEF_HELPER_1(debug, void, env) -DEF_HELPER_1(break, void, env) -DEF_HELPER_1(sleep, void, env) -DEF_HELPER_1(unsupported, void, env) +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) From ca92d7f89b1bdc0cbe6a644d39f96cab23a33bd0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 20 Jun 2021 16:05:53 -0700 Subject: [PATCH 215/272] target/cris: Use translator_use_goto_tb The test for singlestepping is done in translator_use_goto_tb, so we may elide it from cris_tr_tb_stop. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- target/cris/translate.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/target/cris/translate.c b/target/cris/translate.c index a6796c83b9..9258c13e9f 100644 --- a/target/cris/translate.c +++ b/target/cris/translate.c @@ -481,7 +481,7 @@ static void t_gen_swapr(TCGv d, TCGv s) static bool use_goto_tb(DisasContext *dc, target_ulong dest) { - return ((dest ^ dc->base.pc_first) & TARGET_PAGE_MASK) == 0; + return translator_use_goto_tb(&dc->base, dest); } static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) @@ -3234,8 +3234,7 @@ static void cris_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) * Use a conditional branch if either taken or not-taken path * can use goto_tb. If neither can, then treat it as indirect. */ - if (likely(!dc->base.singlestep_enabled) - && likely(!dc->cpustate_changed) + if (likely(!dc->cpustate_changed) && (use_goto_tb(dc, dc->jmp_pc) || use_goto_tb(dc, npc))) { TCGLabel *not_taken = gen_new_label(); From 57f914983c5a73dcff14403e7b2080bd7664cbf5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 20 Jun 2021 16:14:29 -0700 Subject: [PATCH 216/272] target/hppa: Use translator_use_goto_tb Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- target/hppa/translate.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 424ec3252e..835120c038 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -816,10 +816,7 @@ static bool gen_illegal(DisasContext *ctx) static bool use_goto_tb(DisasContext *ctx, target_ureg dest) { - /* Suppress goto_tb for page crossing, IO, or single-steping. */ - return !(((ctx->base.pc_first ^ dest) & TARGET_PAGE_MASK) - || (tb_cflags(ctx->base.tb) & CF_LAST_IO) - || ctx->base.singlestep_enabled); + return translator_use_goto_tb(&ctx->base, dest); } /* If the next insn is to be nullified, and it's on the same page, From b473534d5df82042d1b2c9c651d3e80772ce0f4b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 20 Jun 2021 16:16:45 -0700 Subject: [PATCH 217/272] target/i386: Use translator_use_goto_tb Just use translator_use_goto_tb directly at the one call site, rather than maintaining a local wrapper. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- target/i386/tcg/translate.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 85b00a6945..37a66b4097 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -2313,21 +2313,11 @@ static inline int insn_const_size(MemOp ot) } } -static inline bool use_goto_tb(DisasContext *s, target_ulong pc) -{ -#ifndef CONFIG_USER_ONLY - return (pc & TARGET_PAGE_MASK) == (s->base.tb->pc & TARGET_PAGE_MASK) || - (pc & TARGET_PAGE_MASK) == (s->pc_start & TARGET_PAGE_MASK); -#else - return true; -#endif -} - -static inline void gen_goto_tb(DisasContext *s, int tb_num, target_ulong eip) +static void gen_goto_tb(DisasContext *s, int tb_num, target_ulong eip) { target_ulong pc = s->cs_base + eip; - if (use_goto_tb(s, pc)) { + if (translator_use_goto_tb(&s->base, pc)) { /* jump to same page: we can use a direct jump */ tcg_gen_goto_tb(tb_num); gen_jmp_im(s, eip); From fbf565c4e010e749b7536ccec4eae38729791fac Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 20 Jun 2021 16:18:47 -0700 Subject: [PATCH 218/272] target/m68k: Use translator_use_goto_tb Just use translator_use_goto_tb directly at the one call site, rather than maintaining a local wrapper. Acked-by: Laurent Vivier Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- target/m68k/translate.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 348fc6e844..1fee04b8dd 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -1519,16 +1519,6 @@ static void gen_exit_tb(DisasContext *s) } \ } while (0) -static inline bool use_goto_tb(DisasContext *s, uint32_t dest) -{ -#ifndef CONFIG_USER_ONLY - return (s->base.pc_first & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) - || (s->base.pc_next & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); -#else - return true; -#endif -} - /* Generate a jump to an immediate address. */ static void gen_jmp_tb(DisasContext *s, int n, uint32_t dest) { @@ -1536,7 +1526,7 @@ static void gen_jmp_tb(DisasContext *s, int n, uint32_t dest) update_cc_op(s); tcg_gen_movi_i32(QREG_PC, dest); gen_singlestep_exception(s); - } else if (use_goto_tb(s, dest)) { + } else if (translator_use_goto_tb(&s->base, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_i32(QREG_PC, dest); tcg_gen_exit_tb(s->base.tb, n); From 725930c2a57d37f925b9b28f1655961a231f7d20 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 20 Jun 2021 16:20:04 -0700 Subject: [PATCH 219/272] target/microblaze: Use translator_use_goto_tb Just use translator_use_goto_tb directly at the one call site, rather than maintaining a local wrapper. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- target/microblaze/translate.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 5dfb08d49f..c68a84a219 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -124,15 +124,6 @@ static void gen_raise_hw_excp(DisasContext *dc, uint32_t esr_ec) gen_raise_exception_sync(dc, EXCP_HW_EXCP); } -static inline bool use_goto_tb(DisasContext *dc, target_ulong dest) -{ -#ifndef CONFIG_USER_ONLY - return (dc->base.pc_first & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); -#else - return true; -#endif -} - static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) { if (dc->base.singlestep_enabled) { @@ -140,7 +131,7 @@ static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) tcg_gen_movi_i32(cpu_pc, dest); gen_helper_raise_exception(cpu_env, tmp); tcg_temp_free_i32(tmp); - } else if (use_goto_tb(dc, dest)) { + } else if (translator_use_goto_tb(&dc->base, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_i32(cpu_pc, dest); tcg_gen_exit_tb(dc->base.tb, n); From 97eea3c19bda9537df35c43382f954335dceafff Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 20 Jun 2021 16:21:40 -0700 Subject: [PATCH 220/272] target/mips: Use translator_use_goto_tb Just use translator_use_goto_tb directly at the one call site, rather than maintaining a local wrapper. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- target/mips/tcg/translate.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index cb82426f66..5cd3e7d8dd 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -4947,22 +4947,9 @@ static void gen_trap(DisasContext *ctx, uint32_t opc, tcg_temp_free(t1); } -static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest) +static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) { - if (unlikely(ctx->base.singlestep_enabled)) { - return false; - } - -#ifndef CONFIG_USER_ONLY - return (ctx->base.tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); -#else - return true; -#endif -} - -static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) -{ - if (use_goto_tb(ctx, dest)) { + if (translator_use_goto_tb(&ctx->base, dest)) { tcg_gen_goto_tb(n); gen_save_pc(dest); tcg_gen_exit_tb(ctx->base.tb, n); From 34f5e75a9427e00d0f9d745a2dc20a955ec935bf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 20 Jun 2021 16:23:04 -0700 Subject: [PATCH 221/272] target/mips: Fix missing else in gen_goto_tb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not emit dead code for the singlestep_enabled case, after having exited the TB with a debug exception. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/mips/tcg/translate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 5cd3e7d8dd..47c967acbf 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -4958,8 +4958,9 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) if (ctx->base.singlestep_enabled) { save_cpu_state(ctx, 0); gen_helper_raise_exception_debug(cpu_env); + } else { + tcg_gen_lookup_and_goto_ptr(); } - tcg_gen_lookup_and_goto_ptr(); } } From 6082414e3f240de12987f5136b5fdd314379262c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 20 Jun 2021 16:24:00 -0700 Subject: [PATCH 222/272] target/nios2: Use translator_use_goto_tb Just use translator_use_goto_tb directly at the one call site, rather than maintaining a local wrapper. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- target/nios2/translate.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/target/nios2/translate.c b/target/nios2/translate.c index 930f3d3395..17742cebc7 100644 --- a/target/nios2/translate.c +++ b/target/nios2/translate.c @@ -150,24 +150,11 @@ static void t_gen_helper_raise_exception(DisasContext *dc, dc->base.is_jmp = DISAS_NORETURN; } -static bool use_goto_tb(DisasContext *dc, uint32_t dest) -{ - if (unlikely(dc->base.singlestep_enabled)) { - return false; - } - -#ifndef CONFIG_USER_ONLY - return (dc->base.pc_first & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); -#else - return true; -#endif -} - static void gen_goto_tb(DisasContext *dc, int n, uint32_t dest) { const TranslationBlock *tb = dc->base.tb; - if (use_goto_tb(dc, dest)) { + if (translator_use_goto_tb(&dc->base, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_tl(cpu_R[R_PC], dest); tcg_gen_exit_tb(tb, n); From adf1f3dee69f88a66fc36c962dbf74d28de84b65 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 20 Jun 2021 16:28:24 -0700 Subject: [PATCH 223/272] target/openrisc: Use translator_use_goto_tb Reorder the control statements to allow using the page boundary check from translator_use_goto_tb(). Reviewed-by: Stafford Horne Signed-off-by: Richard Henderson --- target/openrisc/translate.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 5db63d7609..37c3e3e0a3 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -1719,16 +1719,17 @@ static void openrisc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) /* fallthru */ case DISAS_TOO_MANY: - if (unlikely(dc->base.singlestep_enabled)) { - tcg_gen_movi_tl(cpu_pc, jmp_dest); - gen_exception(dc, EXCP_DEBUG); - } else if ((dc->base.pc_first ^ jmp_dest) & TARGET_PAGE_MASK) { - tcg_gen_movi_tl(cpu_pc, jmp_dest); - tcg_gen_lookup_and_goto_ptr(); - } else { + if (translator_use_goto_tb(&dc->base, jmp_dest)) { tcg_gen_goto_tb(0); tcg_gen_movi_tl(cpu_pc, jmp_dest); tcg_gen_exit_tb(dc->base.tb, 0); + break; + } + tcg_gen_movi_tl(cpu_pc, jmp_dest); + if (unlikely(dc->base.singlestep_enabled)) { + gen_exception(dc, EXCP_DEBUG); + } else { + tcg_gen_lookup_and_goto_ptr(); } break; From 6e9cc373ec5871bfd8aca36e319ded67e48ca58f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 20 Jun 2021 16:31:23 -0700 Subject: [PATCH 224/272] target/ppc: Use translator_use_goto_tb Reviewed-by: Luis Pires Signed-off-by: Richard Henderson --- target/ppc/translate.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 07d79acc08..0ad601793c 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -4301,15 +4301,7 @@ static inline void gen_update_cfar(DisasContext *ctx, target_ulong nip) static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest) { - if (unlikely(ctx->singlestep_enabled)) { - return false; - } - -#ifndef CONFIG_USER_ONLY - return (ctx->base.tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); -#else - return true; -#endif + return translator_use_goto_tb(&ctx->base, dest); } static void gen_lookup_and_goto_ptr(DisasContext *ctx) From c54d50c1d6d0607ba3b1f803a9284e4b55124522 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 20 Jun 2021 16:34:21 -0700 Subject: [PATCH 225/272] target/riscv: Use translator_use_goto_tb Just use translator_use_goto_tb directly at the one call site, rather than maintaining a local wrapper. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson --- target/riscv/translate.c | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 62a7d7e4c7..deda0c8a44 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -168,29 +168,11 @@ static void gen_exception_inst_addr_mis(DisasContext *ctx) generate_exception_mtval(ctx, RISCV_EXCP_INST_ADDR_MIS); } -static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest) -{ - if (unlikely(ctx->base.singlestep_enabled)) { - return false; - } - -#ifndef CONFIG_USER_ONLY - return (ctx->base.tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); -#else - return true; -#endif -} - static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) { - if (use_goto_tb(ctx, dest)) { - /* chaining is only allowed when the jump is to the same page */ + if (translator_use_goto_tb(&ctx->base, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_tl(cpu_pc, dest); - - /* No need to check for single stepping here as use_goto_tb() will - * return false in case of single stepping. - */ tcg_gen_exit_tb(ctx->base.tb, n); } else { tcg_gen_movi_tl(cpu_pc, dest); From f3f713cc151086ca39d4f97270594fd8c43e17e5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 20 Jun 2021 16:37:12 -0700 Subject: [PATCH 226/272] target/rx: Use translator_use_goto_tb Just use translator_use_goto_tb directly at the one call site, rather than maintaining a local wrapper. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- target/rx/translate.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/target/rx/translate.c b/target/rx/translate.c index 22a15ee11d..23a626438a 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -142,18 +142,9 @@ void rx_cpu_dump_state(CPUState *cs, FILE *f, int flags) } } -static bool use_goto_tb(DisasContext *dc, target_ulong dest) -{ - if (unlikely(dc->base.singlestep_enabled)) { - return false; - } else { - return true; - } -} - static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) { - if (use_goto_tb(dc, dest)) { + if (translator_use_goto_tb(&dc->base, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_i32(cpu_pc, dest); tcg_gen_exit_tb(dc->base.tb, n); From 7379c0c82e8164b55dbf605ba9b3988442a73025 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 20 Jun 2021 16:42:04 -0700 Subject: [PATCH 227/272] target/s390x: Use translator_use_goto_tb Reviewed-by: David Hildenbrand Signed-off-by: Richard Henderson --- target/s390x/translate.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/target/s390x/translate.c b/target/s390x/translate.c index 5af68e01c6..767e77ca19 100644 --- a/target/s390x/translate.c +++ b/target/s390x/translate.c @@ -696,12 +696,7 @@ static bool use_goto_tb(DisasContext *s, uint64_t dest) if (unlikely(use_exit_tb(s))) { return false; } -#ifndef CONFIG_USER_ONLY - return (dest & TARGET_PAGE_MASK) == (s->base.tb->pc & TARGET_PAGE_MASK) || - (dest & TARGET_PAGE_MASK) == (s->base.pc_next & TARGET_PAGE_MASK); -#else - return true; -#endif + return translator_use_goto_tb(&s->base, dest); } static void account_noninline_branch(DisasContext *s, int cc_op) From 95cc1cb39e12fbb5cca3688f8a7ce22116846520 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 20 Jun 2021 16:44:32 -0700 Subject: [PATCH 228/272] target/s390x: Remove use_exit_tb We have not needed to end a TB for I/O since ba3e7926691 ("icount: clean up cpu_can_io at the entry to the block"). In use_goto_tb, the check for singlestep_enabled is in the generic translator_use_goto_tb. In s390x_tr_tb_stop, the check for singlestep_enabled is in the preceding do_debug test. Which leaves only FLAG_MASK_PER: fold that test alone into the two callers of use_exit tb. Reviewed-by: David Hildenbrand Signed-off-by: Richard Henderson --- target/s390x/translate.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/target/s390x/translate.c b/target/s390x/translate.c index 767e77ca19..0cfe29d227 100644 --- a/target/s390x/translate.c +++ b/target/s390x/translate.c @@ -684,16 +684,9 @@ static void gen_op_calc_cc(DisasContext *s) set_cc_static(s); } -static bool use_exit_tb(DisasContext *s) -{ - return s->base.singlestep_enabled || - (tb_cflags(s->base.tb) & CF_LAST_IO) || - (s->base.tb->flags & FLAG_MASK_PER); -} - static bool use_goto_tb(DisasContext *s, uint64_t dest) { - if (unlikely(use_exit_tb(s))) { + if (unlikely(s->base.tb->flags & FLAG_MASK_PER)) { return false; } return translator_use_goto_tb(&s->base, dest); @@ -6633,7 +6626,7 @@ static void s390x_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) /* Exit the TB, either by raising a debug exception or by return. */ if (dc->do_debug) { gen_exception(EXCP_DEBUG); - } else if (use_exit_tb(dc) || + } else if ((dc->base.tb->flags & FLAG_MASK_PER) || dc->base.is_jmp == DISAS_PC_STALE_NOCHAIN) { tcg_gen_exit_tb(NULL, 0); } else { From 3f1e20984519ad3823438b73ea10035c0b2a9ffd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 20 Jun 2021 16:51:23 -0700 Subject: [PATCH 229/272] target/sh4: Use translator_use_goto_tb Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- target/sh4/translate.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/target/sh4/translate.c b/target/sh4/translate.c index 8a25a4362e..40898e2393 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -224,17 +224,12 @@ static inline bool use_exit_tb(DisasContext *ctx) return (ctx->tbflags & GUSA_EXCLUSIVE) != 0; } -static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest) +static bool use_goto_tb(DisasContext *ctx, target_ulong dest) { - /* Use a direct jump if in same page and singlestep not enabled */ - if (unlikely(ctx->base.singlestep_enabled || use_exit_tb(ctx))) { + if (use_exit_tb(ctx)) { return false; } -#ifndef CONFIG_USER_ONLY - return (ctx->base.tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); -#else - return true; -#endif + return translator_use_goto_tb(&ctx->base, dest); } static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) From 5645aa2e76c4ab8ab1418d5d0fa732d06765d44f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 20 Jun 2021 16:54:12 -0700 Subject: [PATCH 230/272] target/sparc: Use translator_use_goto_tb Reviewed-by: Mark Cave-Ayland Signed-off-by: Richard Henderson --- target/sparc/translate.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index f3fe7a0369..e530cb4aa8 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -338,23 +338,14 @@ static inline TCGv gen_dest_gpr(DisasContext *dc, int reg) } } -static inline bool use_goto_tb(DisasContext *s, target_ulong pc, - target_ulong npc) +static bool use_goto_tb(DisasContext *s, target_ulong pc, target_ulong npc) { - if (unlikely(s->base.singlestep_enabled || singlestep)) { - return false; - } - -#ifndef CONFIG_USER_ONLY - return (pc & TARGET_PAGE_MASK) == (s->base.tb->pc & TARGET_PAGE_MASK) && - (npc & TARGET_PAGE_MASK) == (s->base.tb->pc & TARGET_PAGE_MASK); -#else - return true; -#endif + return translator_use_goto_tb(&s->base, pc) && + translator_use_goto_tb(&s->base, npc); } -static inline void gen_goto_tb(DisasContext *s, int tb_num, - target_ulong pc, target_ulong npc) +static void gen_goto_tb(DisasContext *s, int tb_num, + target_ulong pc, target_ulong npc) { if (use_goto_tb(s, pc, npc)) { /* jump to same page: we can use a direct jump */ From d6b6f26170052452473fd8e674a29e337625dcdd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 20 Jun 2021 16:58:46 -0700 Subject: [PATCH 231/272] target/tricore: Use translator_use_goto_tb Just use translator_use_goto_tb directly at the one call site, rather than maintaining a local wrapper. Reviewed-by: Bastian Koppelmann Signed-off-by: Richard Henderson --- target/tricore/translate.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/target/tricore/translate.c b/target/tricore/translate.c index 2a814263de..09465ea013 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -3225,19 +3225,6 @@ static inline void gen_save_pc(target_ulong pc) tcg_gen_movi_tl(cpu_PC, pc); } -static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest) -{ - if (unlikely(ctx->base.singlestep_enabled)) { - return false; - } - -#ifndef CONFIG_USER_ONLY - return (ctx->base.tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); -#else - return true; -#endif -} - static void generate_qemu_excp(DisasContext *ctx, int excp) { TCGv_i32 tmp = tcg_const_i32(excp); @@ -3246,9 +3233,9 @@ static void generate_qemu_excp(DisasContext *ctx, int excp) tcg_temp_free(tmp); } -static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) +static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) { - if (use_goto_tb(ctx, dest)) { + if (translator_use_goto_tb(&ctx->base, dest)) { tcg_gen_goto_tb(n); gen_save_pc(dest); tcg_gen_exit_tb(ctx->base.tb, n); From 3806471563f9f1b568a32fdece189a1cecb5ca01 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 20 Jun 2021 17:00:46 -0700 Subject: [PATCH 232/272] target/tricore: Use tcg_gen_lookup_and_goto_ptr The non-single-step case of gen_goto_tb may use tcg_gen_lookup_and_goto_ptr to indirectly chain. Reviewed-by: Bastian Koppelmann Signed-off-by: Richard Henderson --- target/tricore/translate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/tricore/translate.c b/target/tricore/translate.c index 09465ea013..865020754d 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -3243,8 +3243,9 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) gen_save_pc(dest); if (ctx->base.singlestep_enabled) { generate_qemu_excp(ctx, EXCP_DEBUG); + } else { + tcg_gen_lookup_and_goto_ptr(); } - tcg_gen_exit_tb(NULL, 0); } } From 70c6eb46d780a1ad1a53a4cfbb1052973271a62d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 20 Jun 2021 17:05:35 -0700 Subject: [PATCH 233/272] target/xtensa: Use translator_use_goto_tb Reviewed-by: Max Filippov Signed-off-by: Richard Henderson --- target/xtensa/translate.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index d5da35f4fc..7094cfcf1d 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -405,11 +405,7 @@ static void gen_jump(DisasContext *dc, TCGv dest) static int adjust_jump_slot(DisasContext *dc, uint32_t dest, int slot) { - if (((dc->base.pc_first ^ dest) & TARGET_PAGE_MASK) != 0) { - return -1; - } else { - return slot; - } + return translator_use_goto_tb(&dc->base, dest) ? slot : -1; } static void gen_jumpi(DisasContext *dc, uint32_t dest, int slot) From 0849cb547844b7205af01455b82dc54956c978a9 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 10 Jun 2021 10:50:26 +0200 Subject: [PATCH 234/272] qemu-option: Drop dead assertion Commit c6ecec43b2 "qemu-option: Check return value instead of @err where convenient" simplified opts = qemu_opts_create(list, qdict_get_try_str(qdict, "id"), 1, &local_err); if (local_err) { error_propagate(errp, local_err); return NULL; } to opts = qemu_opts_create(list, qdict_get_try_str(qdict, "id"), 1, errp); if (!opts) { return NULL; } but neglected to delete assert(opts != NULL); Do that now. Signed-off-by: Markus Armbruster Reviewed-by: Thomas Huth Message-Id: <20210610085026.436081-1-armbru@redhat.com> Signed-off-by: Laurent Vivier --- util/qemu-option.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/util/qemu-option.c b/util/qemu-option.c index ee78e42216..61cb4a97bd 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -997,8 +997,6 @@ QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict, return NULL; } - assert(opts != NULL); - for (entry = qdict_first(qdict); entry; entry = qdict_next(qdict, entry)) { From 9bb5405482e7be4c0a6f259d4f18ea612d4a31ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 7 Mar 2021 08:48:33 +0100 Subject: [PATCH 235/272] memory: Display MemoryRegion name in read/write ops trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MemoryRegion names is cached on first call to memory_region_name(), so displaying the name is trace events is cheap. Add it for read / write ops. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20210307074833.143106-1-f4bug@amsat.org> Signed-off-by: Laurent Vivier --- softmmu/memory.c | 12 ++++++++---- softmmu/trace-events | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/softmmu/memory.c b/softmmu/memory.c index f0161515e9..24a97c8b1f 100644 --- a/softmmu/memory.c +++ b/softmmu/memory.c @@ -442,7 +442,8 @@ static MemTxResult memory_region_read_accessor(MemoryRegion *mr, trace_memory_region_subpage_read(get_cpu_index(), mr, addr, tmp, size); } else if (trace_event_get_state_backends(TRACE_MEMORY_REGION_OPS_READ)) { hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr); - trace_memory_region_ops_read(get_cpu_index(), mr, abs_addr, tmp, size); + trace_memory_region_ops_read(get_cpu_index(), mr, abs_addr, tmp, size, + memory_region_name(mr)); } memory_region_shift_read_access(value, shift, mask, tmp); return MEMTX_OK; @@ -464,7 +465,8 @@ static MemTxResult memory_region_read_with_attrs_accessor(MemoryRegion *mr, trace_memory_region_subpage_read(get_cpu_index(), mr, addr, tmp, size); } else if (trace_event_get_state_backends(TRACE_MEMORY_REGION_OPS_READ)) { hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr); - trace_memory_region_ops_read(get_cpu_index(), mr, abs_addr, tmp, size); + trace_memory_region_ops_read(get_cpu_index(), mr, abs_addr, tmp, size, + memory_region_name(mr)); } memory_region_shift_read_access(value, shift, mask, tmp); return r; @@ -484,7 +486,8 @@ static MemTxResult memory_region_write_accessor(MemoryRegion *mr, trace_memory_region_subpage_write(get_cpu_index(), mr, addr, tmp, size); } else if (trace_event_get_state_backends(TRACE_MEMORY_REGION_OPS_WRITE)) { hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr); - trace_memory_region_ops_write(get_cpu_index(), mr, abs_addr, tmp, size); + trace_memory_region_ops_write(get_cpu_index(), mr, abs_addr, tmp, size, + memory_region_name(mr)); } mr->ops->write(mr->opaque, addr, tmp, size); return MEMTX_OK; @@ -504,7 +507,8 @@ static MemTxResult memory_region_write_with_attrs_accessor(MemoryRegion *mr, trace_memory_region_subpage_write(get_cpu_index(), mr, addr, tmp, size); } else if (trace_event_get_state_backends(TRACE_MEMORY_REGION_OPS_WRITE)) { hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr); - trace_memory_region_ops_write(get_cpu_index(), mr, abs_addr, tmp, size); + trace_memory_region_ops_write(get_cpu_index(), mr, abs_addr, tmp, size, + memory_region_name(mr)); } return mr->ops->write_with_attrs(mr->opaque, addr, tmp, size, attrs); } diff --git a/softmmu/trace-events b/softmmu/trace-events index d18ac41e4e..7b278590a0 100644 --- a/softmmu/trace-events +++ b/softmmu/trace-events @@ -9,8 +9,8 @@ cpu_in(unsigned int addr, char size, unsigned int val) "addr 0x%x(%c) value %u" cpu_out(unsigned int addr, char size, unsigned int val) "addr 0x%x(%c) value %u" # memory.c -memory_region_ops_read(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u" -memory_region_ops_write(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u" +memory_region_ops_read(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size, const char *name) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u name '%s'" +memory_region_ops_write(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size, const char *name) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u name '%s'" memory_region_subpage_read(int cpu_index, void *mr, uint64_t offset, uint64_t value, unsigned size) "cpu %d mr %p offset 0x%"PRIx64" value 0x%"PRIx64" size %u" memory_region_subpage_write(int cpu_index, void *mr, uint64_t offset, uint64_t value, unsigned size) "cpu %d mr %p offset 0x%"PRIx64" value 0x%"PRIx64" size %u" memory_region_ram_device_read(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u" From a476123243617700e16d19237b12d51130d28563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 29 Jun 2021 07:14:00 +0200 Subject: [PATCH 236/272] misc: Fix "havn't" typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix "havn't (make)" -> "haven't (made)" typo. Reviewed-by: Luis Pires Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-Id: <20210629051400.2573253-1-f4bug@amsat.org> Signed-off-by: Laurent Vivier --- hw/usb/desc-msos.c | 2 +- target/s390x/translate.c | 6 ++++-- tcg/arm/tcg-target.c.inc | 6 ++++-- tcg/sparc/tcg-target.c.inc | 6 ++++-- tcg/tcg-op.c | 2 +- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/hw/usb/desc-msos.c b/hw/usb/desc-msos.c index 3a5ad7c8d0..836e38c67e 100644 --- a/hw/usb/desc-msos.c +++ b/hw/usb/desc-msos.c @@ -181,7 +181,7 @@ static int usb_desc_msos_prop(const USBDesc *desc, uint8_t *dest) if (desc->msos->Label) { /* - * Given as example in the specs. Havn't figured yet where + * Given as example in the specs. Haven't figured yet where * this label shows up in the windows gui. */ length += usb_desc_msos_prop_str(dest+length, MSOS_REG_SZ, diff --git a/target/s390x/translate.c b/target/s390x/translate.c index 03dab9f350..8822603a6e 100644 --- a/target/s390x/translate.c +++ b/target/s390x/translate.c @@ -6270,8 +6270,10 @@ static void extract_field(DisasFields *o, const DisasField *f, uint64_t insn) abort(); } - /* Validate that the "compressed" encoding we selected above is valid. - I.e. we havn't make two different original fields overlap. */ + /* + * Validate that the "compressed" encoding we selected above is valid. + * I.e. we haven't made two different original fields overlap. + */ assert(((o->presentC >> f->indexC) & 1) == 0); o->presentC |= 1 << f->indexC; o->presentO |= 1 << f->indexO; diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 7a761a602e..007ceee68e 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -2407,8 +2407,10 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) static void tcg_target_init(TCGContext *s) { - /* Only probe for the platform and capabilities if we havn't already - determined maximum values at compile time. */ + /* + * Only probe for the platform and capabilities if we haven't already + * determined maximum values at compile time. + */ #if !defined(use_idiv_instructions) || !defined(use_neon_instructions) { unsigned long hwcap = qemu_getauxval(AT_HWCAP); diff --git a/tcg/sparc/tcg-target.c.inc b/tcg/sparc/tcg-target.c.inc index a6ec94a094..688827968b 100644 --- a/tcg/sparc/tcg-target.c.inc +++ b/tcg/sparc/tcg-target.c.inc @@ -1690,8 +1690,10 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) static void tcg_target_init(TCGContext *s) { - /* Only probe for the platform and capabilities if we havn't already - determined maximum values at compile time. */ + /* + * Only probe for the platform and capabilities if we haven't already + * determined maximum values at compile time. + */ #ifndef use_vis3_instructions { unsigned long hwcap = qemu_getauxval(AT_HWCAP); diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 44d711c0fc..58a34b5147 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -2741,7 +2741,7 @@ void tcg_gen_goto_tb(unsigned idx) /* We only support two chained exits. */ tcg_debug_assert(idx <= TB_EXIT_IDXMAX); #ifdef CONFIG_DEBUG_TCG - /* Verify that we havn't seen this numbered exit before. */ + /* Verify that we haven't seen this numbered exit before. */ tcg_debug_assert((tcg_ctx->goto_tb_issue_mask & (1 << idx)) == 0); tcg_ctx->goto_tb_issue_mask |= 1 << idx; #endif From 7ef2408a96c4471383aecf263a7ea2bd51a3235c Mon Sep 17 00:00:00 2001 From: Hubert Jasudowicz Date: Thu, 1 Jul 2021 23:11:48 +0200 Subject: [PATCH 237/272] virtiofsd: Add missing newline in error message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hubert Jasudowicz Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Stefan Hajnoczi Message-Id: Signed-off-by: Laurent Vivier --- tools/virtiofsd/fuse_virtio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c index fa4aff9b0e..fc2564a603 100644 --- a/tools/virtiofsd/fuse_virtio.c +++ b/tools/virtiofsd/fuse_virtio.c @@ -917,7 +917,7 @@ static bool fv_socket_lock(struct fuse_session *se) dir = qemu_get_local_state_pathname("run/virtiofsd"); if (g_mkdir_with_parents(dir, S_IRWXU) < 0) { - fuse_log(FUSE_LOG_ERR, "%s: Failed to create directory %s: %s", + fuse_log(FUSE_LOG_ERR, "%s: Failed to create directory %s: %s\n", __func__, dir, strerror(errno)); return false; } From eb1960aac1f5b2cad24de300bda2726d63700290 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Tue, 6 Jul 2021 17:44:33 +0800 Subject: [PATCH 238/272] misc: Remove redundant new line in perror() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Li Zhijian Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20210706094433.1766952-1-lizhijian@cn.fujitsu.com> Signed-off-by: Laurent Vivier --- migration/rdma.c | 2 +- softmmu/cpus.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/migration/rdma.c b/migration/rdma.c index b6cc4bef4a..38a099f7ee 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -1131,7 +1131,7 @@ static int qemu_rdma_reg_whole_ram_blocks(RDMAContext *rdma) IBV_ACCESS_REMOTE_WRITE ); if (!local->block[i].mr) { - perror("Failed to register local dest ram block!\n"); + perror("Failed to register local dest ram block!"); break; } rdma->total_registrations++; diff --git a/softmmu/cpus.c b/softmmu/cpus.c index c3caaeb26e..071085f840 100644 --- a/softmmu/cpus.c +++ b/softmmu/cpus.c @@ -325,7 +325,7 @@ static void sigbus_reraise(void) sigaddset(&set, SIGBUS); pthread_sigmask(SIG_UNBLOCK, &set, NULL); } - perror("Failed to re-raise SIGBUS!\n"); + perror("Failed to re-raise SIGBUS!"); abort(); } From 4c6dd9a0262d39eb8570ba077b5320df682603d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 23 May 2021 11:40:40 +0200 Subject: [PATCH 239/272] hw/virtio: Document *_should_notify() are called within rcu_read_lock() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Such comments make reviewing this file somehow easier. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20210523094040.3516968-1-philmd@redhat.com> Signed-off-by: Laurent Vivier --- hw/virtio/virtio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 6dcf3baf56..874377f37a 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2447,6 +2447,7 @@ static void virtio_set_isr(VirtIODevice *vdev, int value) } } +/* Called within rcu_read_lock(). */ static bool virtio_split_should_notify(VirtIODevice *vdev, VirtQueue *vq) { uint16_t old, new; @@ -2483,6 +2484,7 @@ static bool vring_packed_need_event(VirtQueue *vq, bool wrap, return vring_need_event(off, new, old); } +/* Called within rcu_read_lock(). */ static bool virtio_packed_should_notify(VirtIODevice *vdev, VirtQueue *vq) { VRingPackedDescEvent e; From 3b51b506686f41dba55a0e9567c4d9a7ffb8632c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 6 Jul 2021 10:18:22 +0200 Subject: [PATCH 240/272] target/xtensa/xtensa-semi: Fix compilation problem on Haiku MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The errno numbers are very large on Haiku, so the linking currently fails there with a "final link failed: memory exhausted" error message. We should not use the errno number as array indexes here, thus convert the code to a switch-case statement instead. A clever compiler should be able to optimize this code in a similar way anway. Reported-by: Richard Zak Signed-off-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Acked-by: Max Filippov Reviewed-by: Richard Henderson Message-Id: <20210706081822.1316551-1-thuth@redhat.com> Signed-off-by: Laurent Vivier --- target/xtensa/xtensa-semi.c | 84 +++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 45 deletions(-) diff --git a/target/xtensa/xtensa-semi.c b/target/xtensa/xtensa-semi.c index 79f2b043f2..fa21b7e11f 100644 --- a/target/xtensa/xtensa-semi.c +++ b/target/xtensa/xtensa-semi.c @@ -95,59 +95,53 @@ enum { static uint32_t errno_h2g(int host_errno) { - static const uint32_t guest_errno[] = { - [EPERM] = TARGET_EPERM, - [ENOENT] = TARGET_ENOENT, - [ESRCH] = TARGET_ESRCH, - [EINTR] = TARGET_EINTR, - [EIO] = TARGET_EIO, - [ENXIO] = TARGET_ENXIO, - [E2BIG] = TARGET_E2BIG, - [ENOEXEC] = TARGET_ENOEXEC, - [EBADF] = TARGET_EBADF, - [ECHILD] = TARGET_ECHILD, - [EAGAIN] = TARGET_EAGAIN, - [ENOMEM] = TARGET_ENOMEM, - [EACCES] = TARGET_EACCES, - [EFAULT] = TARGET_EFAULT, + switch (host_errno) { + case 0: return 0; + case EPERM: return TARGET_EPERM; + case ENOENT: return TARGET_ENOENT; + case ESRCH: return TARGET_ESRCH; + case EINTR: return TARGET_EINTR; + case EIO: return TARGET_EIO; + case ENXIO: return TARGET_ENXIO; + case E2BIG: return TARGET_E2BIG; + case ENOEXEC: return TARGET_ENOEXEC; + case EBADF: return TARGET_EBADF; + case ECHILD: return TARGET_ECHILD; + case EAGAIN: return TARGET_EAGAIN; + case ENOMEM: return TARGET_ENOMEM; + case EACCES: return TARGET_EACCES; + case EFAULT: return TARGET_EFAULT; #ifdef ENOTBLK - [ENOTBLK] = TARGET_ENOTBLK, + case ENOTBLK: return TARGET_ENOTBLK; #endif - [EBUSY] = TARGET_EBUSY, - [EEXIST] = TARGET_EEXIST, - [EXDEV] = TARGET_EXDEV, - [ENODEV] = TARGET_ENODEV, - [ENOTDIR] = TARGET_ENOTDIR, - [EISDIR] = TARGET_EISDIR, - [EINVAL] = TARGET_EINVAL, - [ENFILE] = TARGET_ENFILE, - [EMFILE] = TARGET_EMFILE, - [ENOTTY] = TARGET_ENOTTY, + case EBUSY: return TARGET_EBUSY; + case EEXIST: return TARGET_EEXIST; + case EXDEV: return TARGET_EXDEV; + case ENODEV: return TARGET_ENODEV; + case ENOTDIR: return TARGET_ENOTDIR; + case EISDIR: return TARGET_EISDIR; + case EINVAL: return TARGET_EINVAL; + case ENFILE: return TARGET_ENFILE; + case EMFILE: return TARGET_EMFILE; + case ENOTTY: return TARGET_ENOTTY; #ifdef ETXTBSY - [ETXTBSY] = TARGET_ETXTBSY, + case ETXTBSY: return TARGET_ETXTBSY; #endif - [EFBIG] = TARGET_EFBIG, - [ENOSPC] = TARGET_ENOSPC, - [ESPIPE] = TARGET_ESPIPE, - [EROFS] = TARGET_EROFS, - [EMLINK] = TARGET_EMLINK, - [EPIPE] = TARGET_EPIPE, - [EDOM] = TARGET_EDOM, - [ERANGE] = TARGET_ERANGE, - [ENOSYS] = TARGET_ENOSYS, + case EFBIG: return TARGET_EFBIG; + case ENOSPC: return TARGET_ENOSPC; + case ESPIPE: return TARGET_ESPIPE; + case EROFS: return TARGET_EROFS; + case EMLINK: return TARGET_EMLINK; + case EPIPE: return TARGET_EPIPE; + case EDOM: return TARGET_EDOM; + case ERANGE: return TARGET_ERANGE; + case ENOSYS: return TARGET_ENOSYS; #ifdef ELOOP - [ELOOP] = TARGET_ELOOP, + case ELOOP: return TARGET_ELOOP; #endif }; - if (host_errno == 0) { - return 0; - } else if (host_errno > 0 && host_errno < ARRAY_SIZE(guest_errno) && - guest_errno[host_errno]) { - return guest_errno[host_errno]; - } else { - return TARGET_EINVAL; - } + return TARGET_EINVAL; } typedef struct XtensaSimConsole { From 179a808045f16e5d9fee06510f0b5ca5ff0c69e8 Mon Sep 17 00:00:00 2001 From: Olaf Hering Date: Thu, 8 Jul 2021 18:21:59 +0200 Subject: [PATCH 241/272] migration: fix typo in mig_throttle_guest_down comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes commit 3d0684b2ad82a5dde68e3f08b0d7786dccaf619c ("ram: Update all functions comments") Signed-off-by: Olaf Hering Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20210708162159.18045-1-olaf@aepfle.de> Signed-off-by: Laurent Vivier --- migration/ram.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migration/ram.c b/migration/ram.c index 723af67c2e..88ff34f574 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -600,7 +600,7 @@ static size_t save_page_header(RAMState *rs, QEMUFile *f, RAMBlock *block, } /** - * mig_throttle_guest_down: throotle down the guest + * mig_throttle_guest_down: throttle down the guest * * Reduce amount of guest cpu execution to hopefully slow down memory * writes. If guest dirty memory rate is reduced below the rate at From e28ffe90fde5702aa8716ac2fa1b4116cdcc9e61 Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Fri, 9 Jul 2021 07:06:00 -0500 Subject: [PATCH 242/272] util/guest-random: Fix size arg to tail memcpy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We know that in the body of this if statement i is less than len, so we really should be copying len - i bytes not i - len bytes. Fix this typo. Fixes: 8d8404f1564 ("util: Add qemu_guest_getrandom and associated routines") Signed-off-by: Mark Nelson Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20210709120600.11080-1-mdnelson8@gmail.com> Signed-off-by: Laurent Vivier --- util/guest-random.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/guest-random.c b/util/guest-random.c index 086115bd67..23643f86cc 100644 --- a/util/guest-random.c +++ b/util/guest-random.c @@ -38,7 +38,7 @@ static int glib_random_bytes(void *buf, size_t len) } if (i < len) { x = g_rand_int(rand); - __builtin_memcpy(buf + i, &x, i - len); + __builtin_memcpy(buf + i, &x, len - i); } return 0; } From d1c74ab3a1048a78b5fb8df06c1845e58111ee34 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 9 Jul 2021 19:45:42 -0700 Subject: [PATCH 243/272] tcg: Fix prologue disassembly In tcg_region_prologue_set, we reset TCGContext.code_gen_ptr. So do that after we've used it to dump the prologue contents. Fixes: b0a0794a0f16 Signed-off-by: Richard Henderson --- tcg/tcg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tcg/tcg.c b/tcg/tcg.c index 4dd4084419..ed86a70b79 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -752,8 +752,6 @@ void tcg_prologue_init(TCGContext *s) (uintptr_t)s->code_buf, prologue_size); #endif - tcg_region_prologue_set(s); - #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM)) { FILE *logfile = qemu_log_lock(); @@ -795,6 +793,8 @@ void tcg_prologue_init(TCGContext *s) tcg_debug_assert(tcg_code_gen_epilogue != NULL); } #endif + + tcg_region_prologue_set(s); } void tcg_func_start(TCGContext *s) From 50b208b848d9497cf6d320b2d4a38a8f07354f5d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 19 Jun 2021 23:23:17 -0700 Subject: [PATCH 244/272] target/i386: Use cpu_breakpoint_test in breakpoint_handler The loop is performing a simple boolean test for the existence of a BP_CPU breakpoint at EIP. Plus it gets the iteration wrong, if we happen to have a BP_GDB breakpoint at the same address. We have a function for this: cpu_breakpoint_test. Signed-off-by: Richard Henderson Reviewed-by: Eduardo Habkost Message-Id: <20210620062317.1399034-1-richard.henderson@linaro.org> --- target/i386/tcg/sysemu/bpt_helper.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/target/i386/tcg/sysemu/bpt_helper.c b/target/i386/tcg/sysemu/bpt_helper.c index 9bdf7e170b..f1fb479ad9 100644 --- a/target/i386/tcg/sysemu/bpt_helper.c +++ b/target/i386/tcg/sysemu/bpt_helper.c @@ -210,7 +210,6 @@ void breakpoint_handler(CPUState *cs) { X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; - CPUBreakpoint *bp; if (cs->watchpoint_hit) { if (cs->watchpoint_hit->flags & BP_CPU) { @@ -222,14 +221,9 @@ void breakpoint_handler(CPUState *cs) } } } else { - QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { - if (bp->pc == env->eip) { - if (bp->flags & BP_CPU) { - check_hw_breakpoints(env, true); - raise_exception(env, EXCP01_DB); - } - break; - } + if (cpu_breakpoint_test(cs, env->eip, BP_CPU)) { + check_hw_breakpoints(env, true); + raise_exception(env, EXCP01_DB); } } } From 4288eb26a08593d4ad53c07aca4f2193ab0d72af Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jun 2021 12:28:29 -0700 Subject: [PATCH 245/272] accel/tcg: Move helper_lookup_tb_ptr to cpu-exec.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will allow additional code sharing. No functional change. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 30 ++++++++++++++++++++++++++++++ accel/tcg/tcg-runtime.c | 22 ---------------------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index ad1279d2ed..fb6668606f 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -38,6 +38,7 @@ #include "exec/cpu-all.h" #include "sysemu/cpu-timers.h" #include "sysemu/replay.h" +#include "exec/helper-proto.h" #include "tb-hash.h" #include "tb-lookup.h" #include "tb-context.h" @@ -145,6 +146,35 @@ static void init_delay_params(SyncClocks *sc, const CPUState *cpu) } #endif /* CONFIG USER ONLY */ +/** + * helper_lookup_tb_ptr: quick check for next tb + * @env: current cpu state + * + * Look for an existing TB matching the current cpu state. + * If found, return the code pointer. If not found, return + * the tcg epilogue so that we return into cpu_tb_exec. + */ +const void *HELPER(lookup_tb_ptr)(CPUArchState *env) +{ + CPUState *cpu = env_cpu(env); + TranslationBlock *tb; + target_ulong cs_base, pc; + uint32_t flags; + + cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); + + tb = tb_lookup(cpu, pc, cs_base, flags, curr_cflags(cpu)); + if (tb == NULL) { + return tcg_code_gen_epilogue; + } + qemu_log_mask_and_addr(CPU_LOG_EXEC, pc, + "Chain %d: %p [" + TARGET_FMT_lx "/" TARGET_FMT_lx "/%#x] %s\n", + cpu->cpu_index, tb->tc.ptr, cs_base, pc, flags, + lookup_symbol(pc)); + return tb->tc.ptr; +} + /* Execute a TB, and fix up the CPU state afterwards if necessary */ /* * Disable CFI checks. diff --git a/accel/tcg/tcg-runtime.c b/accel/tcg/tcg-runtime.c index 66ac830e2f..e4e030043f 100644 --- a/accel/tcg/tcg-runtime.c +++ b/accel/tcg/tcg-runtime.c @@ -30,7 +30,6 @@ #include "disas/disas.h" #include "exec/log.h" #include "tcg/tcg.h" -#include "tb-lookup.h" /* 32-bit helpers */ @@ -145,27 +144,6 @@ uint64_t HELPER(ctpop_i64)(uint64_t arg) return ctpop64(arg); } -const void *HELPER(lookup_tb_ptr)(CPUArchState *env) -{ - CPUState *cpu = env_cpu(env); - TranslationBlock *tb; - target_ulong cs_base, pc; - uint32_t flags; - - cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); - - tb = tb_lookup(cpu, pc, cs_base, flags, curr_cflags(cpu)); - if (tb == NULL) { - return tcg_code_gen_epilogue; - } - qemu_log_mask_and_addr(CPU_LOG_EXEC, pc, - "Chain %d: %p [" - TARGET_FMT_lx "/" TARGET_FMT_lx "/%#x] %s\n", - cpu->cpu_index, tb->tc.ptr, cs_base, pc, flags, - lookup_symbol(pc)); - return tb->tc.ptr; -} - void HELPER(exit_atomic)(CPUArchState *env) { cpu_loop_exit_atomic(env_cpu(env), GETPC()); From 632cb63d9282ca58b016b6f95abcc5e42e5bda0e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jun 2021 12:31:19 -0700 Subject: [PATCH 246/272] accel/tcg: Move tb_lookup to cpu-exec.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we've moved helper_lookup_tb_ptr, the only user of tb-lookup.h is cpu-exec.c; merge the contents in. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 31 ++++++++++++++++++++++++++- accel/tcg/tb-lookup.h | 49 ------------------------------------------- 2 files changed, 30 insertions(+), 50 deletions(-) delete mode 100644 accel/tcg/tb-lookup.h diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index fb6668606f..0d92698030 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -40,7 +40,6 @@ #include "sysemu/replay.h" #include "exec/helper-proto.h" #include "tb-hash.h" -#include "tb-lookup.h" #include "tb-context.h" #include "internal.h" @@ -146,6 +145,36 @@ static void init_delay_params(SyncClocks *sc, const CPUState *cpu) } #endif /* CONFIG USER ONLY */ +/* Might cause an exception, so have a longjmp destination ready */ +static inline TranslationBlock *tb_lookup(CPUState *cpu, target_ulong pc, + target_ulong cs_base, + uint32_t flags, uint32_t cflags) +{ + TranslationBlock *tb; + uint32_t hash; + + /* we should never be trying to look up an INVALID tb */ + tcg_debug_assert(!(cflags & CF_INVALID)); + + hash = tb_jmp_cache_hash_func(pc); + tb = qatomic_rcu_read(&cpu->tb_jmp_cache[hash]); + + if (likely(tb && + tb->pc == pc && + tb->cs_base == cs_base && + tb->flags == flags && + tb->trace_vcpu_dstate == *cpu->trace_dstate && + tb_cflags(tb) == cflags)) { + return tb; + } + tb = tb_htable_lookup(cpu, pc, cs_base, flags, cflags); + if (tb == NULL) { + return NULL; + } + qatomic_set(&cpu->tb_jmp_cache[hash], tb); + return tb; +} + /** * helper_lookup_tb_ptr: quick check for next tb * @env: current cpu state diff --git a/accel/tcg/tb-lookup.h b/accel/tcg/tb-lookup.h deleted file mode 100644 index 9c9e0079da..0000000000 --- a/accel/tcg/tb-lookup.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2017, Emilio G. Cota - * - * License: GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ -#ifndef EXEC_TB_LOOKUP_H -#define EXEC_TB_LOOKUP_H - -#ifdef NEED_CPU_H -#include "cpu.h" -#else -#include "exec/poison.h" -#endif - -#include "exec/exec-all.h" -#include "tb-hash.h" - -/* Might cause an exception, so have a longjmp destination ready */ -static inline TranslationBlock *tb_lookup(CPUState *cpu, target_ulong pc, - target_ulong cs_base, - uint32_t flags, uint32_t cflags) -{ - TranslationBlock *tb; - uint32_t hash; - - /* we should never be trying to look up an INVALID tb */ - tcg_debug_assert(!(cflags & CF_INVALID)); - - hash = tb_jmp_cache_hash_func(pc); - tb = qatomic_rcu_read(&cpu->tb_jmp_cache[hash]); - - if (likely(tb && - tb->pc == pc && - tb->cs_base == cs_base && - tb->flags == flags && - tb->trace_vcpu_dstate == *cpu->trace_dstate && - tb_cflags(tb) == cflags)) { - return tb; - } - tb = tb_htable_lookup(cpu, pc, cs_base, flags, cflags); - if (tb == NULL) { - return NULL; - } - qatomic_set(&cpu->tb_jmp_cache[hash], tb); - return tb; -} - -#endif /* EXEC_TB_LOOKUP_H */ From abb0cd9349453d6a45af2ab9317e8f08408485f4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jun 2021 13:17:18 -0700 Subject: [PATCH 247/272] accel/tcg: Split out log_cpu_exec Split out CPU_LOG_EXEC and CPU_LOG_TB_CPU logging from cpu_tb_exec to a new function. Perform only one pc range check after a combined mask check. Use the new function in lookup_tb_ptr. This enables CPU_LOG_TB_CPU between indirectly chained tbs. Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 61 ++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 0d92698030..67ed25beb9 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -175,6 +175,36 @@ static inline TranslationBlock *tb_lookup(CPUState *cpu, target_ulong pc, return tb; } +static inline void log_cpu_exec(target_ulong pc, CPUState *cpu, + const TranslationBlock *tb) +{ + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_CPU | CPU_LOG_EXEC)) + && qemu_log_in_addr_range(pc)) { + + qemu_log_mask(CPU_LOG_EXEC, + "Trace %d: %p [" TARGET_FMT_lx + "/" TARGET_FMT_lx "/%#x] %s\n", + cpu->cpu_index, tb->tc.ptr, tb->cs_base, pc, tb->flags, + lookup_symbol(pc)); + +#if defined(DEBUG_DISAS) + if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) { + FILE *logfile = qemu_log_lock(); + int flags = 0; + + if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) { + flags |= CPU_DUMP_FPU; + } +#if defined(TARGET_I386) + flags |= CPU_DUMP_CCOP; +#endif + log_cpu_state(cpu, flags); + qemu_log_unlock(logfile); + } +#endif /* DEBUG_DISAS */ + } +} + /** * helper_lookup_tb_ptr: quick check for next tb * @env: current cpu state @@ -196,11 +226,9 @@ const void *HELPER(lookup_tb_ptr)(CPUArchState *env) if (tb == NULL) { return tcg_code_gen_epilogue; } - qemu_log_mask_and_addr(CPU_LOG_EXEC, pc, - "Chain %d: %p [" - TARGET_FMT_lx "/" TARGET_FMT_lx "/%#x] %s\n", - cpu->cpu_index, tb->tc.ptr, cs_base, pc, flags, - lookup_symbol(pc)); + + log_cpu_exec(pc, cpu, tb); + return tb->tc.ptr; } @@ -222,28 +250,7 @@ cpu_tb_exec(CPUState *cpu, TranslationBlock *itb, int *tb_exit) TranslationBlock *last_tb; const void *tb_ptr = itb->tc.ptr; - qemu_log_mask_and_addr(CPU_LOG_EXEC, itb->pc, - "Trace %d: %p [" - TARGET_FMT_lx "/" TARGET_FMT_lx "/%#x] %s\n", - cpu->cpu_index, itb->tc.ptr, - itb->cs_base, itb->pc, itb->flags, - lookup_symbol(itb->pc)); - -#if defined(DEBUG_DISAS) - if (qemu_loglevel_mask(CPU_LOG_TB_CPU) - && qemu_log_in_addr_range(itb->pc)) { - FILE *logfile = qemu_log_lock(); - int flags = 0; - if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) { - flags |= CPU_DUMP_FPU; - } -#if defined(TARGET_I386) - flags |= CPU_DUMP_CCOP; -#endif - log_cpu_state(cpu, flags); - qemu_log_unlock(logfile); - } -#endif /* DEBUG_DISAS */ + log_cpu_exec(itb->pc, cpu, itb); qemu_thread_jit_execute(); ret = tcg_qemu_tb_exec(env, tb_ptr); From 7eabad361979bbf76dff4d91bc7af35e309c8c26 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 30 Jun 2021 08:31:46 -0700 Subject: [PATCH 248/272] accel/tcg: Log tb->cflags with -d exec 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/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 67ed25beb9..e22bcb99f7 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -183,9 +183,9 @@ static inline void log_cpu_exec(target_ulong pc, CPUState *cpu, qemu_log_mask(CPU_LOG_EXEC, "Trace %d: %p [" TARGET_FMT_lx - "/" TARGET_FMT_lx "/%#x] %s\n", - cpu->cpu_index, tb->tc.ptr, tb->cs_base, pc, tb->flags, - lookup_symbol(pc)); + "/" TARGET_FMT_lx "/%08x/%08x] %s\n", + cpu->cpu_index, tb->tc.ptr, tb->cs_base, pc, + tb->flags, tb->cflags, lookup_symbol(pc)); #if defined(DEBUG_DISAS) if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) { From f4e01e30217b6778e478cf00975daed7a54bc051 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jun 2021 14:47:39 -0700 Subject: [PATCH 249/272] tcg: Remove TCG_TARGET_HAS_goto_ptr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since 6eea04347eb6, all tcg backends support goto_ptr. Remove the conditional, making support mandatory. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/tcg-opc.h | 3 +-- tcg/aarch64/tcg-target.h | 1 - tcg/arm/tcg-target.h | 1 - tcg/i386/tcg-target.h | 1 - tcg/mips/tcg-target.h | 1 - tcg/ppc/tcg-target.h | 1 - tcg/riscv/tcg-target.h | 1 - tcg/s390/tcg-target.h | 1 - tcg/sparc/tcg-target.h | 1 - tcg/tcg-op.c | 2 +- tcg/tcg.c | 8 ++------ tcg/tci/tcg-target.h | 1 - 12 files changed, 4 insertions(+), 18 deletions(-) diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 993992373e..675873e200 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -194,8 +194,7 @@ DEF(insn_start, 0, 0, TLADDR_ARGS * TARGET_INSN_START_WORDS, TCG_OPF_NOT_PRESENT) DEF(exit_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END) DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END) -DEF(goto_ptr, 0, 1, 0, - TCG_OPF_BB_EXIT | TCG_OPF_BB_END | IMPL(TCG_TARGET_HAS_goto_ptr)) +DEF(goto_ptr, 0, 1, 0, TCG_OPF_BB_EXIT | TCG_OPF_BB_END) DEF(plugin_cb_start, 0, 0, 3, TCG_OPF_NOT_PRESENT) DEF(plugin_cb_end, 0, 0, 0, TCG_OPF_NOT_PRESENT) diff --git a/tcg/aarch64/tcg-target.h b/tcg/aarch64/tcg-target.h index 551baf8da3..7a93ac8023 100644 --- a/tcg/aarch64/tcg-target.h +++ b/tcg/aarch64/tcg-target.h @@ -88,7 +88,6 @@ typedef enum { #define TCG_TARGET_HAS_mulsh_i32 0 #define TCG_TARGET_HAS_extrl_i64_i32 0 #define TCG_TARGET_HAS_extrh_i64_i32 0 -#define TCG_TARGET_HAS_goto_ptr 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_div_i64 1 diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h index 95fcef33bc..d113b7f8db 100644 --- a/tcg/arm/tcg-target.h +++ b/tcg/arm/tcg-target.h @@ -148,7 +148,6 @@ extern bool use_neon_instructions; #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_goto_ptr 1 #define TCG_TARGET_HAS_direct_jump 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h index ac10066c3e..b00a6da293 100644 --- a/tcg/i386/tcg-target.h +++ b/tcg/i386/tcg-target.h @@ -135,7 +135,6 @@ extern bool have_movbe; #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_goto_ptr 1 #define TCG_TARGET_HAS_direct_jump 1 #if TCG_TARGET_REG_BITS == 64 diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h index e81e824cab..3a62055f04 100644 --- a/tcg/mips/tcg-target.h +++ b/tcg/mips/tcg-target.h @@ -136,7 +136,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_muluh_i32 1 #define TCG_TARGET_HAS_mulsh_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_goto_ptr 1 #define TCG_TARGET_HAS_direct_jump 1 #if TCG_TARGET_REG_BITS == 64 diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h index c13ed5640a..0943192cde 100644 --- a/tcg/ppc/tcg-target.h +++ b/tcg/ppc/tcg-target.h @@ -108,7 +108,6 @@ extern bool have_vsx; #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_goto_ptr 1 #define TCG_TARGET_HAS_direct_jump 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 diff --git a/tcg/riscv/tcg-target.h b/tcg/riscv/tcg-target.h index 87ea94666b..ef78b99e98 100644 --- a/tcg/riscv/tcg-target.h +++ b/tcg/riscv/tcg-target.h @@ -85,7 +85,6 @@ typedef enum { #define TCG_TARGET_CALL_STACK_OFFSET 0 /* optional instructions */ -#define TCG_TARGET_HAS_goto_ptr 1 #define TCG_TARGET_HAS_movcond_i32 0 #define TCG_TARGET_HAS_div_i32 1 #define TCG_TARGET_HAS_rem_i32 1 diff --git a/tcg/s390/tcg-target.h b/tcg/s390/tcg-target.h index b04b72b7eb..2e4ede2ea2 100644 --- a/tcg/s390/tcg-target.h +++ b/tcg/s390/tcg-target.h @@ -98,7 +98,6 @@ extern uint64_t s390_facilities; #define TCG_TARGET_HAS_mulsh_i32 0 #define TCG_TARGET_HAS_extrl_i64_i32 0 #define TCG_TARGET_HAS_extrh_i64_i32 0 -#define TCG_TARGET_HAS_goto_ptr 1 #define TCG_TARGET_HAS_direct_jump (s390_facilities & FACILITY_GEN_INST_EXT) #define TCG_TARGET_HAS_qemu_st8_i32 0 diff --git a/tcg/sparc/tcg-target.h b/tcg/sparc/tcg-target.h index 86bb9a2d39..c050763049 100644 --- a/tcg/sparc/tcg-target.h +++ b/tcg/sparc/tcg-target.h @@ -121,7 +121,6 @@ extern bool use_vis3_instructions; #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_goto_ptr 1 #define TCG_TARGET_HAS_direct_jump 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 44d711c0fc..3d5db9a33c 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -2754,7 +2754,7 @@ void tcg_gen_goto_tb(unsigned idx) void tcg_gen_lookup_and_goto_ptr(void) { - if (TCG_TARGET_HAS_goto_ptr && !qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) { + if (!qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) { TCGv_ptr ptr; plugin_gen_disable_mem_helpers(); diff --git a/tcg/tcg.c b/tcg/tcg.c index ed86a70b79..4142d42d77 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -789,9 +789,7 @@ void tcg_prologue_init(TCGContext *s) * For tci, we use NULL as the signal to return from the interpreter, * so skip this check. */ - if (TCG_TARGET_HAS_goto_ptr) { - tcg_debug_assert(tcg_code_gen_epilogue != NULL); - } + tcg_debug_assert(tcg_code_gen_epilogue != NULL); #endif tcg_region_prologue_set(s); @@ -1176,6 +1174,7 @@ bool tcg_op_supported(TCGOpcode op) case INDEX_op_insn_start: 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: @@ -1185,9 +1184,6 @@ bool tcg_op_supported(TCGOpcode op) case INDEX_op_qemu_st8_i32: return TCG_TARGET_HAS_qemu_st8_i32; - case INDEX_op_goto_ptr: - return TCG_TARGET_HAS_goto_ptr; - case INDEX_op_mov_i32: case INDEX_op_setcond_i32: case INDEX_op_brcond_i32: diff --git a/tcg/tci/tcg-target.h b/tcg/tci/tcg-target.h index 7b6089f304..033e613f24 100644 --- a/tcg/tci/tcg-target.h +++ b/tcg/tci/tcg-target.h @@ -87,7 +87,6 @@ #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_goto_ptr 1 #define TCG_TARGET_HAS_direct_jump 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 From ad1a706f386c2281adb0b09257d892735e405834 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 1 Jul 2021 08:10:53 -0700 Subject: [PATCH 250/272] cpu: Add breakpoint tracepoints 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 --- cpu.c | 13 +++++++++---- trace-events | 5 +++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/cpu.c b/cpu.c index 164fefeaa3..83059537d7 100644 --- a/cpu.c +++ b/cpu.c @@ -38,6 +38,7 @@ #include "exec/translate-all.h" #include "exec/log.h" #include "hw/core/accel-cpu.h" +#include "trace/trace-root.h" uintptr_t qemu_host_page_size; intptr_t qemu_host_page_mask; @@ -285,6 +286,8 @@ int cpu_breakpoint_insert(CPUState *cpu, vaddr pc, int flags, if (breakpoint) { *breakpoint = bp; } + + trace_breakpoint_insert(cpu->cpu_index, pc, flags); return 0; } @@ -303,13 +306,14 @@ int cpu_breakpoint_remove(CPUState *cpu, vaddr pc, int flags) } /* Remove a specific breakpoint by reference. */ -void cpu_breakpoint_remove_by_ref(CPUState *cpu, CPUBreakpoint *breakpoint) +void cpu_breakpoint_remove_by_ref(CPUState *cpu, CPUBreakpoint *bp) { - QTAILQ_REMOVE(&cpu->breakpoints, breakpoint, entry); + QTAILQ_REMOVE(&cpu->breakpoints, bp, entry); - breakpoint_invalidate(cpu, breakpoint->pc); + breakpoint_invalidate(cpu, bp->pc); - g_free(breakpoint); + trace_breakpoint_remove(cpu->cpu_index, bp->pc, bp->flags); + g_free(bp); } /* Remove all matching breakpoints. */ @@ -337,6 +341,7 @@ void cpu_single_step(CPUState *cpu, int enabled) /* XXX: only flush what is necessary */ tb_flush(cpu); } + trace_breakpoint_singlestep(cpu->cpu_index, enabled); } } diff --git a/trace-events b/trace-events index 765fe251e6..c4cca29939 100644 --- a/trace-events +++ b/trace-events @@ -25,6 +25,11 @@ # # The should be a sprintf()-compatible format string. +# cpu.c +breakpoint_insert(int cpu_index, uint64_t pc, int flags) "cpu=%d pc=0x%" PRIx64 " flags=0x%x" +breakpoint_remove(int cpu_index, uint64_t pc, int flags) "cpu=%d pc=0x%" PRIx64 " flags=0x%x" +breakpoint_singlestep(int cpu_index, int enabled) "cpu=%d enable=%d" + # dma-helpers.c dma_blk_io(void *dbs, void *bs, int64_t offset, bool to_dev) "dbs=%p bs=%p offset=%" PRId64 " to_dev=%d" dma_aio_cancel(void *dbs) "dbs=%p" From 46fd3201cb02d95f42f659121b13cf5e2633db1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 16 Apr 2021 18:18:58 +0200 Subject: [PATCH 251/272] hw/pci-host: Rename Raven ASIC PCI bridge as raven.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ASIC PCI bridge chipset from Motorola is named 'Raven'. This chipset is used in the PowerPC Reference Platform (PReP), but not restricted to it. Rename it accordingly. Signed-off-by: Philippe Mathieu-Daudé Acked-by: David Gibson Message-Id: <20210417103028.601124-5-f4bug@amsat.org> --- MAINTAINERS | 2 +- hw/pci-host/Kconfig | 2 +- hw/pci-host/meson.build | 2 +- hw/pci-host/{prep.c => raven.c} | 0 hw/ppc/Kconfig | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename hw/pci-host/{prep.c => raven.c} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 40d095dbbd..36eb0cb9c3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1304,7 +1304,7 @@ S: Maintained F: hw/ppc/prep.c F: hw/ppc/prep_systemio.c F: hw/ppc/rs6000_mc.c -F: hw/pci-host/prep.[hc] +F: hw/pci-host/raven.c F: hw/isa/i82378.c F: hw/isa/pc87312.c F: hw/dma/i82374.c diff --git a/hw/pci-host/Kconfig b/hw/pci-host/Kconfig index 79c20bf28b..84494400b8 100644 --- a/hw/pci-host/Kconfig +++ b/hw/pci-host/Kconfig @@ -6,7 +6,7 @@ config XEN_IGD_PASSTHROUGH default y depends on XEN && PCI_I440FX -config PREP_PCI +config RAVEN_PCI bool select PCI select OR_IRQ diff --git a/hw/pci-host/meson.build b/hw/pci-host/meson.build index 1698d3a192..4c4f39c15c 100644 --- a/hw/pci-host/meson.build +++ b/hw/pci-host/meson.build @@ -13,7 +13,7 @@ pci_ss.add(when: 'CONFIG_REMOTE_PCIHOST', if_true: files('remote.c')) pci_ss.add(when: 'CONFIG_SH_PCI', if_true: files('sh_pci.c')) # PPC devices -pci_ss.add(when: 'CONFIG_PREP_PCI', if_true: files('prep.c')) +pci_ss.add(when: 'CONFIG_RAVEN_PCI', if_true: files('raven.c')) pci_ss.add(when: 'CONFIG_GRACKLE_PCI', if_true: files('grackle.c')) # NewWorld PowerMac pci_ss.add(when: 'CONFIG_UNIN_PCI', if_true: files('uninorth.c')) diff --git a/hw/pci-host/prep.c b/hw/pci-host/raven.c similarity index 100% rename from hw/pci-host/prep.c rename to hw/pci-host/raven.c diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig index 7fcafec60a..322a7eb031 100644 --- a/hw/ppc/Kconfig +++ b/hw/ppc/Kconfig @@ -85,7 +85,7 @@ config PREP imply PCI_DEVICES imply TEST_DEVICES select CS4231A - select PREP_PCI + select RAVEN_PCI select I82378 select LSI_SCSI_PCI select M48T59 From 64e73920470f3ab848458cd965af3590430d321c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 16 Apr 2021 18:15:56 +0200 Subject: [PATCH 252/272] hw/pci-host/raven: Add PCI_IO_BASE_ADDR definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than using the magic 0x80000000 number for the PCI I/O BAR physical address on the main system bus, use a definition. Signed-off-by: Philippe Mathieu-Daudé Acked-by: David Gibson Message-Id: <20210417103028.601124-6-f4bug@amsat.org> --- hw/pci-host/raven.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/hw/pci-host/raven.c b/hw/pci-host/raven.c index 9fef74fc56..3be27f0a14 100644 --- a/hw/pci-host/raven.c +++ b/hw/pci-host/raven.c @@ -81,6 +81,8 @@ struct PRePPCIState { #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) { int i; @@ -158,7 +160,7 @@ static uint64_t raven_io_read(void *opaque, hwaddr addr, uint8_t buf[4]; addr = raven_io_address(s, addr); - address_space_read(&s->pci_io_as, addr + 0x80000000, + address_space_read(&s->pci_io_as, addr + PCI_IO_BASE_ADDR, MEMTXATTRS_UNSPECIFIED, buf, size); if (size == 1) { @@ -190,7 +192,7 @@ static void raven_io_write(void *opaque, hwaddr addr, g_assert_not_reached(); } - address_space_write(&s->pci_io_as, addr + 0x80000000, + address_space_write(&s->pci_io_as, addr + PCI_IO_BASE_ADDR, MEMTXATTRS_UNSPECIFIED, buf, size); } @@ -293,8 +295,9 @@ static void raven_pcihost_initfn(Object *obj) address_space_init(&s->pci_io_as, &s->pci_io, "raven-io"); /* CPU address space */ - memory_region_add_subregion(address_space_mem, 0x80000000, &s->pci_io); - memory_region_add_subregion_overlap(address_space_mem, 0x80000000, + memory_region_add_subregion(address_space_mem, PCI_IO_BASE_ADDR, + &s->pci_io); + memory_region_add_subregion_overlap(address_space_mem, PCI_IO_BASE_ADDR, &s->pci_io_non_contiguous, 1); memory_region_add_subregion(address_space_mem, 0xc0000000, &s->pci_memory); pci_root_bus_new_inplace(&s->pci_bus, sizeof(s->pci_bus), DEVICE(obj), NULL, From 2d4ab117bebb90ad7e7e65629f99f9e82ba32053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 13 Feb 2021 12:24:44 +0100 Subject: [PATCH 253/272] target/mips/tx79: Introduce PAND/POR/PXOR/PNOR opcodes (parallel logic) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce the parallel logic opcodes: - PAND (Parallel AND) - POR (Parallel OR) - PXOR (Parallel XOR) - PNOR (Parallel NOR) Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20210214175912.732946-16-f4bug@amsat.org> Signed-off-by: Philippe Mathieu-Daudé --- target/mips/tcg/tx79.decode | 4 +++ target/mips/tcg/tx79_translate.c | 54 ++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/target/mips/tcg/tx79.decode b/target/mips/tcg/tx79.decode index 0f748b53a6..26c80b9bce 100644 --- a/target/mips/tcg/tx79.decode +++ b/target/mips/tcg/tx79.decode @@ -32,8 +32,12 @@ MTLO1 011100 ..... 0000000000 00000 010011 @rs # MMI2 PCPYLD 011100 ..... ..... ..... 01110 001001 @rs_rt_rd +PAND 011100 ..... ..... ..... 10010 001001 @rs_rt_rd +PXOR 011100 ..... ..... ..... 10011 001001 @rs_rt_rd # MMI3 PCPYUD 011100 ..... ..... ..... 01110 101001 @rs_rt_rd +POR 011100 ..... ..... ..... 10010 101001 @rs_rt_rd +PNOR 011100 ..... ..... ..... 10011 101001 @rs_rt_rd PCPYH 011100 00000 ..... ..... 11011 101001 @rt_rd diff --git a/target/mips/tcg/tx79_translate.c b/target/mips/tcg/tx79_translate.c index ad83774b97..00364f10d4 100644 --- a/target/mips/tcg/tx79_translate.c +++ b/target/mips/tcg/tx79_translate.c @@ -2,6 +2,7 @@ * Toshiba TX79-specific instructions translation routines * * Copyright (c) 2018 Fredrik Noring + * Copyright (c) 2021 Philippe Mathieu-Daudé * * SPDX-License-Identifier: GPL-2.0-or-later */ @@ -114,6 +115,35 @@ static bool trans_MTLO1(DisasContext *ctx, arg_rtype *a) * PSUBUW rd, rs, rt Parallel Subtract with Unsigned saturation Word */ +static bool trans_parallel_arith(DisasContext *ctx, arg_rtype *a, + void (*gen_logic_i64)(TCGv_i64, TCGv_i64, TCGv_i64)) +{ + TCGv_i64 ax, bx; + + if (a->rd == 0) { + /* nop */ + return true; + } + + ax = tcg_temp_new_i64(); + bx = tcg_temp_new_i64(); + + /* Lower half */ + gen_load_gpr(ax, a->rs); + gen_load_gpr(bx, a->rt); + gen_logic_i64(cpu_gpr[a->rd], ax, bx); + + /* Upper half */ + gen_load_gpr_hi(ax, a->rs); + gen_load_gpr_hi(bx, a->rt); + gen_logic_i64(cpu_gpr_hi[a->rd], ax, bx); + + tcg_temp_free(bx); + tcg_temp_free(ax); + + return true; +} + /* * Min/Max (4 instructions) * ------------------------ @@ -139,6 +169,30 @@ static bool trans_MTLO1(DisasContext *ctx, arg_rtype *a) * PNOR rd, rs, rt Parallel NOR */ +/* Parallel And */ +static bool trans_PAND(DisasContext *ctx, arg_rtype *a) +{ + return trans_parallel_arith(ctx, a, tcg_gen_and_i64); +} + +/* Parallel Or */ +static bool trans_POR(DisasContext *ctx, arg_rtype *a) +{ + return trans_parallel_arith(ctx, a, tcg_gen_or_i64); +} + +/* Parallel Exclusive Or */ +static bool trans_PXOR(DisasContext *ctx, arg_rtype *a) +{ + return trans_parallel_arith(ctx, a, tcg_gen_xor_i64); +} + +/* Parallel Not Or */ +static bool trans_PNOR(DisasContext *ctx, arg_rtype *a) +{ + return trans_parallel_arith(ctx, a, tcg_gen_nor_i64); +} + /* * Shift (9 instructions) * ---------------------- From 709324dc05a17755d43c315e43a8ec9da04fc37b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 13 Feb 2021 14:40:18 +0100 Subject: [PATCH 254/272] target/mips/tx79: Introduce PSUB* opcodes (Parallel Subtract) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce the 'Parallel Subtract' opcodes: - PSUBB (Parallel Subtract Byte) - PSUBH (Parallel Subtract Halfword) - PSUBW (Parallel Subtract Word) Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <820210309145653.743937-11-f4bug@amsat.org> --- target/mips/tcg/tx79.decode | 6 ++++++ target/mips/tcg/tx79_translate.c | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/target/mips/tcg/tx79.decode b/target/mips/tcg/tx79.decode index 26c80b9bce..d1c07c7d90 100644 --- a/target/mips/tcg/tx79.decode +++ b/target/mips/tcg/tx79.decode @@ -29,6 +29,12 @@ MTHI1 011100 ..... 0000000000 00000 010001 @rs MFLO1 011100 0000000000 ..... 00000 010010 @rd MTLO1 011100 ..... 0000000000 00000 010011 @rs +# MMI0 + +PSUBW 011100 ..... ..... ..... 00001 001000 @rs_rt_rd +PSUBH 011100 ..... ..... ..... 00101 001000 @rs_rt_rd +PSUBB 011100 ..... ..... ..... 01001 001000 @rs_rt_rd + # MMI2 PCPYLD 011100 ..... ..... ..... 01110 001001 @rs_rt_rd diff --git a/target/mips/tcg/tx79_translate.c b/target/mips/tcg/tx79_translate.c index 00364f10d4..3abd1d92e7 100644 --- a/target/mips/tcg/tx79_translate.c +++ b/target/mips/tcg/tx79_translate.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "tcg/tcg-op.h" +#include "tcg/tcg-op-gvec.h" #include "exec/helper-gen.h" #include "translate.h" @@ -144,6 +145,24 @@ static bool trans_parallel_arith(DisasContext *ctx, arg_rtype *a, return true; } +/* Parallel Subtract Byte */ +static bool trans_PSUBB(DisasContext *ctx, arg_rtype *a) +{ + return trans_parallel_arith(ctx, a, tcg_gen_vec_sub8_i64); +} + +/* Parallel Subtract Halfword */ +static bool trans_PSUBH(DisasContext *ctx, arg_rtype *a) +{ + return trans_parallel_arith(ctx, a, tcg_gen_vec_sub16_i64); +} + +/* Parallel Subtract Word */ +static bool trans_PSUBW(DisasContext *ctx, arg_rtype *a) +{ + return trans_parallel_arith(ctx, a, tcg_gen_vec_sub32_i64); +} + /* * Min/Max (4 instructions) * ------------------------ From 0bc6937296c39659f6d8f031a62748e815708b06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 13 Feb 2021 20:42:14 +0100 Subject: [PATCH 255/272] target/mips/tx79: Introduce PEXTUW (Parallel Extend Upper from Word) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce the PEXTUW opcode (Parallel Extend Upper from Word). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20210309145653.743937-12-f4bug@amsat.org> --- target/mips/tcg/tx79.decode | 4 ++++ target/mips/tcg/tx79_translate.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/target/mips/tcg/tx79.decode b/target/mips/tcg/tx79.decode index d1c07c7d90..ead5f8281e 100644 --- a/target/mips/tcg/tx79.decode +++ b/target/mips/tcg/tx79.decode @@ -35,6 +35,10 @@ PSUBW 011100 ..... ..... ..... 00001 001000 @rs_rt_rd PSUBH 011100 ..... ..... ..... 00101 001000 @rs_rt_rd PSUBB 011100 ..... ..... ..... 01001 001000 @rs_rt_rd +# MMI1 + +PEXTUW 011100 ..... ..... ..... 10010 101000 @rs_rt_rd + # MMI2 PCPYLD 011100 ..... ..... ..... 01110 001001 @rs_rt_rd diff --git a/target/mips/tcg/tx79_translate.c b/target/mips/tcg/tx79_translate.c index 3abd1d92e7..68c56affc4 100644 --- a/target/mips/tcg/tx79_translate.c +++ b/target/mips/tcg/tx79_translate.c @@ -290,6 +290,36 @@ static bool trans_PNOR(DisasContext *ctx, arg_rtype *a) * PEXTLW rd, rs, rt Parallel Extend Lower from Word */ +static void gen_pextw(TCGv_i64 dl, TCGv_i64 dh, TCGv_i64 a, TCGv_i64 b) +{ + tcg_gen_deposit_i64(dl, b, a, 32, 32); + tcg_gen_shri_i64(b, b, 32); + tcg_gen_deposit_i64(dh, a, b, 0, 32); +} + +/* Parallel Extend Upper from Word */ +static bool trans_PEXTUW(DisasContext *ctx, arg_rtype *a) +{ + TCGv_i64 ax, bx; + + if (a->rd == 0) { + /* nop */ + return true; + } + + ax = tcg_temp_new_i64(); + bx = tcg_temp_new_i64(); + + gen_load_gpr_hi(ax, a->rs); + gen_load_gpr_hi(bx, a->rt); + gen_pextw(cpu_gpr[a->rd], cpu_gpr_hi[a->rd], ax, bx); + + tcg_temp_free(bx); + tcg_temp_free(ax); + + return true; +} + /* * Others (16 instructions) * ------------------------ From a9ea77f2dc5ee516adb7757e266e0d1790ddbf1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 13 Feb 2021 14:42:30 +0100 Subject: [PATCH 256/272] target/mips/tx79: Introduce PEXTL[BHW] opcodes (Parallel Extend Lower) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce the 'Parallel Extend Lower' opcodes: - PEXTLB (Parallel Extend Upper from Byte) - PEXTLH (Parallel Extend Upper from Halfword) - PEXTLW (Parallel Extend Upper from Word) Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20210309145653.743937-13-f4bug@amsat.org> --- target/mips/tcg/tx79.decode | 3 ++ target/mips/tcg/tx79_translate.c | 75 ++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/target/mips/tcg/tx79.decode b/target/mips/tcg/tx79.decode index ead5f8281e..98f21d33e3 100644 --- a/target/mips/tcg/tx79.decode +++ b/target/mips/tcg/tx79.decode @@ -34,6 +34,9 @@ MTLO1 011100 ..... 0000000000 00000 010011 @rs PSUBW 011100 ..... ..... ..... 00001 001000 @rs_rt_rd PSUBH 011100 ..... ..... ..... 00101 001000 @rs_rt_rd PSUBB 011100 ..... ..... ..... 01001 001000 @rs_rt_rd +PEXTLW 011100 ..... ..... ..... 10010 001000 @rs_rt_rd +PEXTLH 011100 ..... ..... ..... 10110 001000 @rs_rt_rd +PEXTLB 011100 ..... ..... ..... 11010 001000 @rs_rt_rd # MMI1 diff --git a/target/mips/tcg/tx79_translate.c b/target/mips/tcg/tx79_translate.c index 68c56affc4..c4656a4c21 100644 --- a/target/mips/tcg/tx79_translate.c +++ b/target/mips/tcg/tx79_translate.c @@ -297,6 +297,81 @@ static void gen_pextw(TCGv_i64 dl, TCGv_i64 dh, TCGv_i64 a, TCGv_i64 b) tcg_gen_deposit_i64(dh, a, b, 0, 32); } +static bool trans_PEXTLx(DisasContext *ctx, arg_rtype *a, unsigned wlen) +{ + TCGv_i64 ax, bx; + + if (a->rd == 0) { + /* nop */ + return true; + } + + ax = tcg_temp_new_i64(); + bx = tcg_temp_new_i64(); + + gen_load_gpr(ax, a->rs); + gen_load_gpr(bx, a->rt); + + /* Lower half */ + for (int i = 0; i < 64 / (2 * wlen); i++) { + tcg_gen_deposit_i64(cpu_gpr[a->rd], + cpu_gpr[a->rd], bx, 2 * wlen * i, wlen); + tcg_gen_deposit_i64(cpu_gpr[a->rd], + cpu_gpr[a->rd], ax, 2 * wlen * i + wlen, wlen); + tcg_gen_shri_i64(bx, bx, wlen); + tcg_gen_shri_i64(ax, ax, wlen); + } + /* Upper half */ + for (int i = 0; i < 64 / (2 * wlen); i++) { + tcg_gen_deposit_i64(cpu_gpr_hi[a->rd], + cpu_gpr_hi[a->rd], bx, 2 * wlen * i, wlen); + tcg_gen_deposit_i64(cpu_gpr_hi[a->rd], + cpu_gpr_hi[a->rd], ax, 2 * wlen * i + wlen, wlen); + tcg_gen_shri_i64(bx, bx, wlen); + tcg_gen_shri_i64(ax, ax, wlen); + } + + tcg_temp_free(bx); + tcg_temp_free(ax); + + return true; +} + +/* Parallel Extend Lower from Byte */ +static bool trans_PEXTLB(DisasContext *ctx, arg_rtype *a) +{ + return trans_PEXTLx(ctx, a, 8); +} + +/* Parallel Extend Lower from Halfword */ +static bool trans_PEXTLH(DisasContext *ctx, arg_rtype *a) +{ + return trans_PEXTLx(ctx, a, 16); +} + +/* Parallel Extend Lower from Word */ +static bool trans_PEXTLW(DisasContext *ctx, arg_rtype *a) +{ + TCGv_i64 ax, bx; + + if (a->rd == 0) { + /* nop */ + return true; + } + + ax = tcg_temp_new_i64(); + bx = tcg_temp_new_i64(); + + gen_load_gpr(ax, a->rs); + gen_load_gpr(bx, a->rt); + gen_pextw(cpu_gpr[a->rd], cpu_gpr_hi[a->rd], ax, bx); + + tcg_temp_free(bx); + tcg_temp_free(ax); + + return true; +} + /* Parallel Extend Upper from Word */ static bool trans_PEXTUW(DisasContext *ctx, arg_rtype *a) { From 82fbf9fc808b94dd8c5a1aafb19818620c5c4801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 13 Feb 2021 14:43:52 +0100 Subject: [PATCH 257/272] target/mips/tx79: Introduce PCEQ* opcodes (Parallel Compare for Equal) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce the 'Parallel Compare for Equal' opcodes: - PCEQB (Parallel Compare for Equal Byte) - PCEQH (Parallel Compare for Equal Halfword) - PCEQW (Parallel Compare for Equal Word) Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20210309145653.743937-14-f4bug@amsat.org> --- target/mips/tcg/tx79.decode | 3 ++ target/mips/tcg/tx79_translate.c | 66 ++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/target/mips/tcg/tx79.decode b/target/mips/tcg/tx79.decode index 98f21d33e3..cfe721755c 100644 --- a/target/mips/tcg/tx79.decode +++ b/target/mips/tcg/tx79.decode @@ -40,6 +40,9 @@ PEXTLB 011100 ..... ..... ..... 11010 001000 @rs_rt_rd # MMI1 +PCEQW 011100 ..... ..... ..... 00010 101000 @rs_rt_rd +PCEQH 011100 ..... ..... ..... 00110 101000 @rs_rt_rd +PCEQB 011100 ..... ..... ..... 01010 101000 @rs_rt_rd PEXTUW 011100 ..... ..... ..... 10010 101000 @rs_rt_rd # MMI2 diff --git a/target/mips/tcg/tx79_translate.c b/target/mips/tcg/tx79_translate.c index c4656a4c21..8dd510c271 100644 --- a/target/mips/tcg/tx79_translate.c +++ b/target/mips/tcg/tx79_translate.c @@ -237,6 +237,72 @@ static bool trans_PNOR(DisasContext *ctx, arg_rtype *a) * PCEQW rd, rs, rt Parallel Compare for Equal Word */ +static bool trans_parallel_compare(DisasContext *ctx, arg_rtype *a, + TCGCond cond, unsigned wlen) +{ + TCGv_i64 c0, c1, ax, bx, t0, t1, t2; + + if (a->rd == 0) { + /* nop */ + return true; + } + + c0 = tcg_const_tl(0); + c1 = tcg_const_tl(0xffffffff); + ax = tcg_temp_new_i64(); + bx = tcg_temp_new_i64(); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + + /* Lower half */ + gen_load_gpr(ax, a->rs); + gen_load_gpr(bx, a->rt); + for (int i = 0; i < (64 / wlen); i++) { + tcg_gen_sextract_i64(t0, ax, wlen * i, wlen); + tcg_gen_sextract_i64(t1, bx, wlen * i, wlen); + tcg_gen_movcond_i64(cond, t2, t1, t0, c1, c0); + tcg_gen_deposit_i64(cpu_gpr[a->rd], cpu_gpr[a->rd], t2, wlen * i, wlen); + } + /* Upper half */ + gen_load_gpr_hi(ax, a->rs); + gen_load_gpr_hi(bx, a->rt); + for (int i = 0; i < (64 / wlen); i++) { + tcg_gen_sextract_i64(t0, ax, wlen * i, wlen); + tcg_gen_sextract_i64(t1, bx, wlen * i, wlen); + tcg_gen_movcond_i64(cond, t2, t1, t0, c1, c0); + tcg_gen_deposit_i64(cpu_gpr_hi[a->rd], cpu_gpr_hi[a->rd], t2, wlen * i, wlen); + } + + tcg_temp_free(t2); + tcg_temp_free(t1); + tcg_temp_free(t0); + tcg_temp_free(bx); + tcg_temp_free(ax); + tcg_temp_free(c1); + tcg_temp_free(c0); + + return true; +} + +/* Parallel Compare for Equal Byte */ +static bool trans_PCEQB(DisasContext *ctx, arg_rtype *a) +{ + return trans_parallel_compare(ctx, a, TCG_COND_EQ, 8); +} + +/* Parallel Compare for Equal Halfword */ +static bool trans_PCEQH(DisasContext *ctx, arg_rtype *a) +{ + return trans_parallel_compare(ctx, a, TCG_COND_EQ, 16); +} + +/* Parallel Compare for Equal Word */ +static bool trans_PCEQW(DisasContext *ctx, arg_rtype *a) +{ + return trans_parallel_compare(ctx, a, TCG_COND_EQ, 32); +} + /* * LZC (1 instruction) * ------------------- From 8bd42c00f28447a84a4be5fffd39a2f9a92b5ac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 13 Feb 2021 14:22:32 +0100 Subject: [PATCH 258/272] target/mips/tx79: Introduce PCGT* (Parallel Compare for Greater Than) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce the 'Parallel Compare for Greater Than' opcodes: - PCGTB (Parallel Compare for Greater Than Byte) - PCGTH (Parallel Compare for Greater Than Halfword) - PCGTW (Parallel Compare for Greater Than Word) Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20210309145653.743937-15-f4bug@amsat.org> --- target/mips/tcg/tx79.decode | 3 +++ target/mips/tcg/tx79_translate.c | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/target/mips/tcg/tx79.decode b/target/mips/tcg/tx79.decode index cfe721755c..63fbe9694b 100644 --- a/target/mips/tcg/tx79.decode +++ b/target/mips/tcg/tx79.decode @@ -32,8 +32,11 @@ MTLO1 011100 ..... 0000000000 00000 010011 @rs # MMI0 PSUBW 011100 ..... ..... ..... 00001 001000 @rs_rt_rd +PCGTW 011100 ..... ..... ..... 00010 001000 @rs_rt_rd PSUBH 011100 ..... ..... ..... 00101 001000 @rs_rt_rd +PCGTH 011100 ..... ..... ..... 00110 001000 @rs_rt_rd PSUBB 011100 ..... ..... ..... 01001 001000 @rs_rt_rd +PCGTB 011100 ..... ..... ..... 01010 001000 @rs_rt_rd PEXTLW 011100 ..... ..... ..... 10010 001000 @rs_rt_rd PEXTLH 011100 ..... ..... ..... 10110 001000 @rs_rt_rd PEXTLB 011100 ..... ..... ..... 11010 001000 @rs_rt_rd diff --git a/target/mips/tcg/tx79_translate.c b/target/mips/tcg/tx79_translate.c index 8dd510c271..f0e3d8c0b6 100644 --- a/target/mips/tcg/tx79_translate.c +++ b/target/mips/tcg/tx79_translate.c @@ -285,18 +285,36 @@ static bool trans_parallel_compare(DisasContext *ctx, arg_rtype *a, return true; } +/* Parallel Compare for Greater Than Byte */ +static bool trans_PCGTB(DisasContext *ctx, arg_rtype *a) +{ + return trans_parallel_compare(ctx, a, TCG_COND_GE, 8); +} + /* Parallel Compare for Equal Byte */ static bool trans_PCEQB(DisasContext *ctx, arg_rtype *a) { return trans_parallel_compare(ctx, a, TCG_COND_EQ, 8); } +/* Parallel Compare for Greater Than Halfword */ +static bool trans_PCGTH(DisasContext *ctx, arg_rtype *a) +{ + return trans_parallel_compare(ctx, a, TCG_COND_GE, 16); +} + /* Parallel Compare for Equal Halfword */ static bool trans_PCEQH(DisasContext *ctx, arg_rtype *a) { return trans_parallel_compare(ctx, a, TCG_COND_EQ, 16); } +/* Parallel Compare for Greater Than Word */ +static bool trans_PCGTW(DisasContext *ctx, arg_rtype *a) +{ + return trans_parallel_compare(ctx, a, TCG_COND_GE, 32); +} + /* Parallel Compare for Equal Word */ static bool trans_PCEQW(DisasContext *ctx, arg_rtype *a) { From 71c49f39b9965506fa32483f80980a4a0199d4f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 13 Feb 2021 14:45:09 +0100 Subject: [PATCH 259/272] target/mips/tx79: Introduce PPACW opcode (Parallel Pack to Word) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce the PPACW opcode (Parallel Pack to Word). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20210214175912.732946-22-f4bug@amsat.org> Signed-off-by: Philippe Mathieu-Daudé --- target/mips/tcg/tx79.decode | 1 + target/mips/tcg/tx79_translate.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/target/mips/tcg/tx79.decode b/target/mips/tcg/tx79.decode index 63fbe9694b..653910371d 100644 --- a/target/mips/tcg/tx79.decode +++ b/target/mips/tcg/tx79.decode @@ -38,6 +38,7 @@ PCGTH 011100 ..... ..... ..... 00110 001000 @rs_rt_rd PSUBB 011100 ..... ..... ..... 01001 001000 @rs_rt_rd PCGTB 011100 ..... ..... ..... 01010 001000 @rs_rt_rd PEXTLW 011100 ..... ..... ..... 10010 001000 @rs_rt_rd +PPACW 011100 ..... ..... ..... 10011 001000 @rs_rt_rd PEXTLH 011100 ..... ..... ..... 10110 001000 @rs_rt_rd PEXTLB 011100 ..... ..... ..... 11010 001000 @rs_rt_rd diff --git a/target/mips/tcg/tx79_translate.c b/target/mips/tcg/tx79_translate.c index f0e3d8c0b6..90c33d26a9 100644 --- a/target/mips/tcg/tx79_translate.c +++ b/target/mips/tcg/tx79_translate.c @@ -374,6 +374,36 @@ static bool trans_PCEQW(DisasContext *ctx, arg_rtype *a) * PEXTLW rd, rs, rt Parallel Extend Lower from Word */ +/* Parallel Pack to Word */ +static bool trans_PPACW(DisasContext *ctx, arg_rtype *a) +{ + TCGv_i64 a0, b0, t0; + + if (a->rd == 0) { + /* nop */ + return true; + } + + a0 = tcg_temp_new_i64(); + b0 = tcg_temp_new_i64(); + t0 = tcg_temp_new_i64(); + + gen_load_gpr(a0, a->rs); + gen_load_gpr(b0, a->rt); + + gen_load_gpr_hi(t0, a->rt); /* b1 */ + tcg_gen_deposit_i64(cpu_gpr[a->rd], b0, t0, 32, 32); + + gen_load_gpr_hi(t0, a->rs); /* a1 */ + tcg_gen_deposit_i64(cpu_gpr_hi[a->rd], a0, t0, 32, 32); + + tcg_temp_free(t0); + tcg_temp_free(b0); + tcg_temp_free(a0); + + return true; +} + static void gen_pextw(TCGv_i64 dl, TCGv_i64 dh, TCGv_i64 a, TCGv_i64 b) { tcg_gen_deposit_i64(dl, b, a, 32, 32); From dce4808f74869577db21ef90a28061f9dc65c5d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 13 Feb 2021 14:46:56 +0100 Subject: [PATCH 260/272] target/mips/tx79: Introduce PROT3W opcode (Parallel Rotate 3 Words) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce the PROT3W opcode (Parallel Rotate 3 Words). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20210214175912.732946-25-f4bug@amsat.org> Signed-off-by: Philippe Mathieu-Daudé --- target/mips/tcg/tx79.decode | 1 + target/mips/tcg/tx79_translate.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/target/mips/tcg/tx79.decode b/target/mips/tcg/tx79.decode index 653910371d..2f65dce243 100644 --- a/target/mips/tcg/tx79.decode +++ b/target/mips/tcg/tx79.decode @@ -54,6 +54,7 @@ PEXTUW 011100 ..... ..... ..... 10010 101000 @rs_rt_rd PCPYLD 011100 ..... ..... ..... 01110 001001 @rs_rt_rd PAND 011100 ..... ..... ..... 10010 001001 @rs_rt_rd PXOR 011100 ..... ..... ..... 10011 001001 @rs_rt_rd +PROT3W 011100 00000 ..... ..... 11111 001001 @rt_rd # MMI3 diff --git a/target/mips/tcg/tx79_translate.c b/target/mips/tcg/tx79_translate.c index 90c33d26a9..402790249f 100644 --- a/target/mips/tcg/tx79_translate.c +++ b/target/mips/tcg/tx79_translate.c @@ -593,3 +593,31 @@ static bool trans_PCPYUD(DisasContext *s, arg_rtype *a) return true; } + +/* Parallel Rotate 3 Words Left */ +static bool trans_PROT3W(DisasContext *ctx, arg_rtype *a) +{ + TCGv_i64 ax; + + if (a->rd == 0) { + /* nop */ + return true; + } + if (a->rt == 0) { + tcg_gen_movi_i64(cpu_gpr[a->rd], 0); + tcg_gen_movi_i64(cpu_gpr_hi[a->rd], 0); + return true; + } + + ax = tcg_temp_new_i64(); + + tcg_gen_mov_i64(ax, cpu_gpr_hi[a->rt]); + tcg_gen_deposit_i64(cpu_gpr_hi[a->rd], ax, cpu_gpr[a->rt], 0, 32); + + tcg_gen_deposit_i64(cpu_gpr[a->rd], cpu_gpr[a->rt], ax, 0, 32); + tcg_gen_rotri_i64(cpu_gpr[a->rd], cpu_gpr[a->rd], 32); + + tcg_temp_free(ax); + + return true; +} From aaaa82a9f9975c59b72debb22bc92b8e1ab4ab10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 13 Feb 2021 14:58:18 +0100 Subject: [PATCH 261/272] target/mips/tx79: Introduce LQ opcode (Load Quadword) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce the LQ opcode (Load Quadword) and remove unreachable code. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20210214175912.732946-26-f4bug@amsat.org> Signed-off-by: Philippe Mathieu-Daudé --- target/mips/tcg/translate.c | 16 ++------------- target/mips/tcg/tx79.decode | 8 ++++++++ target/mips/tcg/tx79_translate.c | 35 ++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index ae33c75f08..82a7f2bcc6 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -1180,7 +1180,6 @@ enum { enum { MMI_OPC_CLASS_MMI = 0x1C << 26, /* Same as OPC_SPECIAL2 */ - MMI_OPC_LQ = 0x1E << 26, /* Same as OPC_MSA */ MMI_OPC_SQ = 0x1F << 26, /* Same as OPC_SPECIAL3 */ }; @@ -15179,11 +15178,6 @@ static void decode_mmi(CPUMIPSState *env, DisasContext *ctx) } } -static void gen_mmi_lq(CPUMIPSState *env, DisasContext *ctx) -{ - gen_reserved_instruction(ctx); /* TODO: MMI_OPC_LQ */ -} - static void gen_mmi_sq(DisasContext *ctx, int base, int rt, int offset) { gen_reserved_instruction(ctx); /* TODO: MMI_OPC_SQ */ @@ -16082,14 +16076,8 @@ static bool decode_opc_legacy(CPUMIPSState *env, DisasContext *ctx) gen_compute_branch(ctx, op, 4, rs, rt, offset, 4); } break; - case OPC_MDMX: /* MMI_OPC_LQ */ - if (ctx->insn_flags & INSN_R5900) { -#if defined(TARGET_MIPS64) - gen_mmi_lq(env, ctx); -#endif - } else { - /* MDMX: Not implemented. */ - } + case OPC_MDMX: + /* MDMX: Not implemented. */ break; case OPC_PCREL: check_insn(ctx, ISA_MIPS_R6); diff --git a/target/mips/tcg/tx79.decode b/target/mips/tcg/tx79.decode index 2f65dce243..0af5c6d0ed 100644 --- a/target/mips/tcg/tx79.decode +++ b/target/mips/tcg/tx79.decode @@ -13,6 +13,8 @@ &rtype rs rt rd sa +&itype base rt offset + ########################################################################### # Named instruction formats. These are generally used to # reduce the amount of duplication between instruction patterns. @@ -22,6 +24,8 @@ @rs ...... rs:5 ..... .......... ...... &rtype rt=0 rd=0 sa=0 @rd ...... .......... rd:5 ..... ...... &rtype rs=0 rt=0 sa=0 +@ldst ...... base:5 rt:5 offset:16 &itype + ########################################################################### MFHI1 011100 0000000000 ..... 00000 010000 @rd @@ -62,3 +66,7 @@ PCPYUD 011100 ..... ..... ..... 01110 101001 @rs_rt_rd POR 011100 ..... ..... ..... 10010 101001 @rs_rt_rd PNOR 011100 ..... ..... ..... 10011 101001 @rs_rt_rd PCPYH 011100 00000 ..... ..... 11011 101001 @rt_rd + +# SPECIAL + +LQ 011110 ..... ..... ................ @ldst diff --git a/target/mips/tcg/tx79_translate.c b/target/mips/tcg/tx79_translate.c index 402790249f..d9193b4d86 100644 --- a/target/mips/tcg/tx79_translate.c +++ b/target/mips/tcg/tx79_translate.c @@ -334,6 +334,41 @@ static bool trans_PCEQW(DisasContext *ctx, arg_rtype *a) * SQ rt, offset(base) Store Quadword */ +static bool trans_LQ(DisasContext *ctx, arg_itype *a) +{ + TCGv_i64 t0; + TCGv addr; + + if (a->rt == 0) { + /* nop */ + return true; + } + + t0 = tcg_temp_new_i64(); + addr = tcg_temp_new(); + + gen_base_offset_addr(ctx, addr, a->base, a->offset); + /* + * Clear least-significant four bits of the effective + * address, effectively creating an aligned address. + */ + tcg_gen_andi_tl(addr, addr, ~0xf); + + /* Lower half */ + tcg_gen_qemu_ld_i64(t0, addr, ctx->mem_idx, MO_TEQ); + gen_store_gpr(t0, a->rt); + + /* Upper half */ + tcg_gen_addi_i64(addr, addr, 8); + tcg_gen_qemu_ld_i64(t0, addr, ctx->mem_idx, MO_TEQ); + gen_store_gpr_hi(t0, a->rt); + + tcg_temp_free(t0); + tcg_temp_free(addr); + + return true; +} + /* * Multiply and Divide (19 instructions) * ------------------------------------- From 80ad6303577612451d56c84d079a8c5b7f21412d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 13 Feb 2021 14:47:47 +0100 Subject: [PATCH 262/272] target/mips/tx79: Introduce SQ opcode (Store Quadword) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce the SQ opcode (Store Quadword). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20210214175912.732946-27-f4bug@amsat.org> Signed-off-by: Philippe Mathieu-Daudé --- target/mips/tcg/tx79.decode | 1 + target/mips/tcg/tx79_translate.c | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/target/mips/tcg/tx79.decode b/target/mips/tcg/tx79.decode index 0af5c6d0ed..03a25a5096 100644 --- a/target/mips/tcg/tx79.decode +++ b/target/mips/tcg/tx79.decode @@ -70,3 +70,4 @@ PCPYH 011100 00000 ..... ..... 11011 101001 @rt_rd # SPECIAL LQ 011110 ..... ..... ................ @ldst +SQ 011111 ..... ..... ................ @ldst diff --git a/target/mips/tcg/tx79_translate.c b/target/mips/tcg/tx79_translate.c index d9193b4d86..395d6afa1f 100644 --- a/target/mips/tcg/tx79_translate.c +++ b/target/mips/tcg/tx79_translate.c @@ -369,6 +369,33 @@ static bool trans_LQ(DisasContext *ctx, arg_itype *a) return true; } +static bool trans_SQ(DisasContext *ctx, arg_itype *a) +{ + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv addr = tcg_temp_new(); + + gen_base_offset_addr(ctx, addr, a->base, a->offset); + /* + * Clear least-significant four bits of the effective + * address, effectively creating an aligned address. + */ + tcg_gen_andi_tl(addr, addr, ~0xf); + + /* Lower half */ + gen_load_gpr(t0, a->rt); + tcg_gen_qemu_st_i64(t0, addr, ctx->mem_idx, MO_TEQ); + + /* Upper half */ + tcg_gen_addi_i64(addr, addr, 8); + gen_load_gpr_hi(t0, a->rt); + tcg_gen_qemu_st_i64(t0, addr, ctx->mem_idx, MO_TEQ); + + tcg_temp_free(addr); + tcg_temp_free(t0); + + return true; +} + /* * Multiply and Divide (19 instructions) * ------------------------------------- From d859a77dbdeca288bd6679e33bf2a83a635349a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 4 Jul 2021 18:00:42 +0200 Subject: [PATCH 263/272] target/mips: Rewrite UHI errno_mips() using switch statement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Linking on Haiku OS fails: /boot/system/develop/tools/bin/../lib/gcc/x86_64-unknown-haiku/8.3.0/../../../../x86_64-unknown-haiku/bin/ld: error: libqemu-mips-softmmu.fa.p/target_mips_tcg_sysemu_mips-semi.c.o(.rodata) is too large (0xffff405a bytes) /boot/system/develop/tools/bin/../lib/gcc/x86_64-unknown-haiku/8.3.0/../../../../x86_64-unknown-haiku/bin/ld: final link failed: memory exhausted collect2: error: ld returned 1 exit status This is because the host_to_mips_errno[] uses errno as index, for example: static const uint16_t host_to_mips_errno[] = { [ENAMETOOLONG] = 91, ... and Haiku defines [*] ENAMETOOLONG as: 12 /* Error baselines */ 13 #define B_GENERAL_ERROR_BASE INT_MIN .. 22 #define B_STORAGE_ERROR_BASE (B_GENERAL_ERROR_BASE + 0x6000) ... 106 #define B_NAME_TOO_LONG (B_STORAGE_ERROR_BASE + 4) ... 211 #define ENAMETOOLONG B_TO_POSIX_ERROR(B_NAME_TOO_LONG) so the array ends up beeing indeed too big. Since POSIX errno can't be use as indexes on Haiku, rewrite errno_mips() using a switch statement. [*] https://github.com/haiku/haiku/blob/r1beta3/headers/os/support/Errors.h#L130 Reported-by: Richard Zak Suggested-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20210706130723.1178961-1-f4bug@amsat.org> --- target/mips/tcg/sysemu/mips-semi.c | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/target/mips/tcg/sysemu/mips-semi.c b/target/mips/tcg/sysemu/mips-semi.c index 77108b0b1a..b4a383ae90 100644 --- a/target/mips/tcg/sysemu/mips-semi.c +++ b/target/mips/tcg/sysemu/mips-semi.c @@ -74,25 +74,19 @@ enum UHIOpenFlags { UHIOpen_EXCL = 0x800 }; -/* Errno values taken from asm-mips/errno.h */ -static const uint16_t host_to_mips_errno[] = { - [ENAMETOOLONG] = 78, +static int errno_mips(int host_errno) +{ + /* Errno values taken from asm-mips/errno.h */ + switch (host_errno) { + case 0: return 0; + case ENAMETOOLONG: return 78; #ifdef EOVERFLOW - [EOVERFLOW] = 79, + case EOVERFLOW: return 79; #endif #ifdef ELOOP - [ELOOP] = 90, + case ELOOP: return 90; #endif -}; - -static int errno_mips(int err) -{ - if (err < 0 || err >= ARRAY_SIZE(host_to_mips_errno)) { - return EINVAL; - } else if (host_to_mips_errno[err]) { - return host_to_mips_errno[err]; - } else { - return err; + default: return EINVAL; } } From 85e411d7ff7d62a084f318f3956d48a644632d6c Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 25 Jun 2021 07:54:00 +0100 Subject: [PATCH 264/272] dp8393x: fix CAM descriptor entry index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently when a LOAD CAM command is executed the entries are loaded into the CAM from memory in order which is incorrect. According to the datasheet the first entry in the CAM descriptor is the entry index which means that each descriptor may update any single entry in the CAM rather than the Nth entry. Decode the CAM entry index and use it store the descriptor in the appropriate slot in the CAM. This fixes the issue where the MacOS toolbox loads a single CAM descriptor into the final slot in order to perform a loopback test which must succeed before the Ethernet port is enabled. Signed-off-by: Mark Cave-Ayland Tested-by: Finn Thain Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20210625065401.30170-10-mark.cave-ayland@ilande.co.uk> Signed-off-by: Philippe Mathieu-Daudé --- hw/net/dp8393x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c index 252c0a2664..11810c9b60 100644 --- a/hw/net/dp8393x.c +++ b/hw/net/dp8393x.c @@ -270,7 +270,7 @@ static void dp8393x_update_irq(dp8393xState *s) static void dp8393x_do_load_cam(dp8393xState *s) { int width, size; - uint16_t index = 0; + uint16_t index; width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; size = sizeof(uint16_t) * 4 * width; @@ -279,6 +279,7 @@ static void dp8393x_do_load_cam(dp8393xState *s) /* Fill current entry */ address_space_read(&s->as, dp8393x_cdp(s), MEMTXATTRS_UNSPECIFIED, s->data, size); + index = dp8393x_get(s, width, 0) & 0xf; s->cam[index][0] = dp8393x_get(s, width, 1) & 0xff; s->cam[index][1] = dp8393x_get(s, width, 1) >> 8; s->cam[index][2] = dp8393x_get(s, width, 2) & 0xff; @@ -291,7 +292,6 @@ static void dp8393x_do_load_cam(dp8393xState *s) /* Move to next entry */ s->regs[SONIC_CDC]--; s->regs[SONIC_CDP] += size; - index++; } /* Read CAM enable */ From 197ade0d110deeef58a05c11adec100926813560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 3 Jul 2021 15:38:33 +0200 Subject: [PATCH 265/272] dp8393x: Replace address_space_rw(is_write=1) by address_space_write() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace address_space_rw(is_write=1) by address_space_write() and remove pointless cast. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Mark Cave-Ayland Tested-by: Finn Thain Message-Id: <20210710174954.2577195-2-f4bug@amsat.org> Tested-by: Mark Cave-Ayland --- hw/net/dp8393x.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c index 11810c9b60..9118364aa3 100644 --- a/hw/net/dp8393x.c +++ b/hw/net/dp8393x.c @@ -816,8 +816,8 @@ static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf, size = sizeof(uint16_t) * width; address = dp8393x_crda(s) + sizeof(uint16_t) * 6 * width; dp8393x_put(s, width, 0, 0); - address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED, - (uint8_t *)s->data, size, 1); + address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED, + s->data, size); /* Move to next descriptor */ s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA]; @@ -846,8 +846,8 @@ static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf, /* Pad short packets to keep pointers aligned */ if (rx_len < padded_len) { size = padded_len - rx_len; - address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED, - (uint8_t *)"\xFF\xFF\xFF", size, 1); + address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED, + "\xFF\xFF\xFF", size); address += size; } From 67b38ddfe58cbfb7c9c4a8d8b7efdc3fe7def41f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 8 Jul 2021 00:30:46 +0200 Subject: [PATCH 266/272] dp8393x: Replace 0x40 magic value by SONIC_REG_COUNT definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Mark Cave-Ayland Tested-by: Finn Thain Message-Id: <20210710174954.2577195-3-f4bug@amsat.org> Tested-by: Mark Cave-Ayland --- hw/net/dp8393x.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c index 9118364aa3..d1e147a82a 100644 --- a/hw/net/dp8393x.c +++ b/hw/net/dp8393x.c @@ -85,6 +85,7 @@ static const char *reg_names[] = { #define SONIC_MPT 0x2e #define SONIC_MDT 0x2f #define SONIC_DCR2 0x3f +#define SONIC_REG_COUNT 0x40 #define SONIC_CR_HTX 0x0001 #define SONIC_CR_TXP 0x0002 @@ -158,7 +159,7 @@ struct dp8393xState { /* Registers */ uint8_t cam[16][6]; - uint16_t regs[0x40]; + uint16_t regs[SONIC_REG_COUNT]; /* Temporaries */ uint8_t tx_buffer[0x10000]; @@ -972,7 +973,7 @@ static void dp8393x_realize(DeviceState *dev, Error **errp) address_space_init(&s->as, s->dma_mr, "dp8393x"); memory_region_init_io(&s->mmio, OBJECT(dev), &dp8393x_ops, s, - "dp8393x-regs", 0x40 << s->it_shift); + "dp8393x-regs", SONIC_REG_COUNT << s->it_shift); s->nic = qemu_new_nic(&net_dp83932_info, &s->conf, object_get_typename(OBJECT(dev)), dev->id, s); @@ -987,7 +988,7 @@ static const VMStateDescription vmstate_dp8393x = { .minimum_version_id = 0, .fields = (VMStateField []) { VMSTATE_BUFFER_UNSAFE(cam, dp8393xState, 0, 16 * 6), - VMSTATE_UINT16_ARRAY(regs, dp8393xState, 0x40), + VMSTATE_UINT16_ARRAY(regs, dp8393xState, SONIC_REG_COUNT), VMSTATE_END_OF_LIST() } }; From 8ac2ffb584590b0398ae4e1a08a0b5d209b6f250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 3 Jul 2021 15:42:51 +0200 Subject: [PATCH 267/272] dp8393x: Store CAM registers as 16-bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per the DP83932C datasheet from July 1995: 4.0 SONIC Registers 4.1 THE CAM UNIT The Content Addressable Memory (CAM) consists of sixteen 48-bit entries for complete address filtering of network packets. Each entry corresponds to a 48-bit destination address that is user programmable and can contain any combination of Multicast or Physical addresses. Each entry is partitioned into three 16-bit CAM cells accessible through CAM Address Ports (CAP 2, CAP 1 and CAP 0) with CAP0 corresponding to the least significant 16 bits of the Destination Address and CAP2 corresponding to the most significant bits. Store the CAM registers as 16-bit as it simplifies the code. Having now the CAM registers as arrays of 3 uint16_t, we can avoid using the VMSTATE_BUFFER_UNSAFE macro by using VMSTATE_UINT16_2DARRAY which is more appropriate. This breaks the migration stream however. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Mark Cave-Ayland Tested-by: Finn Thain Message-Id: <20210710174954.2577195-5-f4bug@amsat.org> Tested-by: Mark Cave-Ayland --- hw/net/dp8393x.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c index d1e147a82a..283de9db0b 100644 --- a/hw/net/dp8393x.c +++ b/hw/net/dp8393x.c @@ -158,7 +158,7 @@ struct dp8393xState { MemoryRegion mmio; /* Registers */ - uint8_t cam[16][6]; + uint16_t cam[16][3]; uint16_t regs[SONIC_REG_COUNT]; /* Temporaries */ @@ -281,15 +281,13 @@ static void dp8393x_do_load_cam(dp8393xState *s) address_space_read(&s->as, dp8393x_cdp(s), MEMTXATTRS_UNSPECIFIED, s->data, size); index = dp8393x_get(s, width, 0) & 0xf; - s->cam[index][0] = dp8393x_get(s, width, 1) & 0xff; - s->cam[index][1] = dp8393x_get(s, width, 1) >> 8; - s->cam[index][2] = dp8393x_get(s, width, 2) & 0xff; - s->cam[index][3] = dp8393x_get(s, width, 2) >> 8; - s->cam[index][4] = dp8393x_get(s, width, 3) & 0xff; - s->cam[index][5] = dp8393x_get(s, width, 3) >> 8; - trace_dp8393x_load_cam(index, s->cam[index][0], s->cam[index][1], - s->cam[index][2], s->cam[index][3], - s->cam[index][4], s->cam[index][5]); + s->cam[index][0] = dp8393x_get(s, width, 1); + s->cam[index][1] = dp8393x_get(s, width, 2); + s->cam[index][2] = dp8393x_get(s, width, 3); + trace_dp8393x_load_cam(index, + s->cam[index][0] >> 8, s->cam[index][0] & 0xff, + s->cam[index][1] >> 8, s->cam[index][1] & 0xff, + s->cam[index][2] >> 8, s->cam[index][2] & 0xff); /* Move to next entry */ s->regs[SONIC_CDC]--; s->regs[SONIC_CDP] += size; @@ -592,8 +590,7 @@ static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size) case SONIC_CAP1: case SONIC_CAP0: if (s->regs[SONIC_CR] & SONIC_CR_RST) { - val = s->cam[s->regs[SONIC_CEP] & 0xf][2 * (SONIC_CAP0 - reg) + 1] << 8; - val |= s->cam[s->regs[SONIC_CEP] & 0xf][2 * (SONIC_CAP0 - reg)]; + val = s->cam[s->regs[SONIC_CEP] & 0xf][SONIC_CAP0 - reg]; } break; /* All other registers have no special contraints */ @@ -984,10 +981,10 @@ static void dp8393x_realize(DeviceState *dev, Error **errp) static const VMStateDescription vmstate_dp8393x = { .name = "dp8393x", - .version_id = 0, - .minimum_version_id = 0, + .version_id = 1, + .minimum_version_id = 1, .fields = (VMStateField []) { - VMSTATE_BUFFER_UNSAFE(cam, dp8393xState, 0, 16 * 6), + VMSTATE_UINT16_2DARRAY(cam, dp8393xState, 16, 3), VMSTATE_UINT16_ARRAY(regs, dp8393xState, SONIC_REG_COUNT), VMSTATE_END_OF_LIST() } From 82adabf7e533a29fe5a122090c2bee523624330a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 3 Jul 2021 15:44:33 +0200 Subject: [PATCH 268/272] dp8393x: Rewrite dp8393x_get() / dp8393x_put() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of accessing N registers via a single address_space API call using a temporary buffer (stored in the device state) and updating each register, move the address_space call in the register put/get. The load/store and word size checks are moved to put/get too. This simplifies a bit, making the code easier to read. Co-developed-by: Philippe Mathieu-Daudé Co-developed-by: Mark Cave-Ayland Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Mark Cave-Ayland Tested-by: Mark Cave-Ayland Tested-by: Finn Thain Message-Id: <20210710174954.2577195-8-f4bug@amsat.org> --- hw/net/dp8393x.c | 160 +++++++++++++++++++---------------------------- 1 file changed, 63 insertions(+), 97 deletions(-) diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c index 283de9db0b..4057a263de 100644 --- a/hw/net/dp8393x.c +++ b/hw/net/dp8393x.c @@ -163,7 +163,6 @@ struct dp8393xState { /* Temporaries */ uint8_t tx_buffer[0x10000]; - uint16_t data[12]; int loopback_packet; /* Memory access */ @@ -220,34 +219,48 @@ static uint32_t dp8393x_wt(dp8393xState *s) return s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0]; } -static uint16_t dp8393x_get(dp8393xState *s, int width, int offset) +static uint16_t dp8393x_get(dp8393xState *s, hwaddr addr, int offset) { + const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; uint16_t val; - if (s->big_endian) { - val = be16_to_cpu(s->data[offset * width + width - 1]); + if (s->regs[SONIC_DCR] & SONIC_DCR_DW) { + addr += offset << 2; + if (s->big_endian) { + val = address_space_ldl_be(&s->as, addr, attrs, NULL); + } else { + val = address_space_ldl_le(&s->as, addr, attrs, NULL); + } } else { - val = le16_to_cpu(s->data[offset * width]); + addr += offset << 1; + if (s->big_endian) { + val = address_space_lduw_be(&s->as, addr, attrs, NULL); + } else { + val = address_space_lduw_le(&s->as, addr, attrs, NULL); + } } + return val; } -static void dp8393x_put(dp8393xState *s, int width, int offset, - uint16_t val) +static void dp8393x_put(dp8393xState *s, + hwaddr addr, int offset, uint16_t val) { - if (s->big_endian) { - if (width == 2) { - s->data[offset * 2] = 0; - s->data[offset * 2 + 1] = cpu_to_be16(val); + const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; + + if (s->regs[SONIC_DCR] & SONIC_DCR_DW) { + addr += offset << 2; + if (s->big_endian) { + address_space_stl_be(&s->as, addr, val, attrs, NULL); } else { - s->data[offset] = cpu_to_be16(val); + address_space_stl_le(&s->as, addr, val, attrs, NULL); } } else { - if (width == 2) { - s->data[offset * 2] = cpu_to_le16(val); - s->data[offset * 2 + 1] = 0; + addr += offset << 1; + if (s->big_endian) { + address_space_stw_be(&s->as, addr, val, attrs, NULL); } else { - s->data[offset] = cpu_to_le16(val); + address_space_stw_le(&s->as, addr, val, attrs, NULL); } } } @@ -278,12 +291,10 @@ static void dp8393x_do_load_cam(dp8393xState *s) while (s->regs[SONIC_CDC] & 0x1f) { /* Fill current entry */ - address_space_read(&s->as, dp8393x_cdp(s), - MEMTXATTRS_UNSPECIFIED, s->data, size); - index = dp8393x_get(s, width, 0) & 0xf; - s->cam[index][0] = dp8393x_get(s, width, 1); - s->cam[index][1] = dp8393x_get(s, width, 2); - s->cam[index][2] = dp8393x_get(s, width, 3); + index = dp8393x_get(s, dp8393x_cdp(s), 0) & 0xf; + s->cam[index][0] = dp8393x_get(s, dp8393x_cdp(s), 1); + s->cam[index][1] = dp8393x_get(s, dp8393x_cdp(s), 2); + s->cam[index][2] = dp8393x_get(s, dp8393x_cdp(s), 3); trace_dp8393x_load_cam(index, s->cam[index][0] >> 8, s->cam[index][0] & 0xff, s->cam[index][1] >> 8, s->cam[index][1] & 0xff, @@ -294,9 +305,7 @@ static void dp8393x_do_load_cam(dp8393xState *s) } /* Read CAM enable */ - address_space_read(&s->as, dp8393x_cdp(s), - MEMTXATTRS_UNSPECIFIED, s->data, size); - s->regs[SONIC_CE] = dp8393x_get(s, width, 0); + s->regs[SONIC_CE] = dp8393x_get(s, dp8393x_cdp(s), 0); trace_dp8393x_load_cam_done(s->regs[SONIC_CE]); /* Done */ @@ -312,14 +321,12 @@ static void dp8393x_do_read_rra(dp8393xState *s) /* Read memory */ width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; size = sizeof(uint16_t) * 4 * width; - address_space_read(&s->as, dp8393x_rrp(s), - MEMTXATTRS_UNSPECIFIED, s->data, size); /* Update SONIC registers */ - s->regs[SONIC_CRBA0] = dp8393x_get(s, width, 0); - s->regs[SONIC_CRBA1] = dp8393x_get(s, width, 1); - s->regs[SONIC_RBWC0] = dp8393x_get(s, width, 2); - s->regs[SONIC_RBWC1] = dp8393x_get(s, width, 3); + s->regs[SONIC_CRBA0] = dp8393x_get(s, dp8393x_rrp(s), 0); + s->regs[SONIC_CRBA1] = dp8393x_get(s, dp8393x_rrp(s), 1); + s->regs[SONIC_RBWC0] = dp8393x_get(s, dp8393x_rrp(s), 2); + s->regs[SONIC_RBWC1] = dp8393x_get(s, dp8393x_rrp(s), 3); trace_dp8393x_read_rra_regs(s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1], s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]); @@ -415,28 +422,22 @@ static void dp8393x_do_receiver_disable(dp8393xState *s) static void dp8393x_do_transmit_packets(dp8393xState *s) { NetClientState *nc = qemu_get_queue(s->nic); - int width, size; int tx_len, len; uint16_t i; - width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; - while (1) { /* Read memory */ - size = sizeof(uint16_t) * 6 * width; s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA]; trace_dp8393x_transmit_packet(dp8393x_ttda(s)); - address_space_read(&s->as, dp8393x_ttda(s) + sizeof(uint16_t) * width, - MEMTXATTRS_UNSPECIFIED, s->data, size); tx_len = 0; /* Update registers */ - s->regs[SONIC_TCR] = dp8393x_get(s, width, 0) & 0xf000; - s->regs[SONIC_TPS] = dp8393x_get(s, width, 1); - s->regs[SONIC_TFC] = dp8393x_get(s, width, 2); - s->regs[SONIC_TSA0] = dp8393x_get(s, width, 3); - s->regs[SONIC_TSA1] = dp8393x_get(s, width, 4); - s->regs[SONIC_TFS] = dp8393x_get(s, width, 5); + s->regs[SONIC_TCR] = dp8393x_get(s, dp8393x_ttda(s), 1) & 0xf000; + s->regs[SONIC_TPS] = dp8393x_get(s, dp8393x_ttda(s), 2); + s->regs[SONIC_TFC] = dp8393x_get(s, dp8393x_ttda(s), 3); + s->regs[SONIC_TSA0] = dp8393x_get(s, dp8393x_ttda(s), 4); + s->regs[SONIC_TSA1] = dp8393x_get(s, dp8393x_ttda(s), 5); + s->regs[SONIC_TFS] = dp8393x_get(s, dp8393x_ttda(s), 6); /* Handle programmable interrupt */ if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) { @@ -458,15 +459,12 @@ static void dp8393x_do_transmit_packets(dp8393xState *s) i++; if (i != s->regs[SONIC_TFC]) { /* Read next fragment details */ - size = sizeof(uint16_t) * 3 * width; - address_space_read(&s->as, - dp8393x_ttda(s) - + sizeof(uint16_t) * width * (4 + 3 * i), - MEMTXATTRS_UNSPECIFIED, s->data, - size); - s->regs[SONIC_TSA0] = dp8393x_get(s, width, 0); - s->regs[SONIC_TSA1] = dp8393x_get(s, width, 1); - s->regs[SONIC_TFS] = dp8393x_get(s, width, 2); + s->regs[SONIC_TSA0] = dp8393x_get(s, dp8393x_ttda(s), + 4 + 3 * i); + s->regs[SONIC_TSA1] = dp8393x_get(s, dp8393x_ttda(s), + 5 + 3 * i); + s->regs[SONIC_TFS] = dp8393x_get(s, dp8393x_ttda(s), + 6 + 3 * i); } } @@ -499,22 +497,12 @@ static void dp8393x_do_transmit_packets(dp8393xState *s) s->regs[SONIC_TCR] |= SONIC_TCR_PTX; /* Write status */ - dp8393x_put(s, width, 0, - s->regs[SONIC_TCR] & 0x0fff); /* status */ - size = sizeof(uint16_t) * width; - address_space_write(&s->as, dp8393x_ttda(s), - MEMTXATTRS_UNSPECIFIED, s->data, size); + dp8393x_put(s, dp8393x_ttda(s), 0, s->regs[SONIC_TCR] & 0x0fff); if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) { /* Read footer of packet */ - size = sizeof(uint16_t) * width; - address_space_read(&s->as, - dp8393x_ttda(s) - + sizeof(uint16_t) * width - * (4 + 3 * s->regs[SONIC_TFC]), - MEMTXATTRS_UNSPECIFIED, s->data, - size); - s->regs[SONIC_CTDA] = dp8393x_get(s, width, 0); + s->regs[SONIC_CTDA] = dp8393x_get(s, dp8393x_ttda(s), + 4 + 3 * s->regs[SONIC_TFC]); if (s->regs[SONIC_CTDA] & SONIC_DESC_EOL) { /* EOL detected */ break; @@ -762,7 +750,7 @@ static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf, dp8393xState *s = qemu_get_nic_opaque(nc); int packet_type; uint32_t available, address; - int width, rx_len, padded_len; + int rx_len, padded_len; uint32_t checksum; int size; @@ -775,10 +763,8 @@ static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf, rx_len = pkt_size + sizeof(checksum); if (s->regs[SONIC_DCR] & SONIC_DCR_DW) { - width = 2; padded_len = ((rx_len - 1) | 3) + 1; } else { - width = 1; padded_len = ((rx_len - 1) | 1) + 1; } @@ -799,11 +785,7 @@ static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf, /* Check for EOL */ if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) { /* Are we still in resource exhaustion? */ - size = sizeof(uint16_t) * 1 * width; - address = dp8393x_crda(s) + sizeof(uint16_t) * 5 * width; - address_space_read(&s->as, address, MEMTXATTRS_UNSPECIFIED, - s->data, size); - s->regs[SONIC_LLFA] = dp8393x_get(s, width, 0); + s->regs[SONIC_LLFA] = dp8393x_get(s, dp8393x_crda(s), 5); if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) { /* Still EOL ; stop reception */ return -1; @@ -811,11 +793,7 @@ static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf, /* Link has been updated by host */ /* Clear in_use */ - size = sizeof(uint16_t) * width; - address = dp8393x_crda(s) + sizeof(uint16_t) * 6 * width; - dp8393x_put(s, width, 0, 0); - address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED, - s->data, size); + dp8393x_put(s, dp8393x_crda(s), 6, 0x0000); /* Move to next descriptor */ s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA]; @@ -869,32 +847,20 @@ static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf, /* Write status to memory */ trace_dp8393x_receive_write_status(dp8393x_crda(s)); - dp8393x_put(s, width, 0, s->regs[SONIC_RCR]); /* status */ - dp8393x_put(s, width, 1, rx_len); /* byte count */ - dp8393x_put(s, width, 2, s->regs[SONIC_TRBA0]); /* pkt_ptr0 */ - dp8393x_put(s, width, 3, s->regs[SONIC_TRBA1]); /* pkt_ptr1 */ - dp8393x_put(s, width, 4, s->regs[SONIC_RSC]); /* seq_no */ - size = sizeof(uint16_t) * 5 * width; - address_space_write(&s->as, dp8393x_crda(s), - MEMTXATTRS_UNSPECIFIED, - s->data, size); + dp8393x_put(s, dp8393x_crda(s), 0, s->regs[SONIC_RCR]); /* status */ + dp8393x_put(s, dp8393x_crda(s), 1, rx_len); /* byte count */ + dp8393x_put(s, dp8393x_crda(s), 2, s->regs[SONIC_TRBA0]); /* pkt_ptr0 */ + dp8393x_put(s, dp8393x_crda(s), 3, s->regs[SONIC_TRBA1]); /* pkt_ptr1 */ + dp8393x_put(s, dp8393x_crda(s), 4, s->regs[SONIC_RSC]); /* seq_no */ /* Check link field */ - size = sizeof(uint16_t) * width; - address_space_read(&s->as, - dp8393x_crda(s) + sizeof(uint16_t) * 5 * width, - MEMTXATTRS_UNSPECIFIED, s->data, size); - s->regs[SONIC_LLFA] = dp8393x_get(s, width, 0); + s->regs[SONIC_LLFA] = dp8393x_get(s, dp8393x_crda(s), 5); if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) { /* EOL detected */ s->regs[SONIC_ISR] |= SONIC_ISR_RDE; } else { /* Clear in_use */ - size = sizeof(uint16_t) * width; - address = dp8393x_crda(s) + sizeof(uint16_t) * 6 * width; - dp8393x_put(s, width, 0, 0); - address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED, - s->data, size); + dp8393x_put(s, dp8393x_crda(s), 6, 0x0000); /* Move to next descriptor */ s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA]; From 39d9919f4b4c3e7f230efd7d845439d6d732dc89 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Mon, 5 Jul 2021 22:49:26 +0100 Subject: [PATCH 269/272] dp8393x: don't force 32-bit register access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 3fe9a838ec "dp8393x: Always use 32-bit accesses" set .impl.min_access_size and .impl.max_access_size to 4 to try and fix the Linux jazzsonic driver which uses 32-bit accesses. The problem with forcing the register access to 32-bit in this way is that since the dp8393x uses 16-bit registers, a manual endian swap is required for devices on big endian machines with 32-bit accesses. For both access sizes and machine endians the QEMU memory API can do the right thing automatically: all that is needed is to set .impl.min_access_size to 2 to declare that the dp8393x implements 16-bit registers. Normally .impl.max_access_size should also be set to 2, however that doesn't quite work in this case since the register stride is specified using a (dynamic) it_shift property which is applied during the MMIO access itself. The effect of this is that for a 32-bit access the memory API performs 2 x 16-bit accesses, but the use of it_shift within the MMIO access itself causes the register value to be repeated in both the top 16-bits and bottom 16-bits. The Linux jazzsonic driver expects the stride to be zero-extended up to access size and therefore fails to correctly detect the dp8393x device due to the extra data in the top 16-bits. The solution here is to remove .impl.max_access_size so that the memory API will correctly zero-extend the 16-bit registers to the access size up to and including it_shift. Since it_shift is never greater than 2 than this will always do the right thing for both 16-bit and 32-bit accesses regardless of the machine endian, allowing the manual endian swap code to be removed. Signed-off-by: Mark Cave-Ayland Fixes: 3fe9a838ec ("dp8393x: Always use 32-bit accesses") Message-Id: <20210705214929.17222-2-mark.cave-ayland@ilande.co.uk> Signed-off-by: Philippe Mathieu-Daudé Tested-by: Finn Thain Tested-by: Mark Cave-Ayland --- hw/net/dp8393x.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c index 4057a263de..45b954e46c 100644 --- a/hw/net/dp8393x.c +++ b/hw/net/dp8393x.c @@ -588,15 +588,14 @@ static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size) trace_dp8393x_read(reg, reg_names[reg], val, size); - return s->big_endian ? val << 16 : val; + return val; } -static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data, +static void dp8393x_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size) { dp8393xState *s = opaque; int reg = addr >> s->it_shift; - uint32_t val = s->big_endian ? data >> 16 : data; trace_dp8393x_write(reg, reg_names[reg], val, size); @@ -677,11 +676,16 @@ static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data, } } +/* + * Since .impl.max_access_size is effectively controlled by the it_shift + * property, leave it unspecified for now to allow the memory API to + * correctly zero extend the 16-bit register values to the access size up to and + * including it_shift. + */ static const MemoryRegionOps dp8393x_ops = { .read = dp8393x_read, .write = dp8393x_write, - .impl.min_access_size = 4, - .impl.max_access_size = 4, + .impl.min_access_size = 2, .endianness = DEVICE_NATIVE_ENDIAN, }; From c60b292106132f72c1a5afbbd9c55dbc341d1620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 7 Jun 2021 17:24:58 +0200 Subject: [PATCH 270/272] hw/sd/sdcard: When card is in wrong state, log which state it is MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We report the card is in an inconsistent state, but don't precise in which state it is. Add this information, as it is useful when debugging problems. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Bin Meng Message-Id: <20210624142209.1193073-2-f4bug@amsat.org> Reviewed-by: Alexander Bulekov --- hw/sd/sd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 282d39a704..d8fdf84f4d 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1504,7 +1504,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) return sd_illegal; } - qemu_log_mask(LOG_GUEST_ERROR, "SD: CMD%i in a wrong state\n", req.cmd); + qemu_log_mask(LOG_GUEST_ERROR, "SD: CMD%i in a wrong state: %s\n", + req.cmd, sd_state_name(sd->state)); return sd_illegal; } From 66c152d7b45ae8bd2a021226bb7689424d872687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 7 Jun 2021 15:32:06 +0200 Subject: [PATCH 271/272] hw/sd/sdcard: Extract address_in_range() helper, log invalid accesses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Multiple commands have to check the address requested is valid. Extract this code pattern as a new address_in_range() helper, and log invalid accesses as guest errors. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Bin Meng Message-Id: <20210624142209.1193073-3-f4bug@amsat.org> --- hw/sd/sd.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index d8fdf84f4d..52f9769f60 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -937,6 +937,19 @@ static void sd_lock_command(SDState *sd) sd->card_status &= ~CARD_IS_LOCKED; } +static bool address_in_range(SDState *sd, const char *desc, + uint64_t addr, uint32_t length) +{ + if (addr + length > sd->size) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s offset %"PRIu64" > card %"PRIu64" [%%%u]\n", + desc, addr, sd->size, length); + sd->card_status |= ADDRESS_ERROR; + return false; + } + return true; +} + static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { uint32_t rca = 0x0000; @@ -1218,8 +1231,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) switch (sd->state) { case sd_transfer_state: - if (addr + sd->blk_len > sd->size) { - sd->card_status |= ADDRESS_ERROR; + if (!address_in_range(sd, "READ_BLOCK", addr, sd->blk_len)) { return sd_r1; } @@ -1264,8 +1276,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) switch (sd->state) { case sd_transfer_state: - if (addr + sd->blk_len > sd->size) { - sd->card_status |= ADDRESS_ERROR; + if (!address_in_range(sd, "WRITE_BLOCK", addr, sd->blk_len)) { return sd_r1; } @@ -1325,8 +1336,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) switch (sd->state) { case sd_transfer_state: - if (addr >= sd->size) { - sd->card_status |= ADDRESS_ERROR; + if (!address_in_range(sd, "SET_WRITE_PROT", addr, 1)) { return sd_r1b; } @@ -1348,8 +1358,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) switch (sd->state) { case sd_transfer_state: - if (addr >= sd->size) { - sd->card_status |= ADDRESS_ERROR; + if (!address_in_range(sd, "CLR_WRITE_PROT", addr, 1)) { return sd_r1b; } @@ -1826,8 +1835,8 @@ void sd_write_byte(SDState *sd, uint8_t value) case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */ if (sd->data_offset == 0) { /* Start of the block - let's check the address is valid */ - if (sd->data_start + sd->blk_len > sd->size) { - sd->card_status |= ADDRESS_ERROR; + if (!address_in_range(sd, "WRITE_MULTIPLE_BLOCK", + sd->data_start, sd->blk_len)) { break; } if (sd->size <= SDSC_MAX_CAPACITY) { @@ -1999,8 +2008,8 @@ uint8_t sd_read_byte(SDState *sd) case 18: /* CMD18: READ_MULTIPLE_BLOCK */ if (sd->data_offset == 0) { - if (sd->data_start + io_len > sd->size) { - sd->card_status |= ADDRESS_ERROR; + if (!address_in_range(sd, "READ_MULTIPLE_BLOCK", + sd->data_start, io_len)) { return 0x00; } BLK_READ_BLOCK(sd->data_start, io_len); From 59b63d78be1f67c87b79331dcc825e485efd3bcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 29 Jun 2021 10:09:28 +0200 Subject: [PATCH 272/272] hw/sd/sdcard: Check for valid address range in SEND_WRITE_PROT (CMD30) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OSS-Fuzz found sending illegal addresses when querying the write protection bits triggers an assertion: qemu-fuzz-i386: hw/sd/sd.c:824: uint32_t sd_wpbits(SDState *, uint64_t): Assertion `wpnum < sd->wpgrps_size' failed. ==11578== ERROR: libFuzzer: deadly signal #8 0x7ffff628e091 in __assert_fail #9 0x5555588f1a3c in sd_wpbits hw/sd/sd.c:824:9 #10 0x5555588dd271 in sd_normal_command hw/sd/sd.c:1383:38 #11 0x5555588d777c in sd_do_command hw/sd/sd.c #12 0x555558cb25a0 in sdbus_do_command hw/sd/core.c:100:16 #13 0x555558e02a9a in sdhci_send_command hw/sd/sdhci.c:337:12 #14 0x555558dffa46 in sdhci_write hw/sd/sdhci.c:1187:9 #15 0x5555598b9d76 in memory_region_write_accessor softmmu/memory.c:489:5 Similarly to commit 8573378e62d ("hw/sd: fix out-of-bounds check for multi block reads"), check the address range before sending the status of the write protection bits. Include the qtest reproducer provided by Alexander Bulekov: $ make check-qtest-i386 ... Running test qtest-i386/fuzz-sdcard-test qemu-system-i386: ../hw/sd/sd.c:824: sd_wpbits: Assertion `wpnum < sd->wpgrps_size' failed. Reported-by: OSS-Fuzz (Issue 29225) Resolves: https://gitlab.com/qemu-project/qemu/-/issues/450 Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Bin Meng Reviewed-by: Alexander Bulekov Message-Id: <20210702155900.148665-4-f4bug@amsat.org> --- MAINTAINERS | 3 +- hw/sd/sd.c | 5 +++ tests/qtest/fuzz-sdcard-test.c | 66 ++++++++++++++++++++++++++++++++++ tests/qtest/meson.build | 1 + 4 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 tests/qtest/fuzz-sdcard-test.c diff --git a/MAINTAINERS b/MAINTAINERS index 3026b979b7..b4799a4329 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1823,7 +1823,8 @@ F: include/hw/sd/sd* F: hw/sd/core.c F: hw/sd/sd* F: hw/sd/ssi-sd.c -F: tests/qtest/sd* +F: tests/qtest/fuzz-sdcard-test.c +F: tests/qtest/sdhci-test.c USB M: Gerd Hoffmann diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 52f9769f60..1f964e022b 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1380,6 +1380,11 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) switch (sd->state) { case sd_transfer_state: + if (!address_in_range(sd, "SEND_WRITE_PROT", + req.arg, sd->blk_len)) { + return sd_r1; + } + sd->state = sd_sendingdata_state; *(uint32_t *) sd->data = sd_wpbits(sd, req.arg); sd->data_start = addr; diff --git a/tests/qtest/fuzz-sdcard-test.c b/tests/qtest/fuzz-sdcard-test.c new file mode 100644 index 0000000000..96602eac7e --- /dev/null +++ b/tests/qtest/fuzz-sdcard-test.c @@ -0,0 +1,66 @@ +/* + * QTest fuzzer-generated testcase for sdcard device + * + * Copyright (c) 2021 Philippe Mathieu-Daudé + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqos/libqtest.h" + +/* + * https://gitlab.com/qemu-project/qemu/-/issues/450 + * Used to trigger: + * Assertion `wpnum < sd->wpgrps_size' failed. + */ +static void oss_fuzz_29225(void) +{ + QTestState *s; + + s = qtest_init(" -display none -m 512m -nodefaults -nographic" + " -device sdhci-pci,sd-spec-version=3" + " -device sd-card,drive=d0" + " -drive if=none,index=0,file=null-co://,format=raw,id=d0"); + + qtest_outl(s, 0xcf8, 0x80001010); + qtest_outl(s, 0xcfc, 0xd0690); + qtest_outl(s, 0xcf8, 0x80001003); + qtest_outl(s, 0xcf8, 0x80001013); + qtest_outl(s, 0xcfc, 0xffffffff); + qtest_outl(s, 0xcf8, 0x80001003); + qtest_outl(s, 0xcfc, 0x3effe00); + + qtest_bufwrite(s, 0xff0d062c, "\xff", 0x1); + qtest_bufwrite(s, 0xff0d060f, "\xb7", 0x1); + qtest_bufwrite(s, 0xff0d060a, "\xc9", 0x1); + qtest_bufwrite(s, 0xff0d060f, "\x29", 0x1); + qtest_bufwrite(s, 0xff0d060f, "\xc2", 0x1); + qtest_bufwrite(s, 0xff0d0628, "\xf7", 0x1); + qtest_bufwrite(s, 0x0, "\xe3", 0x1); + qtest_bufwrite(s, 0x7, "\x13", 0x1); + qtest_bufwrite(s, 0x8, "\xe3", 0x1); + qtest_bufwrite(s, 0xf, "\xe3", 0x1); + qtest_bufwrite(s, 0xff0d060f, "\x03", 0x1); + qtest_bufwrite(s, 0xff0d0605, "\x01", 0x1); + qtest_bufwrite(s, 0xff0d060b, "\xff", 0x1); + qtest_bufwrite(s, 0xff0d060c, "\xff", 0x1); + qtest_bufwrite(s, 0xff0d060e, "\xff", 0x1); + qtest_bufwrite(s, 0xff0d060f, "\x06", 0x1); + qtest_bufwrite(s, 0xff0d060f, "\x9e", 0x1); + + qtest_quit(s); +} + +int main(int argc, char **argv) +{ + const char *arch = qtest_get_arch(); + + g_test_init(&argc, &argv, NULL); + + if (strcmp(arch, "i386") == 0) { + qtest_add_func("fuzz/sdcard/oss_fuzz_29225", oss_fuzz_29225); + } + + return g_test_run(); +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index ee7347b727..e22a0792c5 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -21,6 +21,7 @@ qtests_generic = \ (config_all_devices.has_key('CONFIG_MEGASAS_SCSI_PCI') ? ['fuzz-megasas-test'] : []) + \ (config_all_devices.has_key('CONFIG_VIRTIO_SCSI') ? ['fuzz-virtio-scsi-test'] : []) + \ (config_all_devices.has_key('CONFIG_SB16') ? ['fuzz-sb16-test'] : []) + \ + (config_all_devices.has_key('CONFIG_SDHCI_PCI') ? ['fuzz-sdcard-test'] : []) + \ [ 'cdrom-test', 'device-introspect-test',