summaryrefslogtreecommitdiffstats
path: root/qemu/hw
diff options
context:
space:
mode:
authorJosé Pekkarinen <jose.pekkarinen@nokia.com>2016-05-18 13:18:31 +0300
committerJosé Pekkarinen <jose.pekkarinen@nokia.com>2016-05-18 13:42:15 +0300
commit437fd90c0250dee670290f9b714253671a990160 (patch)
treeb871786c360704244a07411c69fb58da9ead4a06 /qemu/hw
parent5bbd6fe9b8bab2a93e548c5a53b032d1939eec05 (diff)
These changes are the raw update to qemu-2.6.
Collission happened in the following patches: migration: do cleanup operation after completion(738df5b9) Bug fix.(1750c932f86) kvmclock: add a new function to update env->tsc.(b52baab2) The code provided by the patches was already in the upstreamed version. Change-Id: I3cc11841a6a76ae20887b2e245710199e1ea7f9a Signed-off-by: José Pekkarinen <jose.pekkarinen@nokia.com>
Diffstat (limited to 'qemu/hw')
-rw-r--r--qemu/hw/9pfs/9p-handle.c (renamed from qemu/hw/9pfs/virtio-9p-handle.c)15
-rw-r--r--qemu/hw/9pfs/9p-local.c (renamed from qemu/hw/9pfs/virtio-9p-local.c)24
-rw-r--r--qemu/hw/9pfs/9p-posix-acl.c (renamed from qemu/hw/9pfs/virtio-9p-posix-acl.c)9
-rw-r--r--qemu/hw/9pfs/9p-proxy.c (renamed from qemu/hw/9pfs/virtio-9p-proxy.c)21
-rw-r--r--qemu/hw/9pfs/9p-proxy.h (renamed from qemu/hw/9pfs/virtio-9p-proxy.h)10
-rw-r--r--qemu/hw/9pfs/9p-synth.c (renamed from qemu/hw/9pfs/virtio-9p-synth.c)9
-rw-r--r--qemu/hw/9pfs/9p-synth.h (renamed from qemu/hw/9pfs/virtio-9p-synth.h)9
-rw-r--r--qemu/hw/9pfs/9p-xattr-user.c (renamed from qemu/hw/9pfs/virtio-9p-xattr-user.c)9
-rw-r--r--qemu/hw/9pfs/9p-xattr.c (renamed from qemu/hw/9pfs/virtio-9p-xattr.c)8
-rw-r--r--qemu/hw/9pfs/9p-xattr.h (renamed from qemu/hw/9pfs/virtio-9p-xattr.h)6
-rw-r--r--qemu/hw/9pfs/9p.c (renamed from qemu/hw/9pfs/virtio-9p.c)264
-rw-r--r--qemu/hw/9pfs/9p.h324
-rw-r--r--qemu/hw/9pfs/Makefile.objs14
-rw-r--r--qemu/hw/9pfs/codir.c5
-rw-r--r--qemu/hw/9pfs/cofile.c5
-rw-r--r--qemu/hw/9pfs/cofs.c5
-rw-r--r--qemu/hw/9pfs/coth.c42
-rw-r--r--qemu/hw/9pfs/coth.h (renamed from qemu/hw/9pfs/virtio-9p-coth.h)18
-rw-r--r--qemu/hw/9pfs/coxattr.c5
-rw-r--r--qemu/hw/9pfs/virtio-9p-coth.c82
-rw-r--r--qemu/hw/9pfs/virtio-9p-device.c212
-rw-r--r--qemu/hw/9pfs/virtio-9p.h391
-rw-r--r--qemu/hw/Makefile.objs2
-rw-r--r--qemu/hw/acpi/Makefile.objs5
-rw-r--r--qemu/hw/acpi/acpi_interface.c1
-rw-r--r--qemu/hw/acpi/aml-build.c470
-rw-r--r--qemu/hw/acpi/bios-linker-loader.c97
-rw-r--r--qemu/hw/acpi/core.c45
-rw-r--r--qemu/hw/acpi/cpu_hotplug.c3
-rw-r--r--qemu/hw/acpi/cpu_hotplug_acpi_table.c136
-rw-r--r--qemu/hw/acpi/ich9.c59
-rw-r--r--qemu/hw/acpi/memory_hotplug.c16
-rw-r--r--qemu/hw/acpi/memory_hotplug_acpi_table.c262
-rw-r--r--qemu/hw/acpi/nvdimm.c706
-rw-r--r--qemu/hw/acpi/pcihp.c2
-rw-r--r--qemu/hw/acpi/piix4.c2
-rw-r--r--qemu/hw/acpi/tco.c1
-rw-r--r--qemu/hw/alpha/dp264.c36
-rw-r--r--qemu/hw/alpha/pci.c7
-rw-r--r--qemu/hw/alpha/typhoon.c5
-rw-r--r--qemu/hw/arm/Makefile.objs6
-rw-r--r--qemu/hw/arm/allwinner-a10.c21
-rw-r--r--qemu/hw/arm/armv7m.c17
-rw-r--r--qemu/hw/arm/ast2400.c140
-rw-r--r--qemu/hw/arm/bcm2835_peripherals.c312
-rw-r--r--qemu/hw/arm/bcm2836.c184
-rw-r--r--qemu/hw/arm/boot.c235
-rw-r--r--qemu/hw/arm/collie.c14
-rw-r--r--qemu/hw/arm/cubieboard.c27
-rw-r--r--qemu/hw/arm/digic.c8
-rw-r--r--qemu/hw/arm/digic_boards.c20
-rw-r--r--qemu/hw/arm/exynos4210.c25
-rw-r--r--qemu/hw/arm/exynos4_boards.c59
-rw-r--r--qemu/hw/arm/fsl-imx25.c313
-rw-r--r--qemu/hw/arm/fsl-imx31.c287
-rw-r--r--qemu/hw/arm/gumstix.c39
-rw-r--r--qemu/hw/arm/highbank.c121
-rw-r--r--qemu/hw/arm/imx25_pdk.c153
-rw-r--r--qemu/hw/arm/integratorcp.c32
-rw-r--r--qemu/hw/arm/kzm.c218
-rw-r--r--qemu/hw/arm/mainstone.c17
-rw-r--r--qemu/hw/arm/musicpal.c19
-rw-r--r--qemu/hw/arm/netduino2.c22
-rw-r--r--qemu/hw/arm/nseries.c52
-rw-r--r--qemu/hw/arm/omap1.c55
-rw-r--r--qemu/hw/arm/omap2.c21
-rw-r--r--qemu/hw/arm/omap_sx1.c44
-rw-r--r--qemu/hw/arm/palm.c17
-rw-r--r--qemu/hw/arm/palmetto-bmc.c68
-rw-r--r--qemu/hw/arm/pxa2xx.c28
-rw-r--r--qemu/hw/arm/pxa2xx_gpio.c1
-rw-r--r--qemu/hw/arm/pxa2xx_pic.c3
-rw-r--r--qemu/hw/arm/raspi.c172
-rw-r--r--qemu/hw/arm/realview.c114
-rw-r--r--qemu/hw/arm/spitz.c87
-rw-r--r--qemu/hw/arm/stellaris.c91
-rw-r--r--qemu/hw/arm/stm32f205_soc.c23
-rw-r--r--qemu/hw/arm/strongarm.c9
-rw-r--r--qemu/hw/arm/sysbus-fdt.c323
-rw-r--r--qemu/hw/arm/tosa.c19
-rw-r--r--qemu/hw/arm/versatilepb.c59
-rw-r--r--qemu/hw/arm/vexpress.c36
-rw-r--r--qemu/hw/arm/virt-acpi-build.c216
-rw-r--r--qemu/hw/arm/virt.c680
-rw-r--r--qemu/hw/arm/xilinx_zynq.c120
-rw-r--r--qemu/hw/arm/xlnx-ep108.c93
-rw-r--r--qemu/hw/arm/xlnx-zynqmp.c173
-rw-r--r--qemu/hw/arm/z2.c16
-rw-r--r--qemu/hw/audio/ac97.c1
-rw-r--r--qemu/hw/audio/adlib.c13
-rw-r--r--qemu/hw/audio/cs4231.c1
-rw-r--r--qemu/hw/audio/cs4231a.c24
-rw-r--r--qemu/hw/audio/es1370.c28
-rw-r--r--qemu/hw/audio/fmopl.c7
-rw-r--r--qemu/hw/audio/gus.c31
-rw-r--r--qemu/hw/audio/gusemu.h1
-rw-r--r--qemu/hw/audio/gusemu_hal.c1
-rw-r--r--qemu/hw/audio/gusemu_mixer.c1
-rw-r--r--qemu/hw/audio/hda-codec.c1
-rw-r--r--qemu/hw/audio/intel-hda.c1
-rw-r--r--qemu/hw/audio/lm4549.c2
-rw-r--r--qemu/hw/audio/marvell_88w8618.c1
-rw-r--r--qemu/hw/audio/milkymist-ac97.c1
-rw-r--r--qemu/hw/audio/pcspk.c1
-rw-r--r--qemu/hw/audio/pl041.c1
-rw-r--r--qemu/hw/audio/sb16.c43
-rw-r--r--qemu/hw/audio/wm8750.c1
-rw-r--r--qemu/hw/block/block.c2
-rw-r--r--qemu/hw/block/cdrom.c1
-rw-r--r--qemu/hw/block/dataplane/virtio-blk.c237
-rw-r--r--qemu/hw/block/dataplane/virtio-blk.h1
-rw-r--r--qemu/hw/block/ecc.c1
-rw-r--r--qemu/hw/block/fdc.c467
-rw-r--r--qemu/hw/block/hd-geometry.c1
-rw-r--r--qemu/hw/block/m25p80.c334
-rw-r--r--qemu/hw/block/nand.c14
-rw-r--r--qemu/hw/block/nvme.c55
-rw-r--r--qemu/hw/block/nvme.h1
-rw-r--r--qemu/hw/block/onenand.c4
-rw-r--r--qemu/hw/block/pflash_cfi01.c20
-rw-r--r--qemu/hw/block/pflash_cfi02.c10
-rw-r--r--qemu/hw/block/tc58128.c1
-rw-r--r--qemu/hw/block/virtio-blk.c158
-rw-r--r--qemu/hw/block/xen_blkif.h12
-rw-r--r--qemu/hw/block/xen_disk.c101
-rw-r--r--qemu/hw/bt/core.c1
-rw-r--r--qemu/hw/bt/hci-csr.c5
-rw-r--r--qemu/hw/bt/hci.c23
-rw-r--r--qemu/hw/bt/hid.c1
-rw-r--r--qemu/hw/bt/l2cap.c1
-rw-r--r--qemu/hw/bt/sdp.c32
-rw-r--r--qemu/hw/char/Makefile.objs1
-rw-r--r--qemu/hw/char/bcm2835_aux.c316
-rw-r--r--qemu/hw/char/cadence_uart.c8
-rw-r--r--qemu/hw/char/debugcon.c2
-rw-r--r--qemu/hw/char/digic-uart.c1
-rw-r--r--qemu/hw/char/escc.c18
-rw-r--r--qemu/hw/char/etraxfs_ser.c7
-rw-r--r--qemu/hw/char/exynos4210_uart.c16
-rw-r--r--qemu/hw/char/grlib_apbuart.c1
-rw-r--r--qemu/hw/char/imx_serial.c235
-rw-r--r--qemu/hw/char/ipoctal232.c1
-rw-r--r--qemu/hw/char/lm32_juart.c1
-rw-r--r--qemu/hw/char/lm32_uart.c1
-rw-r--r--qemu/hw/char/mcf_uart.c3
-rw-r--r--qemu/hw/char/milkymist-uart.c1
-rw-r--r--qemu/hw/char/omap_uart.c4
-rw-r--r--qemu/hw/char/parallel.c2
-rw-r--r--qemu/hw/char/pl011.c1
-rw-r--r--qemu/hw/char/sclpconsole-lm.c1
-rw-r--r--qemu/hw/char/sclpconsole.c1
-rw-r--r--qemu/hw/char/serial-isa.c2
-rw-r--r--qemu/hw/char/serial-pci.c2
-rw-r--r--qemu/hw/char/serial.c26
-rw-r--r--qemu/hw/char/sh_serial.c1
-rw-r--r--qemu/hw/char/spapr_vty.c4
-rw-r--r--qemu/hw/char/stm32f2xx_usart.c1
-rw-r--r--qemu/hw/char/virtio-console.c1
-rw-r--r--qemu/hw/char/virtio-serial-bus.c87
-rw-r--r--qemu/hw/char/xen_console.c26
-rw-r--r--qemu/hw/char/xilinx_uartlite.c1
-rw-r--r--qemu/hw/core/empty_slot.c1
-rw-r--r--qemu/hw/core/fw-path-provider.c1
-rw-r--r--qemu/hw/core/hotplug.c1
-rw-r--r--qemu/hw/core/irq.c1
-rw-r--r--qemu/hw/core/loader.c131
-rw-r--r--qemu/hw/core/machine.c131
-rw-r--r--qemu/hw/core/nmi.c12
-rw-r--r--qemu/hw/core/null-machine.c17
-rw-r--r--qemu/hw/core/platform-bus.c26
-rw-r--r--qemu/hw/core/ptimer.c4
-rw-r--r--qemu/hw/core/qdev-properties-system.c58
-rw-r--r--qemu/hw/core/qdev-properties.c214
-rw-r--r--qemu/hw/core/qdev.c53
-rw-r--r--qemu/hw/core/stream.c1
-rw-r--r--qemu/hw/core/sysbus.c1
-rw-r--r--qemu/hw/cpu/Makefile.objs1
-rw-r--r--qemu/hw/cpu/a15mpcore.c46
-rw-r--r--qemu/hw/cpu/a9mpcore.c13
-rw-r--r--qemu/hw/cpu/arm11mpcore.c2
-rw-r--r--qemu/hw/cpu/icc_bus.c118
-rw-r--r--qemu/hw/cpu/realview_mpcore.c2
-rw-r--r--qemu/hw/cris/axis_dev88.c23
-rw-r--r--qemu/hw/cris/boot.c6
-rw-r--r--qemu/hw/display/Makefile.objs7
-rw-r--r--qemu/hw/display/ads7846.c3
-rw-r--r--qemu/hw/display/bcm2835_fb.c425
-rw-r--r--qemu/hw/display/blizzard.c1
-rw-r--r--qemu/hw/display/cg3.c10
-rw-r--r--qemu/hw/display/cirrus_vga.c6
-rw-r--r--qemu/hw/display/exynos4210_fimd.c5
-rw-r--r--qemu/hw/display/framebuffer.c1
-rw-r--r--qemu/hw/display/g364fb.c1
-rw-r--r--qemu/hw/display/jazz_led.c7
-rw-r--r--qemu/hw/display/milkymist-tmu2.c5
-rw-r--r--qemu/hw/display/milkymist-vgafb.c1
-rw-r--r--qemu/hw/display/omap_dss.c4
-rw-r--r--qemu/hw/display/omap_lcd_template.h4
-rw-r--r--qemu/hw/display/omap_lcdc.c4
-rw-r--r--qemu/hw/display/pl110.c1
-rw-r--r--qemu/hw/display/pxa2xx_lcd.c9
-rw-r--r--qemu/hw/display/qxl-logger.c1
-rw-r--r--qemu/hw/display/qxl-render.c3
-rw-r--r--qemu/hw/display/qxl.c12
-rw-r--r--qemu/hw/display/sm501.c7
-rw-r--r--qemu/hw/display/ssd0303.c1
-rw-r--r--qemu/hw/display/ssd0323.c3
-rw-r--r--qemu/hw/display/tc6393xb.c4
-rw-r--r--qemu/hw/display/tcx.c33
-rw-r--r--qemu/hw/display/vga-isa-mm.c1
-rw-r--r--qemu/hw/display/vga-isa.c1
-rw-r--r--qemu/hw/display/vga-pci.c1
-rw-r--r--qemu/hw/display/vga.c135
-rw-r--r--qemu/hw/display/vga_int.h1
-rw-r--r--qemu/hw/display/virtio-gpu-3d.c606
-rw-r--r--qemu/hw/display/virtio-gpu-pci.c5
-rw-r--r--qemu/hw/display/virtio-gpu.c226
-rw-r--r--qemu/hw/display/virtio-vga.c11
-rw-r--r--qemu/hw/display/vmware_vga.c15
-rw-r--r--qemu/hw/display/xenfb.c58
-rw-r--r--qemu/hw/dma/Makefile.objs1
-rw-r--r--qemu/hw/dma/bcm2835_dma.c409
-rw-r--r--qemu/hw/dma/etraxfs_dma.c16
-rw-r--r--qemu/hw/dma/i82374.c62
-rw-r--r--qemu/hw/dma/i8257.c415
-rw-r--r--qemu/hw/dma/omap_dma.c7
-rw-r--r--qemu/hw/dma/pl080.c1
-rw-r--r--qemu/hw/dma/pl330.c2
-rw-r--r--qemu/hw/dma/puv3_dma.c1
-rw-r--r--qemu/hw/dma/pxa2xx_dma.c4
-rw-r--r--qemu/hw/dma/rc4030.c3
-rw-r--r--qemu/hw/dma/soc_dma.c40
-rw-r--r--qemu/hw/dma/sparc32_dma.c1
-rw-r--r--qemu/hw/dma/sun4m_iommu.c1
-rw-r--r--qemu/hw/dma/xilinx_axidma.c14
-rw-r--r--qemu/hw/gpio/Makefile.objs2
-rw-r--r--qemu/hw/gpio/gpio_key.c104
-rw-r--r--qemu/hw/gpio/imx_gpio.c350
-rw-r--r--qemu/hw/gpio/max7310.c1
-rw-r--r--qemu/hw/gpio/mpc8xxx.c1
-rw-r--r--qemu/hw/gpio/omap_gpio.c34
-rw-r--r--qemu/hw/gpio/pl061.c68
-rw-r--r--qemu/hw/gpio/puv3_gpio.c1
-rw-r--r--qemu/hw/gpio/zaurus.c6
-rw-r--r--qemu/hw/i2c/Makefile.objs1
-rw-r--r--qemu/hw/i2c/bitbang_i2c.c1
-rw-r--r--qemu/hw/i2c/core.c1
-rw-r--r--qemu/hw/i2c/exynos4210_i2c.c1
-rw-r--r--qemu/hw/i2c/imx_i2c.c337
-rw-r--r--qemu/hw/i2c/omap_i2c.c9
-rw-r--r--qemu/hw/i2c/pm_smbus.c1
-rw-r--r--qemu/hw/i2c/smbus.c1
-rw-r--r--qemu/hw/i2c/smbus_eeprom.c1
-rw-r--r--qemu/hw/i2c/smbus_ich9.c1
-rw-r--r--qemu/hw/i2c/versatile_i2c.c1
-rw-r--r--qemu/hw/i386/Makefile.objs27
-rw-r--r--qemu/hw/i386/acpi-build.c1841
-rw-r--r--qemu/hw/i386/acpi-build.h4
-rw-r--r--qemu/hw/i386/acpi-dsdt-cpu-hotplug.dsl90
-rw-r--r--qemu/hw/i386/acpi-dsdt-dbug.dsl41
-rw-r--r--qemu/hw/i386/acpi-dsdt-hpet.dsl48
-rw-r--r--qemu/hw/i386/acpi-dsdt-isa.dsl117
-rw-r--r--qemu/hw/i386/acpi-dsdt-mem-hotplug.dsl171
-rw-r--r--qemu/hw/i386/acpi-dsdt.dsl304
-rw-r--r--qemu/hw/i386/acpi-dsdt.hex.generated2972
-rw-r--r--qemu/hw/i386/intel_iommu.c166
-rw-r--r--qemu/hw/i386/intel_iommu_internal.h6
-rw-r--r--qemu/hw/i386/kvm/apic.c3
-rw-r--r--qemu/hw/i386/kvm/clock.c1
-rw-r--r--qemu/hw/i386/kvm/i8254.c2
-rw-r--r--qemu/hw/i386/kvm/i8259.c1
-rw-r--r--qemu/hw/i386/kvm/ioapic.c11
-rw-r--r--qemu/hw/i386/kvm/pci-assign.c116
-rw-r--r--qemu/hw/i386/kvmvapic.c16
-rw-r--r--qemu/hw/i386/multiboot.c6
-rw-r--r--qemu/hw/i386/pc.c580
-rw-r--r--qemu/hw/i386/pc_piix.c449
-rw-r--r--qemu/hw/i386/pc_q35.c304
-rw-r--r--qemu/hw/i386/pc_sysfw.c6
-rw-r--r--qemu/hw/i386/pci-assign-load-rom.c85
-rw-r--r--qemu/hw/i386/q35-acpi-dsdt.dsl435
-rw-r--r--qemu/hw/i386/q35-acpi-dsdt.hex.generated7610
-rw-r--r--qemu/hw/i386/xen/xen_apic.c7
-rw-r--r--qemu/hw/i386/xen/xen_platform.c17
-rw-r--r--qemu/hw/i386/xen/xen_pvdevice.c14
-rw-r--r--qemu/hw/ide/ahci.c381
-rw-r--r--qemu/hw/ide/ahci.h35
-rw-r--r--qemu/hw/ide/atapi.c222
-rw-r--r--qemu/hw/ide/cmd646.c2
-rw-r--r--qemu/hw/ide/core.c364
-rw-r--r--qemu/hw/ide/ich.c11
-rw-r--r--qemu/hw/ide/internal.h54
-rw-r--r--qemu/hw/ide/isa.c1
-rw-r--r--qemu/hw/ide/macio.c43
-rw-r--r--qemu/hw/ide/microdrive.c1
-rw-r--r--qemu/hw/ide/mmio.c1
-rw-r--r--qemu/hw/ide/pci.c29
-rw-r--r--qemu/hw/ide/piix.c16
-rw-r--r--qemu/hw/ide/qdev.c19
-rw-r--r--qemu/hw/ide/via.c1
-rw-r--r--qemu/hw/input/Makefile.objs2
-rw-r--r--qemu/hw/input/adb.c21
-rw-r--r--qemu/hw/input/hid.c45
-rw-r--r--qemu/hw/input/lm832x.c1
-rw-r--r--qemu/hw/input/milkymist-softusb.c6
-rw-r--r--qemu/hw/input/pckbd.c1
-rw-r--r--qemu/hw/input/pl050.c1
-rw-r--r--qemu/hw/input/ps2.c34
-rw-r--r--qemu/hw/input/pxa2xx_keypad.c1
-rw-r--r--qemu/hw/input/stellaris_input.c7
-rw-r--r--qemu/hw/input/tsc2005.c4
-rw-r--r--qemu/hw/input/tsc210x.c12
-rw-r--r--qemu/hw/input/virtio-input-hid.c49
-rw-r--r--qemu/hw/input/virtio-input-host.c72
-rw-r--r--qemu/hw/input/virtio-input.c76
-rw-r--r--qemu/hw/input/vmmouse.c1
-rw-r--r--qemu/hw/intc/Makefile.objs4
-rw-r--r--qemu/hw/intc/allwinner-a10-pic.c1
-rw-r--r--qemu/hw/intc/apic.c25
-rw-r--r--qemu/hw/intc/apic_common.c13
-rw-r--r--qemu/hw/intc/arm_gic.c393
-rw-r--r--qemu/hw/intc/arm_gic_common.c102
-rw-r--r--qemu/hw/intc/arm_gic_kvm.c148
-rw-r--r--qemu/hw/intc/arm_gicv2m.c4
-rw-r--r--qemu/hw/intc/arm_gicv3_common.c142
-rw-r--r--qemu/hw/intc/arm_gicv3_kvm.c151
-rw-r--r--qemu/hw/intc/armv7m_nvic.c25
-rw-r--r--qemu/hw/intc/aspeed_vic.c339
-rw-r--r--qemu/hw/intc/bcm2835_ic.c237
-rw-r--r--qemu/hw/intc/bcm2836_control.c304
-rw-r--r--qemu/hw/intc/etraxfs_pic.c1
-rw-r--r--qemu/hw/intc/exynos4210_combiner.c1
-rw-r--r--qemu/hw/intc/exynos4210_gic.c1
-rw-r--r--qemu/hw/intc/grlib_irqmp.c1
-rw-r--r--qemu/hw/intc/heathrow_pic.c1
-rw-r--r--qemu/hw/intc/i8259.c3
-rw-r--r--qemu/hw/intc/i8259_common.c1
-rw-r--r--qemu/hw/intc/imx_avic.c89
-rw-r--r--qemu/hw/intc/ioapic.c89
-rw-r--r--qemu/hw/intc/ioapic_common.c57
-rw-r--r--qemu/hw/intc/lm32_pic.c2
-rw-r--r--qemu/hw/intc/omap_intc.c11
-rw-r--r--qemu/hw/intc/openpic.c5
-rw-r--r--qemu/hw/intc/openpic_kvm.c5
-rw-r--r--qemu/hw/intc/pl190.c1
-rw-r--r--qemu/hw/intc/puv3_intc.c1
-rw-r--r--qemu/hw/intc/realview_gic.c2
-rw-r--r--qemu/hw/intc/s390_flic.c1
-rw-r--r--qemu/hw/intc/s390_flic_kvm.c3
-rw-r--r--qemu/hw/intc/sh_intc.c3
-rw-r--r--qemu/hw/intc/slavio_intctl.c1
-rw-r--r--qemu/hw/intc/vgic_common.h35
-rw-r--r--qemu/hw/intc/xics.c41
-rw-r--r--qemu/hw/intc/xics_kvm.c4
-rw-r--r--qemu/hw/intc/xilinx_intc.c1
-rw-r--r--qemu/hw/ipack/ipack.c2
-rw-r--r--qemu/hw/ipack/tpci200.c2
-rw-r--r--qemu/hw/ipmi/Makefile.objs5
-rw-r--r--qemu/hw/ipmi/ipmi.c151
-rw-r--r--qemu/hw/ipmi/ipmi_bmc_extern.c519
-rw-r--r--qemu/hw/ipmi/ipmi_bmc_sim.c1810
-rw-r--r--qemu/hw/ipmi/isa_ipmi_bt.c530
-rw-r--r--qemu/hw/ipmi/isa_ipmi_kcs.c495
-rw-r--r--qemu/hw/isa/apm.c1
-rw-r--r--qemu/hw/isa/i82378.c9
-rw-r--r--qemu/hw/isa/isa-bus.c38
-rw-r--r--qemu/hw/isa/lpc_ich9.c61
-rw-r--r--qemu/hw/isa/pc87312.c10
-rw-r--r--qemu/hw/isa/piix4.c7
-rw-r--r--qemu/hw/isa/vt82c686.c6
-rw-r--r--qemu/hw/lm32/lm32_boards.c49
-rw-r--r--qemu/hw/lm32/lm32_hwsetup.h1
-rw-r--r--qemu/hw/lm32/milkymist-hw.h3
-rw-r--r--qemu/hw/lm32/milkymist.c21
-rw-r--r--qemu/hw/m68k/an5206.c25
-rw-r--r--qemu/hw/m68k/dummy_m68k.c18
-rw-r--r--qemu/hw/m68k/mcf5206.c3
-rw-r--r--qemu/hw/m68k/mcf5208.c23
-rw-r--r--qemu/hw/m68k/mcf_intc.c3
-rw-r--r--qemu/hw/mem/Makefile.objs1
-rw-r--r--qemu/hw/mem/nvdimm.c47
-rw-r--r--qemu/hw/mem/pc-dimm.c61
-rw-r--r--qemu/hw/microblaze/boot.c8
-rw-r--r--qemu/hw/microblaze/petalogix_ml605_mmu.c26
-rw-r--r--qemu/hw/microblaze/petalogix_s3adsp1800_mmu.c24
-rw-r--r--qemu/hw/mips/Makefile.objs1
-rw-r--r--qemu/hw/mips/addr.c1
-rw-r--r--qemu/hw/mips/cps.c180
-rw-r--r--qemu/hw/mips/cputimer.c34
-rw-r--r--qemu/hw/mips/gt64xxx_pci.c10
-rw-r--r--qemu/hw/mips/mips_fulong2e.c32
-rw-r--r--qemu/hw/mips/mips_int.c1
-rw-r--r--qemu/hw/mips/mips_jazz.c66
-rw-r--r--qemu/hw/mips/mips_malta.c152
-rw-r--r--qemu/hw/mips/mips_mipssim.c21
-rw-r--r--qemu/hw/mips/mips_r4k.c27
-rw-r--r--qemu/hw/misc/Makefile.objs10
-rw-r--r--qemu/hw/misc/a9scu.c1
-rw-r--r--qemu/hw/misc/applesmc.c1
-rw-r--r--qemu/hw/misc/arm11scu.c1
-rw-r--r--qemu/hw/misc/arm_integrator_debug.c3
-rw-r--r--qemu/hw/misc/arm_l2x0.c1
-rw-r--r--qemu/hw/misc/arm_sysctl.c4
-rw-r--r--qemu/hw/misc/bcm2835_mbox.c335
-rw-r--r--qemu/hw/misc/bcm2835_property.c424
-rw-r--r--qemu/hw/misc/cbus.c3
-rw-r--r--qemu/hw/misc/debugexit.c1
-rw-r--r--qemu/hw/misc/eccmemctl.c1
-rw-r--r--qemu/hw/misc/edu.c13
-rw-r--r--qemu/hw/misc/exynos4210_pmu.c1
-rw-r--r--qemu/hw/misc/hyperv_testdev.c168
-rw-r--r--qemu/hw/misc/imx25_ccm.c317
-rw-r--r--qemu/hw/misc/imx31_ccm.c344
-rw-r--r--qemu/hw/misc/imx6_ccm.c774
-rw-r--r--qemu/hw/misc/imx_ccm.c309
-rw-r--r--qemu/hw/misc/ivshmem.c1334
-rw-r--r--qemu/hw/misc/macio/cuda.c480
-rw-r--r--qemu/hw/misc/macio/mac_dbdma.c78
-rw-r--r--qemu/hw/misc/macio/macio.c19
-rw-r--r--qemu/hw/misc/max111x.c3
-rw-r--r--qemu/hw/misc/milkymist-hpdmc.c1
-rw-r--r--qemu/hw/misc/milkymist-pfpu.c1
-rw-r--r--qemu/hw/misc/mips_cmgcr.c160
-rw-r--r--qemu/hw/misc/mips_cpc.c177
-rw-r--r--qemu/hw/misc/mips_itu.c521
-rw-r--r--qemu/hw/misc/mst_fpga.c1
-rw-r--r--qemu/hw/misc/omap_clk.c3
-rw-r--r--qemu/hw/misc/omap_gpmc.c4
-rw-r--r--qemu/hw/misc/omap_l4.c1
-rw-r--r--qemu/hw/misc/omap_sdrc.c4
-rw-r--r--qemu/hw/misc/omap_tap.c1
-rw-r--r--qemu/hw/misc/pc-testdev.c2
-rw-r--r--qemu/hw/misc/pci-testdev.c9
-rw-r--r--qemu/hw/misc/puv3_pm.c1
-rw-r--r--qemu/hw/misc/pvpanic.c4
-rw-r--r--qemu/hw/misc/sga.c1
-rw-r--r--qemu/hw/misc/slavio_misc.c1
-rw-r--r--qemu/hw/misc/stm32f2xx_syscfg.c1
-rw-r--r--qemu/hw/misc/tmp105.c14
-rw-r--r--qemu/hw/misc/vmport.c1
-rw-r--r--qemu/hw/misc/zynq-xadc.c303
-rw-r--r--qemu/hw/misc/zynq_slcr.c9
-rw-r--r--qemu/hw/moxie/moxiesim.c28
-rw-r--r--qemu/hw/net/Makefile.objs1
-rw-r--r--qemu/hw/net/allwinner_emac.c1
-rw-r--r--qemu/hw/net/cadence_gem.c21
-rw-r--r--qemu/hw/net/dp8393x.c4
-rw-r--r--qemu/hw/net/e1000.c499
-rw-r--r--qemu/hw/net/e1000_regs.h8
-rw-r--r--qemu/hw/net/eepro100.c18
-rw-r--r--qemu/hw/net/etraxfs_eth.c6
-rw-r--r--qemu/hw/net/fsl_etsec/etsec.c3
-rw-r--r--qemu/hw/net/fsl_etsec/miim.c1
-rw-r--r--qemu/hw/net/fsl_etsec/registers.c1
-rw-r--r--qemu/hw/net/fsl_etsec/registers.h1
-rw-r--r--qemu/hw/net/fsl_etsec/rings.c5
-rw-r--r--qemu/hw/net/imx_fec.c711
-rw-r--r--qemu/hw/net/lan9118.c21
-rw-r--r--qemu/hw/net/lance.c1
-rw-r--r--qemu/hw/net/mcf_fec.c1
-rw-r--r--qemu/hw/net/milkymist-minimac2.c6
-rw-r--r--qemu/hw/net/mipsnet.c1
-rw-r--r--qemu/hw/net/ne2000-isa.c17
-rw-r--r--qemu/hw/net/ne2000.c25
-rw-r--r--qemu/hw/net/ne2000.h1
-rw-r--r--qemu/hw/net/opencores_eth.c3
-rw-r--r--qemu/hw/net/pcnet-pci.c1
-rw-r--r--qemu/hw/net/pcnet.c18
-rw-r--r--qemu/hw/net/rocker/qmp-norocker.c1
-rw-r--r--qemu/hw/net/rocker/rocker.c59
-rw-r--r--qemu/hw/net/rocker/rocker_desc.c13
-rw-r--r--qemu/hw/net/rocker/rocker_fp.c8
-rw-r--r--qemu/hw/net/rocker/rocker_fp.h1
-rw-r--r--qemu/hw/net/rocker/rocker_of_dpa.c23
-rw-r--r--qemu/hw/net/rocker/rocker_world.c8
-rw-r--r--qemu/hw/net/rocker/rocker_world.h1
-rw-r--r--qemu/hw/net/rtl8139.c135
-rw-r--r--qemu/hw/net/smc91c111.c34
-rw-r--r--qemu/hw/net/spapr_llan.c323
-rw-r--r--qemu/hw/net/stellaris_enet.c13
-rw-r--r--qemu/hw/net/vhost_net.c140
-rw-r--r--qemu/hw/net/virtio-net.c223
-rw-r--r--qemu/hw/net/vmware_utils.h7
-rw-r--r--qemu/hw/net/vmxnet3.c205
-rw-r--r--qemu/hw/net/vmxnet3.h6
-rw-r--r--qemu/hw/net/vmxnet_debug.h139
-rw-r--r--qemu/hw/net/vmxnet_rx_pkt.c1
-rw-r--r--qemu/hw/net/vmxnet_rx_pkt.h2
-rw-r--r--qemu/hw/net/vmxnet_tx_pkt.c20
-rw-r--r--qemu/hw/net/vmxnet_tx_pkt.h2
-rw-r--r--qemu/hw/net/xen_nic.c30
-rw-r--r--qemu/hw/net/xgmac.c1
-rw-r--r--qemu/hw/net/xilinx_axienet.c2
-rw-r--r--qemu/hw/net/xilinx_ethlite.c3
-rw-r--r--qemu/hw/nvram/ds1225y.c1
-rw-r--r--qemu/hw/nvram/eeprom93xx.c1
-rw-r--r--qemu/hw/nvram/fw_cfg.c456
-rw-r--r--qemu/hw/nvram/mac_nvram.c9
-rw-r--r--qemu/hw/nvram/spapr_nvram.c4
-rw-r--r--qemu/hw/openrisc/cputimer.c8
-rw-r--r--qemu/hw/openrisc/openrisc_sim.c26
-rw-r--r--qemu/hw/openrisc/pic_cpu.c1
-rw-r--r--qemu/hw/pci-bridge/dec.c7
-rw-r--r--qemu/hw/pci-bridge/i82801b11.c15
-rw-r--r--qemu/hw/pci-bridge/ioh3420.c9
-rw-r--r--qemu/hw/pci-bridge/pci_bridge_dev.c12
-rw-r--r--qemu/hw/pci-bridge/pci_expander_bridge.c111
-rw-r--r--qemu/hw/pci-bridge/xio3130_downstream.c9
-rw-r--r--qemu/hw/pci-bridge/xio3130_upstream.c9
-rw-r--r--qemu/hw/pci-host/apb.c8
-rw-r--r--qemu/hw/pci-host/bonito.c41
-rw-r--r--qemu/hw/pci-host/gpex.c1
-rw-r--r--qemu/hw/pci-host/grackle.c3
-rw-r--r--qemu/hw/pci-host/pam.c1
-rw-r--r--qemu/hw/pci-host/piix.c137
-rw-r--r--qemu/hw/pci-host/ppce500.c5
-rw-r--r--qemu/hw/pci-host/prep.c16
-rw-r--r--qemu/hw/pci-host/q35.c52
-rw-r--r--qemu/hw/pci-host/uninorth.c22
-rw-r--r--qemu/hw/pci-host/versatile.c12
-rw-r--r--qemu/hw/pci/msi.c31
-rw-r--r--qemu/hw/pci/msix.c25
-rw-r--r--qemu/hw/pci/pci-stub.c1
-rw-r--r--qemu/hw/pci/pci.c97
-rw-r--r--qemu/hw/pci/pci_bridge.c4
-rw-r--r--qemu/hw/pci/pci_host.c16
-rw-r--r--qemu/hw/pci/pcie.c55
-rw-r--r--qemu/hw/pci/pcie_aer.c13
-rw-r--r--qemu/hw/pci/pcie_host.c1
-rw-r--r--qemu/hw/pci/pcie_port.c1
-rw-r--r--qemu/hw/pci/shpc.c4
-rw-r--r--qemu/hw/pci/slotid_cap.c1
-rw-r--r--qemu/hw/pcmcia/pcmcia.c1
-rw-r--r--qemu/hw/pcmcia/pxa2xx.c7
-rw-r--r--qemu/hw/ppc/Makefile.objs2
-rw-r--r--qemu/hw/ppc/e500.c13
-rw-r--r--qemu/hw/ppc/e500plat.c19
-rw-r--r--qemu/hw/ppc/mac.h6
-rw-r--r--qemu/hw/ppc/mac_newworld.c27
-rw-r--r--qemu/hw/ppc/mac_oldworld.c50
-rw-r--r--qemu/hw/ppc/mpc8544_guts.c3
-rw-r--r--qemu/hw/ppc/mpc8544ds.c17
-rw-r--r--qemu/hw/ppc/ppc.c28
-rw-r--r--qemu/hw/ppc/ppc405_boards.c101
-rw-r--r--qemu/hw/ppc/ppc405_uc.c10
-rw-r--r--qemu/hw/ppc/ppc440_bamboo.c21
-rw-r--r--qemu/hw/ppc/ppc4xx_devs.c1
-rw-r--r--qemu/hw/ppc/ppc4xx_pci.c1
-rw-r--r--qemu/hw/ppc/ppc_booke.c5
-rw-r--r--qemu/hw/ppc/ppce500_spin.c1
-rw-r--r--qemu/hw/ppc/prep.c73
-rw-r--r--qemu/hw/ppc/spapr.c1087
-rw-r--r--qemu/hw/ppc/spapr_drc.c184
-rw-r--r--qemu/hw/ppc/spapr_events.c69
-rw-r--r--qemu/hw/ppc/spapr_hcall.c335
-rw-r--r--qemu/hw/ppc/spapr_iommu.c39
-rw-r--r--qemu/hw/ppc/spapr_pci.c264
-rw-r--r--qemu/hw/ppc/spapr_pci_vfio.c137
-rw-r--r--qemu/hw/ppc/spapr_rng.c191
-rw-r--r--qemu/hw/ppc/spapr_rtas.c119
-rw-r--r--qemu/hw/ppc/spapr_rtc.c3
-rw-r--r--qemu/hw/ppc/spapr_vio.c21
-rw-r--r--qemu/hw/ppc/virtex_ml507.c17
-rw-r--r--qemu/hw/s390x/Makefile.objs4
-rw-r--r--qemu/hw/s390x/css.c282
-rw-r--r--qemu/hw/s390x/css.h15
-rw-r--r--qemu/hw/s390x/event-facility.c55
-rw-r--r--qemu/hw/s390x/ipl.c129
-rw-r--r--qemu/hw/s390x/ipl.h30
-rw-r--r--qemu/hw/s390x/s390-pci-bus.c49
-rw-r--r--qemu/hw/s390x/s390-pci-bus.h5
-rw-r--r--qemu/hw/s390x/s390-pci-inst.c53
-rw-r--r--qemu/hw/s390x/s390-skeys-kvm.c76
-rw-r--r--qemu/hw/s390x/s390-skeys.c415
-rw-r--r--qemu/hw/s390x/s390-virtio-bus.c763
-rw-r--r--qemu/hw/s390x/s390-virtio-bus.h186
-rw-r--r--qemu/hw/s390x/s390-virtio-ccw.c286
-rw-r--r--qemu/hw/s390x/s390-virtio-hcall.c1
-rw-r--r--qemu/hw/s390x/s390-virtio.c238
-rw-r--r--qemu/hw/s390x/s390-virtio.h4
-rw-r--r--qemu/hw/s390x/sclp.c273
-rw-r--r--qemu/hw/s390x/sclpcpu.c29
-rw-r--r--qemu/hw/s390x/sclpquiesce.c5
-rw-r--r--qemu/hw/s390x/virtio-ccw.c144
-rw-r--r--qemu/hw/s390x/virtio-ccw.h19
-rw-r--r--qemu/hw/scsi/Makefile.objs1
-rw-r--r--qemu/hw/scsi/esp-pci.c2
-rw-r--r--qemu/hw/scsi/esp.c2
-rw-r--r--qemu/hw/scsi/lsi53c895a.c2
-rw-r--r--qemu/hw/scsi/megasas.c19
-rw-r--r--qemu/hw/scsi/mpi.h1153
-rw-r--r--qemu/hw/scsi/mptconfig.c905
-rw-r--r--qemu/hw/scsi/mptendian.c204
-rw-r--r--qemu/hw/scsi/mptsas.c1442
-rw-r--r--qemu/hw/scsi/mptsas.h100
-rw-r--r--qemu/hw/scsi/scsi-bus.c31
-rw-r--r--qemu/hw/scsi/scsi-disk.c166
-rw-r--r--qemu/hw/scsi/scsi-generic.c178
-rw-r--r--qemu/hw/scsi/spapr_vscsi.c4
-rw-r--r--qemu/hw/scsi/vhost-scsi.c23
-rw-r--r--qemu/hw/scsi/virtio-scsi-dataplane.c218
-rw-r--r--qemu/hw/scsi/virtio-scsi.c228
-rw-r--r--qemu/hw/scsi/vmw_pvscsi.c98
-rw-r--r--qemu/hw/sd/Makefile.objs2
-rw-r--r--qemu/hw/sd/core.c146
-rw-r--r--qemu/hw/sd/milkymist-memcard.c3
-rw-r--r--qemu/hw/sd/omap_mmc.c9
-rw-r--r--qemu/hw/sd/pl181.c5
-rw-r--r--qemu/hw/sd/pxa2xx_mmci.c308
-rw-r--r--qemu/hw/sd/sd.c304
-rw-r--r--qemu/hw/sd/sdhci-internal.h (renamed from qemu/hw/sd/sdhci.h)73
-rw-r--r--qemu/hw/sd/sdhci.c205
-rw-r--r--qemu/hw/sd/ssi-sd.c5
-rw-r--r--qemu/hw/sh4/r2d.c25
-rw-r--r--qemu/hw/sh4/sh7750.c2
-rw-r--r--qemu/hw/sh4/sh7750_regnames.c1
-rw-r--r--qemu/hw/sh4/sh_pci.c6
-rw-r--r--qemu/hw/sh4/shix.c25
-rw-r--r--qemu/hw/smbios/Makefile.objs1
-rw-r--r--qemu/hw/smbios/smbios.c (renamed from qemu/hw/i386/smbios.c)150
-rw-r--r--qemu/hw/sparc/leon3.c21
-rw-r--r--qemu/hw/sparc/sun4m.c249
-rw-r--r--qemu/hw/sparc64/sun4u.c192
-rw-r--r--qemu/hw/ssi/omap_spi.c4
-rw-r--r--qemu/hw/ssi/pl022.c3
-rw-r--r--qemu/hw/ssi/ssi.c3
-rw-r--r--qemu/hw/ssi/xilinx_spi.c3
-rw-r--r--qemu/hw/ssi/xilinx_spips.c49
-rw-r--r--qemu/hw/timer/Makefile.objs2
-rw-r--r--qemu/hw/timer/a9gtimer.c3
-rw-r--r--qemu/hw/timer/allwinner-a10-pit.c1
-rw-r--r--qemu/hw/timer/arm_mptimer.c7
-rw-r--r--qemu/hw/timer/arm_timer.c43
-rw-r--r--qemu/hw/timer/aspeed_timer.c449
-rw-r--r--qemu/hw/timer/cadence_ttc.c1
-rw-r--r--qemu/hw/timer/digic-timer.c1
-rw-r--r--qemu/hw/timer/ds1338.c3
-rw-r--r--qemu/hw/timer/etraxfs_timer.c1
-rw-r--r--qemu/hw/timer/exynos4210_mct.c13
-rw-r--r--qemu/hw/timer/exynos4210_pwm.c13
-rw-r--r--qemu/hw/timer/exynos4210_rtc.c14
-rw-r--r--qemu/hw/timer/grlib_gptimer.c1
-rw-r--r--qemu/hw/timer/hpet.c28
-rw-r--r--qemu/hw/timer/i8254.c5
-rw-r--r--qemu/hw/timer/i8254_common.c7
-rw-r--r--qemu/hw/timer/imx_epit.c144
-rw-r--r--qemu/hw/timer/imx_gpt.c204
-rw-r--r--qemu/hw/timer/lm32_timer.c1
-rw-r--r--qemu/hw/timer/m48t59.c7
-rw-r--r--qemu/hw/timer/mc146818rtc.c18
-rw-r--r--qemu/hw/timer/milkymist-sysctl.c1
-rw-r--r--qemu/hw/timer/omap_gptimer.c6
-rw-r--r--qemu/hw/timer/omap_synctimer.c4
-rw-r--r--qemu/hw/timer/pl031.c23
-rw-r--r--qemu/hw/timer/puv3_ost.c1
-rw-r--r--qemu/hw/timer/pxa2xx_timer.c55
-rw-r--r--qemu/hw/timer/sh_timer.c1
-rw-r--r--qemu/hw/timer/slavio_timer.c1
-rw-r--r--qemu/hw/timer/stm32f2xx_timer.c1
-rw-r--r--qemu/hw/timer/twl92230.c2
-rw-r--r--qemu/hw/timer/xilinx_timer.c1
-rw-r--r--qemu/hw/tpm/tpm_passthrough.c33
-rw-r--r--qemu/hw/tpm/tpm_tis.c6
-rw-r--r--qemu/hw/tpm/tpm_util.c1
-rw-r--r--qemu/hw/tricore/tricore_testboard.c39
-rw-r--r--qemu/hw/unicore32/puv3.c31
-rw-r--r--qemu/hw/usb/Makefile.objs7
-rw-r--r--qemu/hw/usb/bus.c23
-rw-r--r--qemu/hw/usb/ccid-card-emulated.c30
-rw-r--r--qemu/hw/usb/ccid-card-passthru.c17
-rw-r--r--qemu/hw/usb/combined-packet.c1
-rw-r--r--qemu/hw/usb/core.c19
-rw-r--r--qemu/hw/usb/desc-msos.c1
-rw-r--r--qemu/hw/usb/desc.c2
-rw-r--r--qemu/hw/usb/desc.h1
-rw-r--r--qemu/hw/usb/dev-audio.c3
-rw-r--r--qemu/hw/usb/dev-bluetooth.c1
-rw-r--r--qemu/hw/usb/dev-hid.c2
-rw-r--r--qemu/hw/usb/dev-hub.c2
-rw-r--r--qemu/hw/usb/dev-mtp.c324
-rw-r--r--qemu/hw/usb/dev-network.c15
-rw-r--r--qemu/hw/usb/dev-serial.c3
-rw-r--r--qemu/hw/usb/dev-smartcard-reader.c2
-rw-r--r--qemu/hw/usb/dev-storage.c45
-rw-r--r--qemu/hw/usb/dev-uas.c1
-rw-r--r--qemu/hw/usb/dev-wacom.c1
-rw-r--r--qemu/hw/usb/hcd-ehci-pci.c7
-rw-r--r--qemu/hw/usb/hcd-ehci-sysbus.c1
-rw-r--r--qemu/hw/usb/hcd-ehci.c32
-rw-r--r--qemu/hw/usb/hcd-musb.c3
-rw-r--r--qemu/hw/usb/hcd-ohci.c129
-rw-r--r--qemu/hw/usb/hcd-uhci.c30
-rw-r--r--qemu/hw/usb/hcd-xhci.c13
-rw-r--r--qemu/hw/usb/host-legacy.c1
-rw-r--r--qemu/hw/usb/host-libusb.c7
-rw-r--r--qemu/hw/usb/host-stub.c1
-rw-r--r--qemu/hw/usb/libhw.c1
-rw-r--r--qemu/hw/usb/quirks.c1
-rw-r--r--qemu/hw/usb/redirect.c25
-rw-r--r--qemu/hw/usb/tusb6010.c (renamed from qemu/hw/timer/tusb6010.c)7
-rw-r--r--qemu/hw/vfio/Makefile.objs3
-rw-r--r--qemu/hw/vfio/amd-xgbe.c56
-rw-r--r--qemu/hw/vfio/calxeda-xgmac.c1
-rw-r--r--qemu/hw/vfio/common.c456
-rw-r--r--qemu/hw/vfio/pci-quirks.c1205
-rw-r--r--qemu/hw/vfio/pci.c2061
-rw-r--r--qemu/hw/vfio/pci.h162
-rw-r--r--qemu/hw/vfio/platform.c250
-rw-r--r--qemu/hw/virtio/Makefile.objs1
-rw-r--r--qemu/hw/virtio/dataplane/Makefile.objs1
-rw-r--r--qemu/hw/virtio/dataplane/vring.c453
-rw-r--r--qemu/hw/virtio/vhost-backend.c147
-rw-r--r--qemu/hw/virtio/vhost-user.c624
-rw-r--r--qemu/hw/virtio/vhost.c213
-rw-r--r--qemu/hw/virtio/virtio-balloon.c124
-rw-r--r--qemu/hw/virtio/virtio-bus.c4
-rw-r--r--qemu/hw/virtio/virtio-mmio.c1
-rw-r--r--qemu/hw/virtio/virtio-pci.c398
-rw-r--r--qemu/hw/virtio/virtio-pci.h49
-rw-r--r--qemu/hw/virtio/virtio-rng.c19
-rw-r--r--qemu/hw/virtio/virtio.c562
-rw-r--r--qemu/hw/watchdog/watchdog.c17
-rw-r--r--qemu/hw/watchdog/wdt_diag288.c16
-rw-r--r--qemu/hw/watchdog/wdt_i6300esb.c13
-rw-r--r--qemu/hw/watchdog/wdt_ib700.c3
-rw-r--r--qemu/hw/xen/Makefile.objs2
-rw-r--r--qemu/hw/xen/xen-host-pci-device.c158
-rw-r--r--qemu/hw/xen/xen-host-pci-device.h7
-rw-r--r--qemu/hw/xen/xen_backend.c56
-rw-r--r--qemu/hw/xen/xen_devconfig.c1
-rw-r--r--qemu/hw/xen/xen_pt.c253
-rw-r--r--qemu/hw/xen/xen_pt.h40
-rw-r--r--qemu/hw/xen/xen_pt_config_init.c356
-rw-r--r--qemu/hw/xen/xen_pt_graphics.c275
-rw-r--r--qemu/hw/xen/xen_pt_msi.c124
-rw-r--r--qemu/hw/xenpv/Makefile.objs4
-rw-r--r--qemu/hw/xenpv/xen_domainbuild.c13
-rw-r--r--qemu/hw/xenpv/xen_machine_pv.c33
-rw-r--r--qemu/hw/xtensa/pic_cpu.c1
-rw-r--r--qemu/hw/xtensa/sim.c29
-rw-r--r--qemu/hw/xtensa/xtfpga.c136
741 files changed, 44453 insertions, 26801 deletions
diff --git a/qemu/hw/9pfs/virtio-9p-handle.c b/qemu/hw/9pfs/9p-handle.c
index 13eabb98a..894041488 100644
--- a/qemu/hw/9pfs/virtio-9p-handle.c
+++ b/qemu/hw/9pfs/9p-handle.c
@@ -1,5 +1,5 @@
/*
- * Virtio 9p handle callback
+ * 9p handle callback
*
* Copyright IBM, Corp. 2011
*
@@ -11,16 +11,17 @@
*
*/
-#include "hw/virtio/virtio.h"
-#include "virtio-9p.h"
-#include "virtio-9p-xattr.h"
+#include "qemu/osdep.h"
+#include "9p.h"
+#include "9p-xattr.h"
#include <arpa/inet.h>
#include <pwd.h>
#include <grp.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "qemu/xattr.h"
-#include <unistd.h>
+#include "qemu/cutils.h"
+#include "qemu/error-report.h"
#include <linux/fs.h>
#ifdef CONFIG_LINUX_MAGIC_H
#include <linux/magic.h>
@@ -656,12 +657,12 @@ static int handle_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse)
const char *path = qemu_opt_get(opts, "path");
if (sec_model) {
- fprintf(stderr, "Invalid argument security_model specified with handle fsdriver\n");
+ error_report("Invalid argument security_model specified with handle fsdriver");
return -1;
}
if (!path) {
- fprintf(stderr, "fsdev: No path specified.\n");
+ error_report("fsdev: No path specified");
return -1;
}
fse->path = g_strdup(path);
diff --git a/qemu/hw/9pfs/virtio-9p-local.c b/qemu/hw/9pfs/9p-local.c
index f1f2e2573..16f45f485 100644
--- a/qemu/hw/9pfs/virtio-9p-local.c
+++ b/qemu/hw/9pfs/9p-local.c
@@ -1,5 +1,5 @@
/*
- * Virtio 9p Posix callback
+ * 9p Posix callback
*
* Copyright IBM, Corp. 2010
*
@@ -11,9 +11,9 @@
*
*/
-#include "hw/virtio/virtio.h"
-#include "virtio-9p.h"
-#include "virtio-9p-xattr.h"
+#include "qemu/osdep.h"
+#include "9p.h"
+#include "9p-xattr.h"
#include "fsdev/qemu-fsdev.h" /* local_ops */
#include <arpa/inet.h>
#include <pwd.h>
@@ -21,6 +21,8 @@
#include <sys/socket.h>
#include <sys/un.h>
#include "qemu/xattr.h"
+#include "qemu/cutils.h"
+#include "qemu/error-report.h"
#include <libgen.h>
#include <linux/fs.h>
#ifdef CONFIG_LINUX_MAGIC_H
@@ -1210,9 +1212,9 @@ static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse)
const char *path = qemu_opt_get(opts, "path");
if (!sec_model) {
- fprintf(stderr, "security model not specified, "
- "local fs needs security model\nvalid options are:"
- "\tsecurity_model=[passthrough|mapped|none]\n");
+ error_report("Security model not specified, local fs needs security model");
+ error_printf("valid options are:"
+ "\tsecurity_model=[passthrough|mapped-xattr|mapped-file|none]\n");
return -1;
}
@@ -1226,14 +1228,14 @@ static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse)
} else if (!strcmp(sec_model, "mapped-file")) {
fse->export_flags |= V9FS_SM_MAPPED_FILE;
} else {
- fprintf(stderr, "Invalid security model %s specified, valid options are"
- "\n\t [passthrough|mapped-xattr|mapped-file|none]\n",
- sec_model);
+ error_report("Invalid security model %s specified", sec_model);
+ error_printf("valid options are:"
+ "\t[passthrough|mapped-xattr|mapped-file|none]\n");
return -1;
}
if (!path) {
- fprintf(stderr, "fsdev: No path specified.\n");
+ error_report("fsdev: No path specified");
return -1;
}
fse->path = g_strdup(path);
diff --git a/qemu/hw/9pfs/virtio-9p-posix-acl.c b/qemu/hw/9pfs/9p-posix-acl.c
index 09dad071e..ec003181c 100644
--- a/qemu/hw/9pfs/virtio-9p-posix-acl.c
+++ b/qemu/hw/9pfs/9p-posix-acl.c
@@ -1,5 +1,5 @@
/*
- * Virtio 9p system.posix* xattr callback
+ * 9p system.posix* xattr callback
*
* Copyright IBM, Corp. 2010
*
@@ -11,12 +11,11 @@
*
*/
-#include <sys/types.h>
+#include "qemu/osdep.h"
#include "qemu/xattr.h"
-#include "hw/virtio/virtio.h"
-#include "virtio-9p.h"
+#include "9p.h"
#include "fsdev/file-op-9p.h"
-#include "virtio-9p-xattr.h"
+#include "9p-xattr.h"
#define MAP_ACL_ACCESS "user.virtfs.system.posix_acl_access"
#define MAP_ACL_DEFAULT "user.virtfs.system.posix_acl_default"
diff --git a/qemu/hw/9pfs/virtio-9p-proxy.c b/qemu/hw/9pfs/9p-proxy.c
index 1bc7881f0..00a4eb2a7 100644
--- a/qemu/hw/9pfs/virtio-9p-proxy.c
+++ b/qemu/hw/9pfs/9p-proxy.c
@@ -1,5 +1,5 @@
/*
- * Virtio 9p Proxy callback
+ * 9p Proxy callback
*
* Copyright IBM, Corp. 2011
*
@@ -9,13 +9,14 @@
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
#include <sys/socket.h>
#include <sys/un.h>
-#include "hw/virtio/virtio.h"
-#include "virtio-9p.h"
+#include "9p.h"
+#include "qemu/cutils.h"
#include "qemu/error-report.h"
#include "fsdev/qemu-fsdev.h"
-#include "virtio-9p-proxy.h"
+#include "9p-proxy.h"
typedef struct V9fsProxy {
int sockfd;
@@ -1101,19 +1102,19 @@ static int connect_namedsocket(const char *path)
struct sockaddr_un helper;
if (strlen(path) >= sizeof(helper.sun_path)) {
- fprintf(stderr, "Socket name too large\n");
+ error_report("Socket name too long");
return -1;
}
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sockfd < 0) {
- fprintf(stderr, "failed to create socket: %s\n", strerror(errno));
+ error_report("Failed to create socket: %s", strerror(errno));
return -1;
}
strcpy(helper.sun_path, path);
helper.sun_family = AF_UNIX;
size = strlen(helper.sun_path) + sizeof(helper.sun_family);
if (connect(sockfd, (struct sockaddr *)&helper, size) < 0) {
- fprintf(stderr, "failed to connect to %s: %s\n", path, strerror(errno));
+ error_report("Failed to connect to %s: %s", path, strerror(errno));
close(sockfd);
return -1;
}
@@ -1129,11 +1130,11 @@ static int proxy_parse_opts(QemuOpts *opts, struct FsDriverEntry *fs)
const char *sock_fd = qemu_opt_get(opts, "sock_fd");
if (!socket && !sock_fd) {
- fprintf(stderr, "socket and sock_fd none of the option specified\n");
+ error_report("Must specify either socket or sock_fd");
return -1;
}
if (socket && sock_fd) {
- fprintf(stderr, "Both socket and sock_fd options specified\n");
+ error_report("Both socket and sock_fd options specified");
return -1;
}
if (socket) {
@@ -1156,7 +1157,7 @@ static int proxy_init(FsContext *ctx)
} else {
sock_id = atoi(ctx->fs_root);
if (sock_id < 0) {
- fprintf(stderr, "socket descriptor not initialized\n");
+ error_report("Socket descriptor not initialized");
}
}
if (sock_id < 0) {
diff --git a/qemu/hw/9pfs/virtio-9p-proxy.h b/qemu/hw/9pfs/9p-proxy.h
index 005c1ad75..ba9ca203d 100644
--- a/qemu/hw/9pfs/virtio-9p-proxy.h
+++ b/qemu/hw/9pfs/9p-proxy.h
@@ -1,5 +1,5 @@
/*
- * Virtio 9p Proxy callback
+ * 9p Proxy callback
*
* Copyright IBM, Corp. 2011
*
@@ -9,8 +9,8 @@
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*/
-#ifndef _QEMU_VIRTIO_9P_PROXY_H
-#define _QEMU_VIRTIO_9P_PROXY_H
+#ifndef _QEMU_9P_PROXY_H
+#define _QEMU_9P_PROXY_H
#define PROXY_MAX_IO_SZ (64 * 1024)
#define V9FS_FD_VALID INT_MAX
@@ -20,9 +20,9 @@
* marsha/unmarshal doesn't do little endian conversion.
*/
#define proxy_unmarshal(in_sg, offset, fmt, args...) \
- v9fs_unmarshal(in_sg, 1, offset, 0, fmt, ##args)
+ v9fs_iov_unmarshal(in_sg, 1, offset, 0, fmt, ##args)
#define proxy_marshal(out_sg, offset, fmt, args...) \
- v9fs_marshal(out_sg, 1, offset, 0, fmt, ##args)
+ v9fs_iov_marshal(out_sg, 1, offset, 0, fmt, ##args)
union MsgControl {
struct cmsghdr cmsg;
diff --git a/qemu/hw/9pfs/virtio-9p-synth.c b/qemu/hw/9pfs/9p-synth.c
index a0ab9a86a..f1475dfd6 100644
--- a/qemu/hw/9pfs/virtio-9p-synth.c
+++ b/qemu/hw/9pfs/9p-synth.c
@@ -12,14 +12,15 @@
*
*/
+#include "qemu/osdep.h"
#include "hw/virtio/virtio.h"
-#include "virtio-9p.h"
-#include "virtio-9p-xattr.h"
+#include "9p.h"
+#include "9p-xattr.h"
#include "fsdev/qemu-fsdev.h"
-#include "virtio-9p-synth.h"
+#include "9p-synth.h"
#include "qemu/rcu.h"
#include "qemu/rcu_queue.h"
-#include <sys/stat.h>
+#include "qemu/cutils.h"
/* Root node for synth file system */
static V9fsSynthNode v9fs_synth_root = {
diff --git a/qemu/hw/9pfs/virtio-9p-synth.h b/qemu/hw/9pfs/9p-synth.h
index ab05a8e78..82962512a 100644
--- a/qemu/hw/9pfs/virtio-9p-synth.h
+++ b/qemu/hw/9pfs/9p-synth.h
@@ -1,5 +1,5 @@
/*
- * Virtio 9p
+ * 9p
*
* Copyright IBM, Corp. 2011
*
@@ -10,12 +10,9 @@
* the COPYING file in the top-level directory.
*
*/
-#ifndef HW_9PFS_VIRTIO9P_SYNTH_H
-#define HW_9PFS_VIRTIO9P_SYNTH_H 1
+#ifndef HW_9PFS_SYNTH_H
+#define HW_9PFS_SYNTH_H 1
-#include <unistd.h>
-#include <sys/types.h>
-#include <limits.h>
typedef struct V9fsSynthNode V9fsSynthNode;
typedef ssize_t (*v9fs_synth_read)(void *buf, int len, off_t offset,
diff --git a/qemu/hw/9pfs/virtio-9p-xattr-user.c b/qemu/hw/9pfs/9p-xattr-user.c
index 46133e06d..f87530c8b 100644
--- a/qemu/hw/9pfs/virtio-9p-xattr-user.c
+++ b/qemu/hw/9pfs/9p-xattr-user.c
@@ -1,5 +1,5 @@
/*
- * Virtio 9p user. xattr callback
+ * 9p user. xattr callback
*
* Copyright IBM, Corp. 2010
*
@@ -11,11 +11,10 @@
*
*/
-#include <sys/types.h>
-#include "hw/virtio/virtio.h"
-#include "virtio-9p.h"
+#include "qemu/osdep.h"
+#include "9p.h"
#include "fsdev/file-op-9p.h"
-#include "virtio-9p-xattr.h"
+#include "9p-xattr.h"
static ssize_t mp_user_getxattr(FsContext *ctx, const char *path,
diff --git a/qemu/hw/9pfs/virtio-9p-xattr.c b/qemu/hw/9pfs/9p-xattr.c
index 07183887c..5d8595ed9 100644
--- a/qemu/hw/9pfs/virtio-9p-xattr.c
+++ b/qemu/hw/9pfs/9p-xattr.c
@@ -1,5 +1,5 @@
/*
- * Virtio 9p xattr callback
+ * 9p xattr callback
*
* Copyright IBM, Corp. 2010
*
@@ -11,10 +11,10 @@
*
*/
-#include "hw/virtio/virtio.h"
-#include "virtio-9p.h"
+#include "qemu/osdep.h"
+#include "9p.h"
#include "fsdev/file-op-9p.h"
-#include "virtio-9p-xattr.h"
+#include "9p-xattr.h"
static XattrOperations *get_xattr_operations(XattrOperations **h,
diff --git a/qemu/hw/9pfs/virtio-9p-xattr.h b/qemu/hw/9pfs/9p-xattr.h
index 327b32b5a..4d39a2026 100644
--- a/qemu/hw/9pfs/virtio-9p-xattr.h
+++ b/qemu/hw/9pfs/9p-xattr.h
@@ -1,5 +1,5 @@
/*
- * Virtio 9p
+ * 9p
*
* Copyright IBM, Corp. 2010
*
@@ -10,8 +10,8 @@
* the COPYING file in the top-level directory.
*
*/
-#ifndef _QEMU_VIRTIO_9P_XATTR_H
-#define _QEMU_VIRTIO_9P_XATTR_H
+#ifndef _QEMU_9P_XATTR_H
+#define _QEMU_9P_XATTR_H
#include "qemu/xattr.h"
diff --git a/qemu/hw/9pfs/virtio-9p.c b/qemu/hw/9pfs/9p.c
index f972731f5..f5e30125f 100644
--- a/qemu/hw/9pfs/virtio-9p.c
+++ b/qemu/hw/9pfs/9p.c
@@ -11,15 +11,17 @@
*
*/
+#include "qemu/osdep.h"
#include "hw/virtio/virtio.h"
#include "hw/i386/pc.h"
+#include "qapi/error.h"
#include "qemu/error-report.h"
#include "qemu/iov.h"
#include "qemu/sockets.h"
#include "virtio-9p.h"
#include "fsdev/qemu-fsdev.h"
-#include "virtio-9p-xattr.h"
-#include "virtio-9p-coth.h"
+#include "9p-xattr.h"
+#include "coth.h"
#include "trace.h"
#include "migration/migration.h"
@@ -39,6 +41,35 @@ enum {
Oappend = 0x80,
};
+ssize_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
+{
+ ssize_t ret;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ret = virtio_pdu_vmarshal(pdu, offset, fmt, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+ssize_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
+{
+ ssize_t ret;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ret = virtio_pdu_vunmarshal(pdu, offset, fmt, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+static void pdu_push_and_notify(V9fsPDU *pdu)
+{
+ virtio_9p_push_and_notify(pdu);
+}
+
static int omode_to_uflags(int8_t mode)
{
int ret = 0;
@@ -563,7 +594,7 @@ static int fid_to_qid(V9fsPDU *pdu, V9fsFidState *fidp, V9fsQID *qidp)
return 0;
}
-static V9fsPDU *alloc_pdu(V9fsState *s)
+V9fsPDU *pdu_alloc(V9fsState *s)
{
V9fsPDU *pdu = NULL;
@@ -575,9 +606,10 @@ static V9fsPDU *alloc_pdu(V9fsState *s)
return pdu;
}
-static void free_pdu(V9fsState *s, V9fsPDU *pdu)
+void pdu_free(V9fsPDU *pdu)
{
if (pdu) {
+ V9fsState *s = pdu->s;
/*
* Cancelled pdu are added back to the freelist
* by flush request .
@@ -594,9 +626,10 @@ static void free_pdu(V9fsState *s, V9fsPDU *pdu)
* because we always expect to have enough space to encode
* error details
*/
-static void complete_pdu(V9fsState *s, V9fsPDU *pdu, ssize_t len)
+static void pdu_complete(V9fsPDU *pdu, ssize_t len)
{
int8_t id = pdu->id + 1; /* Response */
+ V9fsState *s = pdu->s;
if (len < 0) {
int err = -len;
@@ -627,16 +660,12 @@ static void complete_pdu(V9fsState *s, V9fsPDU *pdu, ssize_t len)
pdu->size = len;
pdu->id = id;
- /* push onto queue and notify */
- virtqueue_push(s->vq, &pdu->elem, len);
-
- /* FIXME: we should batch these completions */
- virtio_notify(VIRTIO_DEVICE(s), s->vq);
+ pdu_push_and_notify(pdu);
/* Now wakeup anybody waiting in flush for this request */
qemu_co_queue_next(&pdu->complete);
- free_pdu(s, pdu);
+ pdu_free(pdu);
}
static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension)
@@ -931,7 +960,7 @@ static void v9fs_version(void *opaque)
offset += err;
trace_v9fs_version_return(pdu->tag, pdu->id, s->msize, version.data);
out:
- complete_pdu(s, pdu, offset);
+ pdu_complete(pdu, offset);
v9fs_string_free(&version);
}
@@ -995,7 +1024,7 @@ static void v9fs_attach(void *opaque)
out:
put_fid(pdu, fidp);
out_nofid:
- complete_pdu(s, pdu, err);
+ pdu_complete(pdu, err);
v9fs_string_free(&uname);
v9fs_string_free(&aname);
}
@@ -1009,7 +1038,6 @@ static void v9fs_stat(void *opaque)
struct stat stbuf;
V9fsFidState *fidp;
V9fsPDU *pdu = opaque;
- V9fsState *s = pdu->s;
err = pdu_unmarshal(pdu, offset, "d", &fid);
if (err < 0) {
@@ -1042,7 +1070,7 @@ static void v9fs_stat(void *opaque)
out:
put_fid(pdu, fidp);
out_nofid:
- complete_pdu(s, pdu, err);
+ pdu_complete(pdu, err);
}
static void v9fs_getattr(void *opaque)
@@ -1105,7 +1133,7 @@ static void v9fs_getattr(void *opaque)
out:
put_fid(pdu, fidp);
out_nofid:
- complete_pdu(s, pdu, retval);
+ pdu_complete(pdu, retval);
}
/* Attribute flags */
@@ -1129,7 +1157,6 @@ static void v9fs_setattr(void *opaque)
size_t offset = 7;
V9fsIattr v9iattr;
V9fsPDU *pdu = opaque;
- V9fsState *s = pdu->s;
err = pdu_unmarshal(pdu, offset, "dI", &fid, &v9iattr);
if (err < 0) {
@@ -1203,7 +1230,7 @@ static void v9fs_setattr(void *opaque)
out:
put_fid(pdu, fidp);
out_nofid:
- complete_pdu(s, pdu, err);
+ pdu_complete(pdu, err);
}
static int v9fs_walk_marshal(V9fsPDU *pdu, uint16_t nwnames, V9fsQID *qids)
@@ -1245,7 +1272,7 @@ static void v9fs_walk(void *opaque)
err = pdu_unmarshal(pdu, offset, "ddw", &fid, &newfid, &nwnames);
if (err < 0) {
- complete_pdu(s, pdu, err);
+ pdu_complete(pdu, err);
return ;
}
offset += err;
@@ -1313,7 +1340,7 @@ out:
v9fs_path_free(&dpath);
v9fs_path_free(&path);
out_nofid:
- complete_pdu(s, pdu, err);
+ pdu_complete(pdu, err);
if (nwnames && nwnames <= P9_MAXWELEM) {
for (name_idx = 0; name_idx < nwnames; name_idx++) {
v9fs_string_free(&wnames[name_idx]);
@@ -1430,7 +1457,7 @@ static void v9fs_open(void *opaque)
out:
put_fid(pdu, fidp);
out_nofid:
- complete_pdu(s, pdu, err);
+ pdu_complete(pdu, err);
}
static void v9fs_lcreate(void *opaque)
@@ -1487,7 +1514,7 @@ static void v9fs_lcreate(void *opaque)
out:
put_fid(pdu, fidp);
out_nofid:
- complete_pdu(pdu->s, pdu, err);
+ pdu_complete(pdu, err);
v9fs_string_free(&name);
}
@@ -1499,7 +1526,6 @@ static void v9fs_fsync(void *opaque)
size_t offset = 7;
V9fsFidState *fidp;
V9fsPDU *pdu = opaque;
- V9fsState *s = pdu->s;
err = pdu_unmarshal(pdu, offset, "dd", &fid, &datasync);
if (err < 0) {
@@ -1518,7 +1544,7 @@ static void v9fs_fsync(void *opaque)
}
put_fid(pdu, fidp);
out_nofid:
- complete_pdu(s, pdu, err);
+ pdu_complete(pdu, err);
}
static void v9fs_clunk(void *opaque)
@@ -1551,7 +1577,7 @@ static void v9fs_clunk(void *opaque)
err = offset;
}
out_nofid:
- complete_pdu(s, pdu, err);
+ pdu_complete(pdu, err);
}
static int v9fs_xattr_read(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp,
@@ -1561,6 +1587,8 @@ static int v9fs_xattr_read(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp,
size_t offset = 7;
int read_count;
int64_t xattr_len;
+ V9fsVirtioState *v = container_of(s, V9fsVirtioState, state);
+ VirtQueueElement *elem = v->elems[pdu->idx];
xattr_len = fidp->fs.xattr.len;
read_count = xattr_len - off;
@@ -1577,7 +1605,8 @@ static int v9fs_xattr_read(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp,
return err;
}
offset += err;
- err = v9fs_pack(pdu->elem.in_sg, pdu->elem.in_num, offset,
+
+ err = v9fs_pack(elem->in_sg, elem->in_num, offset,
((char *)fidp->fs.xattr.value) + off,
read_count);
if (err < 0) {
@@ -1667,13 +1696,7 @@ static void v9fs_init_qiov_from_pdu(QEMUIOVector *qiov, V9fsPDU *pdu,
struct iovec *iov;
unsigned int niov;
- if (is_write) {
- iov = pdu->elem.out_sg;
- niov = pdu->elem.out_num;
- } else {
- iov = pdu->elem.in_sg;
- niov = pdu->elem.in_num;
- }
+ virtio_init_iov_from_pdu(pdu, &iov, &niov, is_write);
qemu_iovec_init_external(&elem, iov, niov);
qemu_iovec_init(qiov, niov);
@@ -1761,7 +1784,7 @@ static void v9fs_read(void *opaque)
out:
put_fid(pdu, fidp);
out_nofid:
- complete_pdu(s, pdu, err);
+ pdu_complete(pdu, err);
}
static size_t v9fs_readdir_data_size(V9fsString *name)
@@ -1848,7 +1871,6 @@ static void v9fs_readdir(void *opaque)
int32_t count;
uint32_t max_count;
V9fsPDU *pdu = opaque;
- V9fsState *s = pdu->s;
retval = pdu_unmarshal(pdu, offset, "dqd", &fid,
&initial_offset, &max_count);
@@ -1885,7 +1907,7 @@ static void v9fs_readdir(void *opaque)
out:
put_fid(pdu, fidp);
out_nofid:
- complete_pdu(s, pdu, retval);
+ pdu_complete(pdu, retval);
}
static int v9fs_xattr_write(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp,
@@ -1952,7 +1974,7 @@ static void v9fs_write(void *opaque)
err = pdu_unmarshal(pdu, offset, "dqd", &fid, &off, &count);
if (err < 0) {
- complete_pdu(s, pdu, err);
+ pdu_complete(pdu, err);
return;
}
offset += err;
@@ -2015,7 +2037,7 @@ out:
put_fid(pdu, fidp);
out_nofid:
qemu_iovec_destroy(&qiov_full);
- complete_pdu(s, pdu, err);
+ pdu_complete(pdu, err);
}
static void v9fs_create(void *opaque)
@@ -2182,7 +2204,7 @@ static void v9fs_create(void *opaque)
out:
put_fid(pdu, fidp);
out_nofid:
- complete_pdu(pdu->s, pdu, err);
+ pdu_complete(pdu, err);
v9fs_string_free(&name);
v9fs_string_free(&extension);
v9fs_path_free(&path);
@@ -2229,7 +2251,7 @@ static void v9fs_symlink(void *opaque)
out:
put_fid(pdu, dfidp);
out_nofid:
- complete_pdu(pdu->s, pdu, err);
+ pdu_complete(pdu, err);
v9fs_string_free(&name);
v9fs_string_free(&symname);
}
@@ -2245,7 +2267,7 @@ static void v9fs_flush(void *opaque)
err = pdu_unmarshal(pdu, offset, "w", &tag);
if (err < 0) {
- complete_pdu(s, pdu, err);
+ pdu_complete(pdu, err);
return;
}
trace_v9fs_flush(pdu->tag, pdu->id, tag);
@@ -2262,15 +2284,14 @@ static void v9fs_flush(void *opaque)
*/
qemu_co_queue_wait(&cancel_pdu->complete);
cancel_pdu->cancelled = 0;
- free_pdu(pdu->s, cancel_pdu);
+ pdu_free(cancel_pdu);
}
- complete_pdu(s, pdu, 7);
+ pdu_complete(pdu, 7);
}
static void v9fs_link(void *opaque)
{
V9fsPDU *pdu = opaque;
- V9fsState *s = pdu->s;
int32_t dfid, oldfid;
V9fsFidState *dfidp, *oldfidp;
V9fsString name;
@@ -2303,7 +2324,7 @@ out:
put_fid(pdu, dfidp);
out_nofid:
v9fs_string_free(&name);
- complete_pdu(s, pdu, err);
+ pdu_complete(pdu, err);
}
/* Only works with path name based fid */
@@ -2348,7 +2369,7 @@ out_err:
clunk_fid(pdu->s, fidp->fid);
put_fid(pdu, fidp);
out_nofid:
- complete_pdu(pdu->s, pdu, err);
+ pdu_complete(pdu, err);
}
static void v9fs_unlinkat(void *opaque)
@@ -2392,7 +2413,7 @@ out_err:
put_fid(pdu, dfidp);
v9fs_path_free(&path);
out_nofid:
- complete_pdu(pdu->s, pdu, err);
+ pdu_complete(pdu, err);
v9fs_string_free(&name);
}
@@ -2492,7 +2513,7 @@ static void v9fs_rename(void *opaque)
out:
put_fid(pdu, fidp);
out_nofid:
- complete_pdu(s, pdu, err);
+ pdu_complete(pdu, err);
v9fs_string_free(&name);
}
@@ -2593,7 +2614,7 @@ static void v9fs_renameat(void *opaque)
}
out_err:
- complete_pdu(s, pdu, err);
+ pdu_complete(pdu, err);
v9fs_string_free(&old_name);
v9fs_string_free(&new_name);
}
@@ -2608,7 +2629,6 @@ static void v9fs_wstat(void *opaque)
struct stat stbuf;
V9fsFidState *fidp;
V9fsPDU *pdu = opaque;
- V9fsState *s = pdu->s;
v9fs_stat_init(&v9stat);
err = pdu_unmarshal(pdu, offset, "dwS", &fid, &unused, &v9stat);
@@ -2690,7 +2710,7 @@ out:
put_fid(pdu, fidp);
out_nofid:
v9fs_stat_free(&v9stat);
- complete_pdu(s, pdu, err);
+ pdu_complete(pdu, err);
}
static int v9fs_fill_statfs(V9fsState *s, V9fsPDU *pdu, struct statfs *stbuf)
@@ -2769,7 +2789,7 @@ static void v9fs_statfs(void *opaque)
out:
put_fid(pdu, fidp);
out_nofid:
- complete_pdu(s, pdu, retval);
+ pdu_complete(pdu, retval);
}
static void v9fs_mknod(void *opaque)
@@ -2786,7 +2806,6 @@ static void v9fs_mknod(void *opaque)
struct stat stbuf;
V9fsFidState *fidp;
V9fsPDU *pdu = opaque;
- V9fsState *s = pdu->s;
v9fs_string_init(&name);
err = pdu_unmarshal(pdu, offset, "dsdddd", &fid, &name, &mode,
@@ -2817,7 +2836,7 @@ static void v9fs_mknod(void *opaque)
out:
put_fid(pdu, fidp);
out_nofid:
- complete_pdu(s, pdu, err);
+ pdu_complete(pdu, err);
v9fs_string_free(&name);
}
@@ -2838,7 +2857,6 @@ static void v9fs_lock(void *opaque)
V9fsFidState *fidp;
int32_t fid, err = 0;
V9fsPDU *pdu = opaque;
- V9fsState *s = pdu->s;
status = P9_LOCK_ERROR;
v9fs_string_init(&flock.client_id);
@@ -2875,7 +2893,7 @@ out_nofid:
err += offset;
}
trace_v9fs_lock_return(pdu->tag, pdu->id, status);
- complete_pdu(s, pdu, err);
+ pdu_complete(pdu, err);
v9fs_string_free(&flock.client_id);
}
@@ -2891,7 +2909,6 @@ static void v9fs_getlock(void *opaque)
V9fsGetlock glock;
int32_t fid, err = 0;
V9fsPDU *pdu = opaque;
- V9fsState *s = pdu->s;
v9fs_string_init(&glock.client_id);
err = pdu_unmarshal(pdu, offset, "dbqqds", &fid, &glock.type,
@@ -2925,7 +2942,7 @@ static void v9fs_getlock(void *opaque)
out:
put_fid(pdu, fidp);
out_nofid:
- complete_pdu(s, pdu, err);
+ pdu_complete(pdu, err);
v9fs_string_free(&glock.client_id);
}
@@ -2969,7 +2986,7 @@ static void v9fs_mkdir(void *opaque)
out:
put_fid(pdu, fidp);
out_nofid:
- complete_pdu(pdu->s, pdu, err);
+ pdu_complete(pdu, err);
v9fs_string_free(&name);
}
@@ -3075,7 +3092,7 @@ out:
put_fid(pdu, xattr_fidp);
}
out_nofid:
- complete_pdu(s, pdu, err);
+ pdu_complete(pdu, err);
v9fs_string_free(&name);
}
@@ -3090,7 +3107,6 @@ static void v9fs_xattrcreate(void *opaque)
V9fsFidState *file_fidp;
V9fsFidState *xattr_fidp;
V9fsPDU *pdu = opaque;
- V9fsState *s = pdu->s;
v9fs_string_init(&name);
err = pdu_unmarshal(pdu, offset, "dsqd", &fid, &name, &size, &flags);
@@ -3116,7 +3132,7 @@ static void v9fs_xattrcreate(void *opaque)
err = offset;
put_fid(pdu, file_fidp);
out_nofid:
- complete_pdu(s, pdu, err);
+ pdu_complete(pdu, err);
v9fs_string_free(&name);
}
@@ -3156,7 +3172,7 @@ static void v9fs_readlink(void *opaque)
out:
put_fid(pdu, fidp);
out_nofid:
- complete_pdu(pdu->s, pdu, err);
+ pdu_complete(pdu, err);
}
static CoroutineEntry *pdu_co_handlers[] = {
@@ -3199,13 +3215,13 @@ static CoroutineEntry *pdu_co_handlers[] = {
static void v9fs_op_not_supp(void *opaque)
{
V9fsPDU *pdu = opaque;
- complete_pdu(pdu->s, pdu, -EOPNOTSUPP);
+ pdu_complete(pdu, -EOPNOTSUPP);
}
static void v9fs_fs_ro(void *opaque)
{
V9fsPDU *pdu = opaque;
- complete_pdu(pdu->s, pdu, -EROFS);
+ pdu_complete(pdu, -EROFS);
}
static inline bool is_read_only_op(V9fsPDU *pdu)
@@ -3235,10 +3251,11 @@ static inline bool is_read_only_op(V9fsPDU *pdu)
}
}
-static void submit_pdu(V9fsState *s, V9fsPDU *pdu)
+void pdu_submit(V9fsPDU *pdu)
{
Coroutine *co;
CoroutineEntry *handler;
+ V9fsState *s = pdu->s;
if (pdu->id >= ARRAY_SIZE(pdu_co_handlers) ||
(pdu_co_handlers[pdu->id] == NULL)) {
@@ -3254,45 +3271,108 @@ static void submit_pdu(V9fsState *s, V9fsPDU *pdu)
qemu_coroutine_enter(co, pdu);
}
-void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq)
+/* Returns 0 on success, 1 on failure. */
+int v9fs_device_realize_common(V9fsState *s, Error **errp)
{
- V9fsState *s = (V9fsState *)vdev;
- V9fsPDU *pdu;
- ssize_t len;
+ V9fsVirtioState *v = container_of(s, V9fsVirtioState, state);
+ int i, len;
+ struct stat stat;
+ FsDriverEntry *fse;
+ V9fsPath path;
+ int rc = 1;
+
+ /* initialize pdu allocator */
+ QLIST_INIT(&s->free_list);
+ QLIST_INIT(&s->active_list);
+ for (i = 0; i < (MAX_REQ - 1); i++) {
+ QLIST_INSERT_HEAD(&s->free_list, &v->pdus[i], next);
+ v->pdus[i].s = s;
+ v->pdus[i].idx = i;
+ }
+
+ v9fs_path_init(&path);
+
+ fse = get_fsdev_fsentry(s->fsconf.fsdev_id);
+
+ if (!fse) {
+ /* We don't have a fsdev identified by fsdev_id */
+ error_setg(errp, "9pfs device couldn't find fsdev with the "
+ "id = %s",
+ s->fsconf.fsdev_id ? s->fsconf.fsdev_id : "NULL");
+ goto out;
+ }
+
+ if (!s->fsconf.tag) {
+ /* we haven't specified a mount_tag */
+ error_setg(errp, "fsdev with id %s needs mount_tag arguments",
+ s->fsconf.fsdev_id);
+ goto out;
+ }
+
+ s->ctx.export_flags = fse->export_flags;
+ s->ctx.fs_root = g_strdup(fse->path);
+ s->ctx.exops.get_st_gen = NULL;
+ len = strlen(s->fsconf.tag);
+ if (len > MAX_TAG_LEN - 1) {
+ error_setg(errp, "mount tag '%s' (%d bytes) is longer than "
+ "maximum (%d bytes)", s->fsconf.tag, len, MAX_TAG_LEN - 1);
+ goto out;
+ }
- while ((pdu = alloc_pdu(s)) &&
- (len = virtqueue_pop(vq, &pdu->elem)) != 0) {
- struct {
- uint32_t size_le;
- uint8_t id;
- uint16_t tag_le;
- } QEMU_PACKED out;
- int len;
+ s->tag = g_strdup(s->fsconf.tag);
+ s->ctx.uid = -1;
- pdu->s = s;
- BUG_ON(pdu->elem.out_num == 0 || pdu->elem.in_num == 0);
- QEMU_BUILD_BUG_ON(sizeof out != 7);
+ s->ops = fse->ops;
- len = iov_to_buf(pdu->elem.out_sg, pdu->elem.out_num, 0,
- &out, sizeof out);
- BUG_ON(len != sizeof out);
+ s->fid_list = NULL;
+ qemu_co_rwlock_init(&s->rename_lock);
- pdu->size = le32_to_cpu(out.size_le);
+ if (s->ops->init(&s->ctx) < 0) {
+ error_setg(errp, "9pfs Failed to initialize fs-driver with id:%s"
+ " and export path:%s", s->fsconf.fsdev_id, s->ctx.fs_root);
+ goto out;
+ }
- pdu->id = out.id;
- pdu->tag = le16_to_cpu(out.tag_le);
+ /*
+ * Check details of export path, We need to use fs driver
+ * call back to do that. Since we are in the init path, we don't
+ * use co-routines here.
+ */
+ if (s->ops->name_to_path(&s->ctx, NULL, "/", &path) < 0) {
+ error_setg(errp,
+ "error in converting name to path %s", strerror(errno));
+ goto out;
+ }
+ if (s->ops->lstat(&s->ctx, &path, &stat)) {
+ error_setg(errp, "share path %s does not exist", fse->path);
+ goto out;
+ } else if (!S_ISDIR(stat.st_mode)) {
+ error_setg(errp, "share path %s is not a directory", fse->path);
+ goto out;
+ }
+ v9fs_path_free(&path);
- qemu_co_queue_init(&pdu->complete);
- submit_pdu(s, pdu);
+ rc = 0;
+out:
+ if (rc) {
+ g_free(s->ctx.fs_root);
+ g_free(s->tag);
+ v9fs_path_free(&path);
}
- free_pdu(s, pdu);
+ return rc;
+}
+
+void v9fs_device_unrealize_common(V9fsState *s, Error **errp)
+{
+ g_free(s->ctx.fs_root);
+ g_free(s->tag);
}
-static void __attribute__((__constructor__)) virtio_9p_set_fd_limit(void)
+static void __attribute__((__constructor__)) v9fs_set_fd_limit(void)
{
struct rlimit rlim;
if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
- fprintf(stderr, "Failed to get the resource limit\n");
+ error_report("Failed to get the resource limit");
exit(1);
}
open_fd_hw = rlim.rlim_cur - MIN(400, rlim.rlim_cur/3);
diff --git a/qemu/hw/9pfs/9p.h b/qemu/hw/9pfs/9p.h
new file mode 100644
index 000000000..1a19418a8
--- /dev/null
+++ b/qemu/hw/9pfs/9p.h
@@ -0,0 +1,324 @@
+#ifndef _QEMU_9P_H
+#define _QEMU_9P_H
+
+#include <dirent.h>
+#include <utime.h>
+#include <sys/resource.h>
+#include <glib.h>
+#include "standard-headers/linux/virtio_9p.h"
+#include "hw/virtio/virtio.h"
+#include "fsdev/file-op-9p.h"
+#include "fsdev/9p-iov-marshal.h"
+#include "qemu/thread.h"
+#include "qemu/coroutine.h"
+
+enum {
+ P9_TLERROR = 6,
+ P9_RLERROR,
+ P9_TSTATFS = 8,
+ P9_RSTATFS,
+ P9_TLOPEN = 12,
+ P9_RLOPEN,
+ P9_TLCREATE = 14,
+ P9_RLCREATE,
+ P9_TSYMLINK = 16,
+ P9_RSYMLINK,
+ P9_TMKNOD = 18,
+ P9_RMKNOD,
+ P9_TRENAME = 20,
+ P9_RRENAME,
+ P9_TREADLINK = 22,
+ P9_RREADLINK,
+ P9_TGETATTR = 24,
+ P9_RGETATTR,
+ P9_TSETATTR = 26,
+ P9_RSETATTR,
+ P9_TXATTRWALK = 30,
+ P9_RXATTRWALK,
+ P9_TXATTRCREATE = 32,
+ P9_RXATTRCREATE,
+ P9_TREADDIR = 40,
+ P9_RREADDIR,
+ P9_TFSYNC = 50,
+ P9_RFSYNC,
+ P9_TLOCK = 52,
+ P9_RLOCK,
+ P9_TGETLOCK = 54,
+ P9_RGETLOCK,
+ P9_TLINK = 70,
+ P9_RLINK,
+ P9_TMKDIR = 72,
+ P9_RMKDIR,
+ P9_TRENAMEAT = 74,
+ P9_RRENAMEAT,
+ P9_TUNLINKAT = 76,
+ P9_RUNLINKAT,
+ P9_TVERSION = 100,
+ P9_RVERSION,
+ P9_TAUTH = 102,
+ P9_RAUTH,
+ P9_TATTACH = 104,
+ P9_RATTACH,
+ P9_TERROR = 106,
+ P9_RERROR,
+ P9_TFLUSH = 108,
+ P9_RFLUSH,
+ P9_TWALK = 110,
+ P9_RWALK,
+ P9_TOPEN = 112,
+ P9_ROPEN,
+ P9_TCREATE = 114,
+ P9_RCREATE,
+ P9_TREAD = 116,
+ P9_RREAD,
+ P9_TWRITE = 118,
+ P9_RWRITE,
+ P9_TCLUNK = 120,
+ P9_RCLUNK,
+ P9_TREMOVE = 122,
+ P9_RREMOVE,
+ P9_TSTAT = 124,
+ P9_RSTAT,
+ P9_TWSTAT = 126,
+ P9_RWSTAT,
+};
+
+
+/* qid.types */
+enum {
+ P9_QTDIR = 0x80,
+ P9_QTAPPEND = 0x40,
+ P9_QTEXCL = 0x20,
+ P9_QTMOUNT = 0x10,
+ P9_QTAUTH = 0x08,
+ P9_QTTMP = 0x04,
+ P9_QTSYMLINK = 0x02,
+ P9_QTLINK = 0x01,
+ P9_QTFILE = 0x00,
+};
+
+enum p9_proto_version {
+ V9FS_PROTO_2000U = 0x01,
+ V9FS_PROTO_2000L = 0x02,
+};
+
+#define P9_NOTAG (u16)(~0)
+#define P9_NOFID (u32)(~0)
+#define P9_MAXWELEM 16
+
+#define FID_REFERENCED 0x1
+#define FID_NON_RECLAIMABLE 0x2
+static inline char *rpath(FsContext *ctx, const char *path)
+{
+ return g_strdup_printf("%s/%s", ctx->fs_root, path);
+}
+
+/*
+ * ample room for Twrite/Rread header
+ * size[4] Tread/Twrite tag[2] fid[4] offset[8] count[4]
+ */
+#define P9_IOHDRSZ 24
+
+typedef struct V9fsPDU V9fsPDU;
+struct V9fsState;
+
+struct V9fsPDU
+{
+ uint32_t size;
+ uint16_t tag;
+ uint8_t id;
+ uint8_t cancelled;
+ CoQueue complete;
+ struct V9fsState *s;
+ QLIST_ENTRY(V9fsPDU) next;
+ uint32_t idx;
+};
+
+
+/* FIXME
+ * 1) change user needs to set groups and stuff
+ */
+
+#define MAX_REQ 128
+#define MAX_TAG_LEN 32
+
+#define BUG_ON(cond) assert(!(cond))
+
+typedef struct V9fsFidState V9fsFidState;
+
+enum {
+ P9_FID_NONE = 0,
+ P9_FID_FILE,
+ P9_FID_DIR,
+ P9_FID_XATTR,
+};
+
+typedef struct V9fsConf
+{
+ /* tag name for the device */
+ char *tag;
+ char *fsdev_id;
+} V9fsConf;
+
+typedef struct V9fsXattr
+{
+ int64_t copied_len;
+ int64_t len;
+ void *value;
+ V9fsString name;
+ int flags;
+} V9fsXattr;
+
+/*
+ * Filled by fs driver on open and other
+ * calls.
+ */
+union V9fsFidOpenState {
+ int fd;
+ DIR *dir;
+ V9fsXattr xattr;
+ /*
+ * private pointer for fs drivers, that
+ * have its own internal representation of
+ * open files.
+ */
+ void *private;
+};
+
+struct V9fsFidState
+{
+ int fid_type;
+ int32_t fid;
+ V9fsPath path;
+ V9fsFidOpenState fs;
+ V9fsFidOpenState fs_reclaim;
+ int flags;
+ int open_flags;
+ uid_t uid;
+ int ref;
+ int clunked;
+ V9fsFidState *next;
+ V9fsFidState *rclm_lst;
+};
+
+typedef struct V9fsState
+{
+ QLIST_HEAD(, V9fsPDU) free_list;
+ QLIST_HEAD(, V9fsPDU) active_list;
+ V9fsFidState *fid_list;
+ FileOperations *ops;
+ FsContext ctx;
+ char *tag;
+ enum p9_proto_version proto_version;
+ int32_t msize;
+ /*
+ * lock ensuring atomic path update
+ * on rename.
+ */
+ CoRwlock rename_lock;
+ int32_t root_fid;
+ Error *migration_blocker;
+ V9fsConf fsconf;
+} V9fsState;
+
+/* 9p2000.L open flags */
+#define P9_DOTL_RDONLY 00000000
+#define P9_DOTL_WRONLY 00000001
+#define P9_DOTL_RDWR 00000002
+#define P9_DOTL_NOACCESS 00000003
+#define P9_DOTL_CREATE 00000100
+#define P9_DOTL_EXCL 00000200
+#define P9_DOTL_NOCTTY 00000400
+#define P9_DOTL_TRUNC 00001000
+#define P9_DOTL_APPEND 00002000
+#define P9_DOTL_NONBLOCK 00004000
+#define P9_DOTL_DSYNC 00010000
+#define P9_DOTL_FASYNC 00020000
+#define P9_DOTL_DIRECT 00040000
+#define P9_DOTL_LARGEFILE 00100000
+#define P9_DOTL_DIRECTORY 00200000
+#define P9_DOTL_NOFOLLOW 00400000
+#define P9_DOTL_NOATIME 01000000
+#define P9_DOTL_CLOEXEC 02000000
+#define P9_DOTL_SYNC 04000000
+
+/* 9p2000.L at flags */
+#define P9_DOTL_AT_REMOVEDIR 0x200
+
+/* 9P2000.L lock type */
+#define P9_LOCK_TYPE_RDLCK 0
+#define P9_LOCK_TYPE_WRLCK 1
+#define P9_LOCK_TYPE_UNLCK 2
+
+#define P9_LOCK_SUCCESS 0
+#define P9_LOCK_BLOCKED 1
+#define P9_LOCK_ERROR 2
+#define P9_LOCK_GRACE 3
+
+#define P9_LOCK_FLAGS_BLOCK 1
+#define P9_LOCK_FLAGS_RECLAIM 2
+
+typedef struct V9fsFlock
+{
+ uint8_t type;
+ uint32_t flags;
+ uint64_t start; /* absolute offset */
+ uint64_t length;
+ uint32_t proc_id;
+ V9fsString client_id;
+} V9fsFlock;
+
+typedef struct V9fsGetlock
+{
+ uint8_t type;
+ uint64_t start; /* absolute offset */
+ uint64_t length;
+ uint32_t proc_id;
+ V9fsString client_id;
+} V9fsGetlock;
+
+extern int open_fd_hw;
+extern int total_open_fd;
+
+static inline void v9fs_path_write_lock(V9fsState *s)
+{
+ if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) {
+ qemu_co_rwlock_wrlock(&s->rename_lock);
+ }
+}
+
+static inline void v9fs_path_read_lock(V9fsState *s)
+{
+ if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) {
+ qemu_co_rwlock_rdlock(&s->rename_lock);
+ }
+}
+
+static inline void v9fs_path_unlock(V9fsState *s)
+{
+ if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) {
+ qemu_co_rwlock_unlock(&s->rename_lock);
+ }
+}
+
+static inline uint8_t v9fs_request_cancelled(V9fsPDU *pdu)
+{
+ return pdu->cancelled;
+}
+
+extern void v9fs_reclaim_fd(V9fsPDU *pdu);
+extern void v9fs_path_init(V9fsPath *path);
+extern void v9fs_path_free(V9fsPath *path);
+extern void v9fs_path_copy(V9fsPath *lhs, V9fsPath *rhs);
+extern int v9fs_name_to_path(V9fsState *s, V9fsPath *dirpath,
+ const char *name, V9fsPath *path);
+extern int v9fs_device_realize_common(V9fsState *s, Error **errp);
+extern void v9fs_device_unrealize_common(V9fsState *s, Error **errp);
+
+ssize_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...);
+ssize_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...);
+V9fsPDU *pdu_alloc(V9fsState *s);
+void pdu_free(V9fsPDU *pdu);
+void pdu_submit(V9fsPDU *pdu);
+
+#endif
diff --git a/qemu/hw/9pfs/Makefile.objs b/qemu/hw/9pfs/Makefile.objs
index 1e9b595cb..da0ae0cfd 100644
--- a/qemu/hw/9pfs/Makefile.objs
+++ b/qemu/hw/9pfs/Makefile.objs
@@ -1,9 +1,9 @@
-common-obj-y = virtio-9p.o
-common-obj-y += virtio-9p-local.o virtio-9p-xattr.o
-common-obj-y += virtio-9p-xattr-user.o virtio-9p-posix-acl.o
-common-obj-y += virtio-9p-coth.o cofs.o codir.o cofile.o
-common-obj-y += coxattr.o virtio-9p-synth.o
-common-obj-$(CONFIG_OPEN_BY_HANDLE) += virtio-9p-handle.o
-common-obj-y += virtio-9p-proxy.o
+common-obj-y = 9p.o
+common-obj-y += 9p-local.o 9p-xattr.o
+common-obj-y += 9p-xattr-user.o 9p-posix-acl.o
+common-obj-y += coth.o cofs.o codir.o cofile.o
+common-obj-y += coxattr.o 9p-synth.o
+common-obj-$(CONFIG_OPEN_BY_HANDLE) += 9p-handle.o
+common-obj-y += 9p-proxy.o
obj-y += virtio-9p-device.o
diff --git a/qemu/hw/9pfs/codir.c b/qemu/hw/9pfs/codir.c
index 65ad3298b..91df7f7a7 100644
--- a/qemu/hw/9pfs/codir.c
+++ b/qemu/hw/9pfs/codir.c
@@ -12,10 +12,11 @@
*
*/
+#include "qemu/osdep.h"
#include "fsdev/qemu-fsdev.h"
#include "qemu/thread.h"
-#include "block/coroutine.h"
-#include "virtio-9p-coth.h"
+#include "qemu/coroutine.h"
+#include "coth.h"
int v9fs_co_readdir_r(V9fsPDU *pdu, V9fsFidState *fidp, struct dirent *dent,
struct dirent **result)
diff --git a/qemu/hw/9pfs/cofile.c b/qemu/hw/9pfs/cofile.c
index 2efebf357..293483e0c 100644
--- a/qemu/hw/9pfs/cofile.c
+++ b/qemu/hw/9pfs/cofile.c
@@ -12,10 +12,11 @@
*
*/
+#include "qemu/osdep.h"
#include "fsdev/qemu-fsdev.h"
#include "qemu/thread.h"
-#include "block/coroutine.h"
-#include "virtio-9p-coth.h"
+#include "qemu/coroutine.h"
+#include "coth.h"
int v9fs_co_st_gen(V9fsPDU *pdu, V9fsPath *path, mode_t st_mode,
V9fsStatDotl *v9stat)
diff --git a/qemu/hw/9pfs/cofs.c b/qemu/hw/9pfs/cofs.c
index 42ee614e2..18c81cb3d 100644
--- a/qemu/hw/9pfs/cofs.c
+++ b/qemu/hw/9pfs/cofs.c
@@ -12,10 +12,11 @@
*
*/
+#include "qemu/osdep.h"
#include "fsdev/qemu-fsdev.h"
#include "qemu/thread.h"
-#include "block/coroutine.h"
-#include "virtio-9p-coth.h"
+#include "qemu/coroutine.h"
+#include "coth.h"
static ssize_t __readlink(V9fsState *s, V9fsPath *path, V9fsString *buf)
{
diff --git a/qemu/hw/9pfs/coth.c b/qemu/hw/9pfs/coth.c
new file mode 100644
index 000000000..464293ef2
--- /dev/null
+++ b/qemu/hw/9pfs/coth.c
@@ -0,0 +1,42 @@
+/*
+ * 9p backend
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Harsh Prateek Bora <harsh@linux.vnet.ibm.com>
+ * Venkateswararao Jujjuri(JV) <jvrao@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "block/thread-pool.h"
+#include "qemu/coroutine.h"
+#include "qemu/main-loop.h"
+#include "coth.h"
+
+/* Called from QEMU I/O thread. */
+static void coroutine_enter_cb(void *opaque, int ret)
+{
+ Coroutine *co = opaque;
+ qemu_coroutine_enter(co, NULL);
+}
+
+/* Called from worker thread. */
+static int coroutine_enter_func(void *arg)
+{
+ Coroutine *co = arg;
+ qemu_coroutine_enter(co, NULL);
+ return 0;
+}
+
+void co_run_in_worker_bh(void *opaque)
+{
+ Coroutine *co = opaque;
+ thread_pool_submit_aio(aio_get_thread_pool(qemu_get_aio_context()),
+ coroutine_enter_func, co, coroutine_enter_cb, co);
+}
diff --git a/qemu/hw/9pfs/virtio-9p-coth.h b/qemu/hw/9pfs/coth.h
index 4f51b250d..209fc6a9a 100644
--- a/qemu/hw/9pfs/virtio-9p-coth.h
+++ b/qemu/hw/9pfs/coth.h
@@ -1,5 +1,5 @@
/*
- * Virtio 9p backend
+ * 9p backend
*
* Copyright IBM, Corp. 2010
*
@@ -12,20 +12,12 @@
*
*/
-#ifndef _QEMU_VIRTIO_9P_COTH_H
-#define _QEMU_VIRTIO_9P_COTH_H
+#ifndef _QEMU_9P_COTH_H
+#define _QEMU_9P_COTH_H
#include "qemu/thread.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
#include "virtio-9p.h"
-#include <glib.h>
-
-typedef struct V9fsThPool {
- EventNotifier e;
-
- GThreadPool *pool;
- GAsyncQueue *completed;
-} V9fsThPool;
/*
* we want to use bottom half because we want to make sure the below
@@ -45,7 +37,7 @@ typedef struct V9fsThPool {
qemu_bh_schedule(co_bh); \
/* \
* yield in qemu thread and re-enter back \
- * in glib worker thread \
+ * in worker thread \
*/ \
qemu_coroutine_yield(); \
qemu_bh_delete(co_bh); \
diff --git a/qemu/hw/9pfs/coxattr.c b/qemu/hw/9pfs/coxattr.c
index 18ee08df0..6ad96ea9f 100644
--- a/qemu/hw/9pfs/coxattr.c
+++ b/qemu/hw/9pfs/coxattr.c
@@ -12,10 +12,11 @@
*
*/
+#include "qemu/osdep.h"
#include "fsdev/qemu-fsdev.h"
#include "qemu/thread.h"
-#include "block/coroutine.h"
-#include "virtio-9p-coth.h"
+#include "qemu/coroutine.h"
+#include "coth.h"
int v9fs_co_llistxattr(V9fsPDU *pdu, V9fsPath *path, void *value, size_t size)
{
diff --git a/qemu/hw/9pfs/virtio-9p-coth.c b/qemu/hw/9pfs/virtio-9p-coth.c
deleted file mode 100644
index 8185c533c..000000000
--- a/qemu/hw/9pfs/virtio-9p-coth.c
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Virtio 9p backend
- *
- * Copyright IBM, Corp. 2010
- *
- * Authors:
- * Harsh Prateek Bora <harsh@linux.vnet.ibm.com>
- * Venkateswararao Jujjuri(JV) <jvrao@linux.vnet.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
- *
- */
-
-#include "fsdev/qemu-fsdev.h"
-#include "qemu/thread.h"
-#include "qemu/event_notifier.h"
-#include "block/coroutine.h"
-#include "virtio-9p-coth.h"
-
-/* v9fs glib thread pool */
-static V9fsThPool v9fs_pool;
-
-void co_run_in_worker_bh(void *opaque)
-{
- Coroutine *co = opaque;
- g_thread_pool_push(v9fs_pool.pool, co, NULL);
-}
-
-static void v9fs_qemu_process_req_done(EventNotifier *e)
-{
- Coroutine *co;
-
- event_notifier_test_and_clear(e);
-
- while ((co = g_async_queue_try_pop(v9fs_pool.completed)) != NULL) {
- qemu_coroutine_enter(co, NULL);
- }
-}
-
-static void v9fs_thread_routine(gpointer data, gpointer user_data)
-{
- Coroutine *co = data;
-
- qemu_coroutine_enter(co, NULL);
-
- g_async_queue_push(v9fs_pool.completed, co);
-
- event_notifier_set(&v9fs_pool.e);
-}
-
-int v9fs_init_worker_threads(void)
-{
- int ret = 0;
- V9fsThPool *p = &v9fs_pool;
- sigset_t set, oldset;
-
- sigfillset(&set);
- /* Leave signal handling to the iothread. */
- pthread_sigmask(SIG_SETMASK, &set, &oldset);
-
- p->pool = g_thread_pool_new(v9fs_thread_routine, p, -1, FALSE, NULL);
- if (!p->pool) {
- ret = -1;
- goto err_out;
- }
- p->completed = g_async_queue_new();
- if (!p->completed) {
- /*
- * We are going to terminate.
- * So don't worry about cleanup
- */
- ret = -1;
- goto err_out;
- }
- event_notifier_init(&p->e, 0);
-
- event_notifier_set_handler(&p->e, v9fs_qemu_process_req_done);
-err_out:
- pthread_sigmask(SIG_SETMASK, &oldset, NULL);
- return ret;
-}
diff --git a/qemu/hw/9pfs/virtio-9p-device.c b/qemu/hw/9pfs/virtio-9p-device.c
index 93a407c45..a38850ee8 100644
--- a/qemu/hw/9pfs/virtio-9p-device.c
+++ b/qemu/hw/9pfs/virtio-9p-device.c
@@ -11,15 +11,70 @@
*
*/
+#include "qemu/osdep.h"
#include "hw/virtio/virtio.h"
-#include "hw/virtio/virtio-9p.h"
#include "hw/i386/pc.h"
#include "qemu/sockets.h"
#include "virtio-9p.h"
#include "fsdev/qemu-fsdev.h"
-#include "virtio-9p-xattr.h"
-#include "virtio-9p-coth.h"
+#include "9p-xattr.h"
+#include "coth.h"
#include "hw/virtio/virtio-access.h"
+#include "qemu/iov.h"
+
+void virtio_9p_push_and_notify(V9fsPDU *pdu)
+{
+ V9fsState *s = pdu->s;
+ V9fsVirtioState *v = container_of(s, V9fsVirtioState, state);
+ VirtQueueElement *elem = v->elems[pdu->idx];
+
+ /* push onto queue and notify */
+ virtqueue_push(v->vq, elem, pdu->size);
+ g_free(elem);
+ v->elems[pdu->idx] = NULL;
+
+ /* FIXME: we should batch these completions */
+ virtio_notify(VIRTIO_DEVICE(v), v->vq);
+}
+
+static void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq)
+{
+ V9fsVirtioState *v = (V9fsVirtioState *)vdev;
+ V9fsState *s = &v->state;
+ V9fsPDU *pdu;
+ ssize_t len;
+
+ while ((pdu = pdu_alloc(s))) {
+ struct {
+ uint32_t size_le;
+ uint8_t id;
+ uint16_t tag_le;
+ } QEMU_PACKED out;
+ VirtQueueElement *elem;
+
+ elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
+ if (!elem) {
+ pdu_free(pdu);
+ break;
+ }
+
+ BUG_ON(elem->out_num == 0 || elem->in_num == 0);
+ QEMU_BUILD_BUG_ON(sizeof out != 7);
+
+ v->elems[pdu->idx] = elem;
+ len = iov_to_buf(elem->out_sg, elem->out_num, 0,
+ &out, sizeof out);
+ BUG_ON(len != sizeof out);
+
+ pdu->size = le32_to_cpu(out.size_le);
+
+ pdu->id = out.id;
+ pdu->tag = le16_to_cpu(out.tag_le);
+
+ qemu_co_queue_init(&pdu->complete);
+ pdu_submit(pdu);
+ }
+}
static uint64_t virtio_9p_get_features(VirtIODevice *vdev, uint64_t features,
Error **errp)
@@ -32,117 +87,99 @@ static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config)
{
int len;
struct virtio_9p_config *cfg;
- V9fsState *s = VIRTIO_9P(vdev);
+ V9fsVirtioState *v = VIRTIO_9P(vdev);
+ V9fsState *s = &v->state;
len = strlen(s->tag);
cfg = g_malloc0(sizeof(struct virtio_9p_config) + len);
virtio_stw_p(vdev, &cfg->tag_len, len);
/* We don't copy the terminating null to config space */
memcpy(cfg->tag, s->tag, len);
- memcpy(config, cfg, s->config_size);
+ memcpy(config, cfg, v->config_size);
g_free(cfg);
}
+static void virtio_9p_save(QEMUFile *f, void *opaque)
+{
+ virtio_save(VIRTIO_DEVICE(opaque), f);
+}
+
+static int virtio_9p_load(QEMUFile *f, void *opaque, int version_id)
+{
+ return virtio_load(VIRTIO_DEVICE(opaque), f, version_id);
+}
+
static void virtio_9p_device_realize(DeviceState *dev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
- V9fsState *s = VIRTIO_9P(dev);
- int i, len;
- struct stat stat;
- FsDriverEntry *fse;
- V9fsPath path;
-
- virtio_init(vdev, "virtio-9p", VIRTIO_ID_9P,
- sizeof(struct virtio_9p_config) + MAX_TAG_LEN);
-
- /* initialize pdu allocator */
- QLIST_INIT(&s->free_list);
- QLIST_INIT(&s->active_list);
- for (i = 0; i < (MAX_REQ - 1); i++) {
- QLIST_INSERT_HEAD(&s->free_list, &s->pdus[i], next);
- }
+ V9fsVirtioState *v = VIRTIO_9P(dev);
+ V9fsState *s = &v->state;
- s->vq = virtio_add_queue(vdev, MAX_REQ, handle_9p_output);
+ if (v9fs_device_realize_common(s, errp)) {
+ goto out;
+ }
- v9fs_path_init(&path);
+ v->config_size = sizeof(struct virtio_9p_config) + strlen(s->fsconf.tag);
+ virtio_init(vdev, "virtio-9p", VIRTIO_ID_9P, v->config_size);
+ v->vq = virtio_add_queue(vdev, MAX_REQ, handle_9p_output);
+ register_savevm(dev, "virtio-9p", -1, 1, virtio_9p_save, virtio_9p_load, v);
- fse = get_fsdev_fsentry(s->fsconf.fsdev_id);
+out:
+ return;
+}
- if (!fse) {
- /* We don't have a fsdev identified by fsdev_id */
- error_setg(errp, "Virtio-9p device couldn't find fsdev with the "
- "id = %s",
- s->fsconf.fsdev_id ? s->fsconf.fsdev_id : "NULL");
- goto out;
- }
+static void virtio_9p_device_unrealize(DeviceState *dev, Error **errp)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ V9fsVirtioState *v = VIRTIO_9P(dev);
+ V9fsState *s = &v->state;
- if (!s->fsconf.tag) {
- /* we haven't specified a mount_tag */
- error_setg(errp, "fsdev with id %s needs mount_tag arguments",
- s->fsconf.fsdev_id);
- goto out;
- }
+ virtio_cleanup(vdev);
+ unregister_savevm(dev, "virtio-9p", v);
+ v9fs_device_unrealize_common(s, errp);
+}
- s->ctx.export_flags = fse->export_flags;
- s->ctx.fs_root = g_strdup(fse->path);
- s->ctx.exops.get_st_gen = NULL;
- len = strlen(s->fsconf.tag);
- if (len > MAX_TAG_LEN - 1) {
- error_setg(errp, "mount tag '%s' (%d bytes) is longer than "
- "maximum (%d bytes)", s->fsconf.tag, len, MAX_TAG_LEN - 1);
- goto out;
- }
+ssize_t virtio_pdu_vmarshal(V9fsPDU *pdu, size_t offset,
+ const char *fmt, va_list ap)
+{
+ V9fsState *s = pdu->s;
+ V9fsVirtioState *v = container_of(s, V9fsVirtioState, state);
+ VirtQueueElement *elem = v->elems[pdu->idx];
- s->tag = g_strdup(s->fsconf.tag);
- s->ctx.uid = -1;
+ return v9fs_iov_vmarshal(elem->in_sg, elem->in_num, offset, 1, fmt, ap);
+}
- s->ops = fse->ops;
- s->config_size = sizeof(struct virtio_9p_config) + len;
- s->fid_list = NULL;
- qemu_co_rwlock_init(&s->rename_lock);
+ssize_t virtio_pdu_vunmarshal(V9fsPDU *pdu, size_t offset,
+ const char *fmt, va_list ap)
+{
+ V9fsState *s = pdu->s;
+ V9fsVirtioState *v = container_of(s, V9fsVirtioState, state);
+ VirtQueueElement *elem = v->elems[pdu->idx];
- if (s->ops->init(&s->ctx) < 0) {
- error_setg(errp, "Virtio-9p Failed to initialize fs-driver with id:%s"
- " and export path:%s", s->fsconf.fsdev_id, s->ctx.fs_root);
- goto out;
- }
- if (v9fs_init_worker_threads() < 0) {
- error_setg(errp, "worker thread initialization failed");
- goto out;
- }
+ return v9fs_iov_vunmarshal(elem->out_sg, elem->out_num, offset, 1, fmt, ap);
+}
- /*
- * Check details of export path, We need to use fs driver
- * call back to do that. Since we are in the init path, we don't
- * use co-routines here.
- */
- if (s->ops->name_to_path(&s->ctx, NULL, "/", &path) < 0) {
- error_setg(errp,
- "error in converting name to path %s", strerror(errno));
- goto out;
- }
- if (s->ops->lstat(&s->ctx, &path, &stat)) {
- error_setg(errp, "share path %s does not exist", fse->path);
- goto out;
- } else if (!S_ISDIR(stat.st_mode)) {
- error_setg(errp, "share path %s is not a directory", fse->path);
- goto out;
+void virtio_init_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov,
+ unsigned int *pniov, bool is_write)
+{
+ V9fsState *s = pdu->s;
+ V9fsVirtioState *v = container_of(s, V9fsVirtioState, state);
+ VirtQueueElement *elem = v->elems[pdu->idx];
+
+ if (is_write) {
+ *piov = elem->out_sg;
+ *pniov = elem->out_num;
+ } else {
+ *piov = elem->in_sg;
+ *pniov = elem->in_num;
}
- v9fs_path_free(&path);
-
- return;
-out:
- g_free(s->ctx.fs_root);
- g_free(s->tag);
- virtio_cleanup(vdev);
- v9fs_path_free(&path);
}
/* virtio-9p device */
static Property virtio_9p_properties[] = {
- DEFINE_PROP_STRING("mount_tag", V9fsState, fsconf.tag),
- DEFINE_PROP_STRING("fsdev", V9fsState, fsconf.fsdev_id),
+ DEFINE_PROP_STRING("mount_tag", V9fsVirtioState, state.fsconf.tag),
+ DEFINE_PROP_STRING("fsdev", V9fsVirtioState, state.fsconf.fsdev_id),
DEFINE_PROP_END_OF_LIST(),
};
@@ -154,6 +191,7 @@ static void virtio_9p_class_init(ObjectClass *klass, void *data)
dc->props = virtio_9p_properties;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
vdc->realize = virtio_9p_device_realize;
+ vdc->unrealize = virtio_9p_device_unrealize;
vdc->get_features = virtio_9p_get_features;
vdc->get_config = virtio_9p_get_config;
}
@@ -161,7 +199,7 @@ static void virtio_9p_class_init(ObjectClass *klass, void *data)
static const TypeInfo virtio_device_info = {
.name = TYPE_VIRTIO_9P,
.parent = TYPE_VIRTIO_DEVICE,
- .instance_size = sizeof(V9fsState),
+ .instance_size = sizeof(V9fsVirtioState),
.class_init = virtio_9p_class_init,
};
diff --git a/qemu/hw/9pfs/virtio-9p.h b/qemu/hw/9pfs/virtio-9p.h
index 2e7d48857..7f6d88553 100644
--- a/qemu/hw/9pfs/virtio-9p.h
+++ b/qemu/hw/9pfs/virtio-9p.h
@@ -1,394 +1,31 @@
#ifndef _QEMU_VIRTIO_9P_H
#define _QEMU_VIRTIO_9P_H
-#include <sys/types.h>
-#include <dirent.h>
-#include <sys/time.h>
-#include <utime.h>
-#include <sys/resource.h>
-#include <glib.h>
#include "standard-headers/linux/virtio_9p.h"
#include "hw/virtio/virtio.h"
-#include "hw/virtio/virtio-9p.h"
-#include "fsdev/file-op-9p.h"
-#include "fsdev/virtio-9p-marshal.h"
-#include "qemu/thread.h"
-#include "block/coroutine.h"
+#include "9p.h"
-enum {
- P9_TLERROR = 6,
- P9_RLERROR,
- P9_TSTATFS = 8,
- P9_RSTATFS,
- P9_TLOPEN = 12,
- P9_RLOPEN,
- P9_TLCREATE = 14,
- P9_RLCREATE,
- P9_TSYMLINK = 16,
- P9_RSYMLINK,
- P9_TMKNOD = 18,
- P9_RMKNOD,
- P9_TRENAME = 20,
- P9_RRENAME,
- P9_TREADLINK = 22,
- P9_RREADLINK,
- P9_TGETATTR = 24,
- P9_RGETATTR,
- P9_TSETATTR = 26,
- P9_RSETATTR,
- P9_TXATTRWALK = 30,
- P9_RXATTRWALK,
- P9_TXATTRCREATE = 32,
- P9_RXATTRCREATE,
- P9_TREADDIR = 40,
- P9_RREADDIR,
- P9_TFSYNC = 50,
- P9_RFSYNC,
- P9_TLOCK = 52,
- P9_RLOCK,
- P9_TGETLOCK = 54,
- P9_RGETLOCK,
- P9_TLINK = 70,
- P9_RLINK,
- P9_TMKDIR = 72,
- P9_RMKDIR,
- P9_TRENAMEAT = 74,
- P9_RRENAMEAT,
- P9_TUNLINKAT = 76,
- P9_RUNLINKAT,
- P9_TVERSION = 100,
- P9_RVERSION,
- P9_TAUTH = 102,
- P9_RAUTH,
- P9_TATTACH = 104,
- P9_RATTACH,
- P9_TERROR = 106,
- P9_RERROR,
- P9_TFLUSH = 108,
- P9_RFLUSH,
- P9_TWALK = 110,
- P9_RWALK,
- P9_TOPEN = 112,
- P9_ROPEN,
- P9_TCREATE = 114,
- P9_RCREATE,
- P9_TREAD = 116,
- P9_RREAD,
- P9_TWRITE = 118,
- P9_RWRITE,
- P9_TCLUNK = 120,
- P9_RCLUNK,
- P9_TREMOVE = 122,
- P9_RREMOVE,
- P9_TSTAT = 124,
- P9_RSTAT,
- P9_TWSTAT = 126,
- P9_RWSTAT,
-};
-
-
-/* qid.types */
-enum {
- P9_QTDIR = 0x80,
- P9_QTAPPEND = 0x40,
- P9_QTEXCL = 0x20,
- P9_QTMOUNT = 0x10,
- P9_QTAUTH = 0x08,
- P9_QTTMP = 0x04,
- P9_QTSYMLINK = 0x02,
- P9_QTLINK = 0x01,
- P9_QTFILE = 0x00,
-};
-
-enum p9_proto_version {
- V9FS_PROTO_2000U = 0x01,
- V9FS_PROTO_2000L = 0x02,
-};
-
-#define P9_NOTAG (u16)(~0)
-#define P9_NOFID (u32)(~0)
-#define P9_MAXWELEM 16
-
-#define FID_REFERENCED 0x1
-#define FID_NON_RECLAIMABLE 0x2
-static inline char *rpath(FsContext *ctx, const char *path)
-{
- return g_strdup_printf("%s/%s", ctx->fs_root, path);
-}
-
-/*
- * ample room for Twrite/Rread header
- * size[4] Tread/Twrite tag[2] fid[4] offset[8] count[4]
- */
-#define P9_IOHDRSZ 24
-
-typedef struct V9fsPDU V9fsPDU;
-struct V9fsState;
-
-struct V9fsPDU
-{
- uint32_t size;
- uint16_t tag;
- uint8_t id;
- uint8_t cancelled;
- CoQueue complete;
- VirtQueueElement elem;
- struct V9fsState *s;
- QLIST_ENTRY(V9fsPDU) next;
-};
-
-
-/* FIXME
- * 1) change user needs to set groups and stuff
- */
-
-#define MAX_REQ 128
-#define MAX_TAG_LEN 32
-
-#define BUG_ON(cond) assert(!(cond))
-
-typedef struct V9fsFidState V9fsFidState;
-
-enum {
- P9_FID_NONE = 0,
- P9_FID_FILE,
- P9_FID_DIR,
- P9_FID_XATTR,
-};
-
-typedef struct V9fsXattr
-{
- int64_t copied_len;
- int64_t len;
- void *value;
- V9fsString name;
- int flags;
-} V9fsXattr;
-
-/*
- * Filled by fs driver on open and other
- * calls.
- */
-union V9fsFidOpenState {
- int fd;
- DIR *dir;
- V9fsXattr xattr;
- /*
- * private pointer for fs drivers, that
- * have its own internal representation of
- * open files.
- */
- void *private;
-};
-
-struct V9fsFidState
-{
- int fid_type;
- int32_t fid;
- V9fsPath path;
- V9fsFidOpenState fs;
- V9fsFidOpenState fs_reclaim;
- int flags;
- int open_flags;
- uid_t uid;
- int ref;
- int clunked;
- V9fsFidState *next;
- V9fsFidState *rclm_lst;
-};
-
-typedef struct V9fsState
+typedef struct V9fsVirtioState
{
VirtIODevice parent_obj;
VirtQueue *vq;
- V9fsPDU pdus[MAX_REQ];
- QLIST_HEAD(, V9fsPDU) free_list;
- QLIST_HEAD(, V9fsPDU) active_list;
- V9fsFidState *fid_list;
- FileOperations *ops;
- FsContext ctx;
- char *tag;
size_t config_size;
- enum p9_proto_version proto_version;
- int32_t msize;
- /*
- * lock ensuring atomic path update
- * on rename.
- */
- CoRwlock rename_lock;
- int32_t root_fid;
- Error *migration_blocker;
- V9fsConf fsconf;
-} V9fsState;
-
-typedef struct V9fsStatState {
- V9fsPDU *pdu;
- size_t offset;
- V9fsStat v9stat;
- V9fsFidState *fidp;
- struct stat stbuf;
-} V9fsStatState;
-
-typedef struct V9fsOpenState {
- V9fsPDU *pdu;
- size_t offset;
- int32_t mode;
- V9fsFidState *fidp;
- V9fsQID qid;
- struct stat stbuf;
- int iounit;
-} V9fsOpenState;
-
-typedef struct V9fsReadState {
- V9fsPDU *pdu;
- size_t offset;
- int32_t count;
- int32_t total;
- int64_t off;
- V9fsFidState *fidp;
- struct iovec iov[128]; /* FIXME: bad, bad, bad */
- struct iovec *sg;
- off_t dir_pos;
- struct dirent *dent;
- struct stat stbuf;
- V9fsString name;
- V9fsStat v9stat;
- int32_t len;
- int32_t cnt;
- int32_t max_count;
-} V9fsReadState;
-
-typedef struct V9fsWriteState {
- V9fsPDU *pdu;
- size_t offset;
- int32_t len;
- int32_t count;
- int32_t total;
- int64_t off;
- V9fsFidState *fidp;
- struct iovec iov[128]; /* FIXME: bad, bad, bad */
- struct iovec *sg;
- int cnt;
-} V9fsWriteState;
-
-typedef struct V9fsMkState {
- V9fsPDU *pdu;
- size_t offset;
- V9fsQID qid;
- struct stat stbuf;
- V9fsString name;
- V9fsString fullname;
-} V9fsMkState;
-
-/* 9p2000.L open flags */
-#define P9_DOTL_RDONLY 00000000
-#define P9_DOTL_WRONLY 00000001
-#define P9_DOTL_RDWR 00000002
-#define P9_DOTL_NOACCESS 00000003
-#define P9_DOTL_CREATE 00000100
-#define P9_DOTL_EXCL 00000200
-#define P9_DOTL_NOCTTY 00000400
-#define P9_DOTL_TRUNC 00001000
-#define P9_DOTL_APPEND 00002000
-#define P9_DOTL_NONBLOCK 00004000
-#define P9_DOTL_DSYNC 00010000
-#define P9_DOTL_FASYNC 00020000
-#define P9_DOTL_DIRECT 00040000
-#define P9_DOTL_LARGEFILE 00100000
-#define P9_DOTL_DIRECTORY 00200000
-#define P9_DOTL_NOFOLLOW 00400000
-#define P9_DOTL_NOATIME 01000000
-#define P9_DOTL_CLOEXEC 02000000
-#define P9_DOTL_SYNC 04000000
-
-/* 9p2000.L at flags */
-#define P9_DOTL_AT_REMOVEDIR 0x200
-
-/* 9P2000.L lock type */
-#define P9_LOCK_TYPE_RDLCK 0
-#define P9_LOCK_TYPE_WRLCK 1
-#define P9_LOCK_TYPE_UNLCK 2
-
-#define P9_LOCK_SUCCESS 0
-#define P9_LOCK_BLOCKED 1
-#define P9_LOCK_ERROR 2
-#define P9_LOCK_GRACE 3
-
-#define P9_LOCK_FLAGS_BLOCK 1
-#define P9_LOCK_FLAGS_RECLAIM 2
-
-typedef struct V9fsFlock
-{
- uint8_t type;
- uint32_t flags;
- uint64_t start; /* absolute offset */
- uint64_t length;
- uint32_t proc_id;
- V9fsString client_id;
-} V9fsFlock;
-
-typedef struct V9fsGetlock
-{
- uint8_t type;
- uint64_t start; /* absolute offset */
- uint64_t length;
- uint32_t proc_id;
- V9fsString client_id;
-} V9fsGetlock;
-
-extern int open_fd_hw;
-extern int total_open_fd;
-
-size_t pdu_packunpack(void *addr, struct iovec *sg, int sg_count,
- size_t offset, size_t size, int pack);
-
-static inline size_t do_pdu_unpack(void *dst, struct iovec *sg, int sg_count,
- size_t offset, size_t size)
-{
- return pdu_packunpack(dst, sg, sg_count, offset, size, 0);
-}
-
-static inline void v9fs_path_write_lock(V9fsState *s)
-{
- if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) {
- qemu_co_rwlock_wrlock(&s->rename_lock);
- }
-}
-
-static inline void v9fs_path_read_lock(V9fsState *s)
-{
- if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) {
- qemu_co_rwlock_rdlock(&s->rename_lock);
- }
-}
-
-static inline void v9fs_path_unlock(V9fsState *s)
-{
- if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) {
- qemu_co_rwlock_unlock(&s->rename_lock);
- }
-}
-
-static inline uint8_t v9fs_request_cancelled(V9fsPDU *pdu)
-{
- return pdu->cancelled;
-}
+ V9fsPDU pdus[MAX_REQ];
+ VirtQueueElement *elems[MAX_REQ];
+ V9fsState state;
+} V9fsVirtioState;
-extern void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq);
-extern void v9fs_reclaim_fd(V9fsPDU *pdu);
-extern void v9fs_path_init(V9fsPath *path);
-extern void v9fs_path_free(V9fsPath *path);
-extern void v9fs_path_copy(V9fsPath *lhs, V9fsPath *rhs);
-extern int v9fs_name_to_path(V9fsState *s, V9fsPath *dirpath,
- const char *name, V9fsPath *path);
+extern void virtio_9p_push_and_notify(V9fsPDU *pdu);
-#define pdu_marshal(pdu, offset, fmt, args...) \
- v9fs_marshal(pdu->elem.in_sg, pdu->elem.in_num, offset, 1, fmt, ##args)
-#define pdu_unmarshal(pdu, offset, fmt, args...) \
- v9fs_unmarshal(pdu->elem.out_sg, pdu->elem.out_num, offset, 1, fmt, ##args)
+ssize_t virtio_pdu_vmarshal(V9fsPDU *pdu, size_t offset,
+ const char *fmt, va_list ap);
+ssize_t virtio_pdu_vunmarshal(V9fsPDU *pdu, size_t offset,
+ const char *fmt, va_list ap);
+void virtio_init_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov,
+ unsigned int *pniov, bool is_write);
#define TYPE_VIRTIO_9P "virtio-9p-device"
#define VIRTIO_9P(obj) \
- OBJECT_CHECK(V9fsState, (obj), TYPE_VIRTIO_9P)
+ OBJECT_CHECK(V9fsVirtioState, (obj), TYPE_VIRTIO_9P)
#endif
diff --git a/qemu/hw/Makefile.objs b/qemu/hw/Makefile.objs
index 73afa41b3..4a07ed434 100644
--- a/qemu/hw/Makefile.objs
+++ b/qemu/hw/Makefile.objs
@@ -13,6 +13,7 @@ devices-dirs-$(CONFIG_SOFTMMU) += ide/
devices-dirs-$(CONFIG_SOFTMMU) += input/
devices-dirs-$(CONFIG_SOFTMMU) += intc/
devices-dirs-$(CONFIG_IPACK) += ipack/
+devices-dirs-$(CONFIG_IPMI) += ipmi/
devices-dirs-$(CONFIG_SOFTMMU) += isa/
devices-dirs-$(CONFIG_SOFTMMU) += misc/
devices-dirs-$(CONFIG_SOFTMMU) += net/
@@ -31,6 +32,7 @@ devices-dirs-$(CONFIG_VIRTIO) += virtio/
devices-dirs-$(CONFIG_SOFTMMU) += watchdog/
devices-dirs-$(CONFIG_SOFTMMU) += xen/
devices-dirs-$(CONFIG_MEM_HOTPLUG) += mem/
+devices-dirs-$(CONFIG_SMBIOS) += smbios/
devices-dirs-y += core/
common-obj-y += $(devices-dirs-y)
obj-y += $(devices-dirs-y)
diff --git a/qemu/hw/acpi/Makefile.objs b/qemu/hw/acpi/Makefile.objs
index 7d3230c2a..faee86c5c 100644
--- a/qemu/hw/acpi/Makefile.objs
+++ b/qemu/hw/acpi/Makefile.objs
@@ -1,7 +1,8 @@
common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o pcihp.o
common-obj-$(CONFIG_ACPI_X86_ICH) += ich9.o tco.o
-common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o
-common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
+common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o cpu_hotplug_acpi_table.o
+common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o memory_hotplug_acpi_table.o
+obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
common-obj-$(CONFIG_ACPI) += acpi_interface.o
common-obj-$(CONFIG_ACPI) += bios-linker-loader.o
common-obj-$(CONFIG_ACPI) += aml-build.o
diff --git a/qemu/hw/acpi/acpi_interface.c b/qemu/hw/acpi/acpi_interface.c
index c181bb226..d82131326 100644
--- a/qemu/hw/acpi/acpi_interface.c
+++ b/qemu/hw/acpi/acpi_interface.c
@@ -1,3 +1,4 @@
+#include "qemu/osdep.h"
#include "hw/acpi/acpi_dev_interface.h"
#include "qemu/module.h"
diff --git a/qemu/hw/acpi/aml-build.c b/qemu/hw/acpi/aml-build.c
index 0d4b3247b..ab89ca638 100644
--- a/qemu/hw/acpi/aml-build.c
+++ b/qemu/hw/acpi/aml-build.c
@@ -19,12 +19,8 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include <glib/gprintf.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <assert.h>
-#include <stdbool.h>
-#include <string.h>
#include "hw/acpi/aml-build.h"
#include "qemu/bswap.h"
#include "qemu/bitops.h"
@@ -262,6 +258,34 @@ static void build_append_int(GArray *table, uint64_t value)
}
}
+/*
+ * Build NAME(XXXX, 0x00000000) where 0x00000000 is encoded as a dword,
+ * and return the offset to 0x00000000 for runtime patching.
+ *
+ * Warning: runtime patching is best avoided. Only use this as
+ * a replacement for DataTableRegion (for guests that don't
+ * support it).
+ */
+int
+build_append_named_dword(GArray *array, const char *name_format, ...)
+{
+ int offset;
+ va_list ap;
+
+ build_append_byte(array, 0x08); /* NameOp */
+ va_start(ap, name_format);
+ build_append_namestringv(array, name_format, ap);
+ va_end(ap);
+
+ build_append_byte(array, 0x0C); /* DWordPrefix */
+
+ offset = array->len;
+ build_append_int_noprefix(array, 0x00000000, 4);
+ assert(array->len == offset + 4);
+
+ return offset;
+}
+
static GPtrArray *alloc_list;
static Aml *aml_alloc(void)
@@ -427,6 +451,41 @@ Aml *aml_arg(int pos)
return var;
}
+/* ACPI 2.0a: 17.2.4.4 Type 2 Opcodes Encoding: DefToInteger */
+Aml *aml_to_integer(Aml *arg)
+{
+ Aml *var = aml_opcode(0x99 /* ToIntegerOp */);
+ aml_append(var, arg);
+ build_append_byte(var->buf, 0x00 /* NullNameOp */);
+ return var;
+}
+
+/* ACPI 2.0a: 17.2.4.4 Type 2 Opcodes Encoding: DefToHexString */
+Aml *aml_to_hexstring(Aml *src, Aml *dst)
+{
+ Aml *var = aml_opcode(0x98 /* ToHexStringOp */);
+ aml_append(var, src);
+ if (dst) {
+ aml_append(var, dst);
+ } else {
+ build_append_byte(var->buf, 0x00 /* NullNameOp */);
+ }
+ return var;
+}
+
+/* ACPI 2.0a: 17.2.4.4 Type 2 Opcodes Encoding: DefToBuffer */
+Aml *aml_to_buffer(Aml *src, Aml *dst)
+{
+ Aml *var = aml_opcode(0x96 /* ToBufferOp */);
+ aml_append(var, src);
+ if (dst) {
+ aml_append(var, dst);
+ } else {
+ build_append_byte(var->buf, 0x00 /* NullNameOp */);
+ }
+ return var;
+}
+
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefStore */
Aml *aml_store(Aml *val, Aml *target)
{
@@ -436,44 +495,64 @@ Aml *aml_store(Aml *val, Aml *target)
return var;
}
-/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAnd */
-Aml *aml_and(Aml *arg1, Aml *arg2)
+/**
+ * build_opcode_2arg_dst:
+ * @op: 1-byte opcode
+ * @arg1: 1st operand
+ * @arg2: 2nd operand
+ * @dst: optional target to store to, set to NULL if it's not required
+ *
+ * An internal helper to compose AML terms that have
+ * "Op Operand Operand Target"
+ * pattern.
+ *
+ * Returns: The newly allocated and composed according to patter Aml object.
+ */
+static Aml *
+build_opcode_2arg_dst(uint8_t op, Aml *arg1, Aml *arg2, Aml *dst)
{
- Aml *var = aml_opcode(0x7B /* AndOp */);
+ Aml *var = aml_opcode(op);
aml_append(var, arg1);
aml_append(var, arg2);
- build_append_byte(var->buf, 0x00 /* NullNameOp */);
+ if (dst) {
+ aml_append(var, dst);
+ } else {
+ build_append_byte(var->buf, 0x00 /* NullNameOp */);
+ }
return var;
}
+/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAnd */
+Aml *aml_and(Aml *arg1, Aml *arg2, Aml *dst)
+{
+ return build_opcode_2arg_dst(0x7B /* AndOp */, arg1, arg2, dst);
+}
+
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefOr */
-Aml *aml_or(Aml *arg1, Aml *arg2)
+Aml *aml_or(Aml *arg1, Aml *arg2, Aml *dst)
{
- Aml *var = aml_opcode(0x7D /* OrOp */);
+ return build_opcode_2arg_dst(0x7D /* OrOp */, arg1, arg2, dst);
+}
+
+/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLOr */
+Aml *aml_lor(Aml *arg1, Aml *arg2)
+{
+ Aml *var = aml_opcode(0x91 /* LOrOp */);
aml_append(var, arg1);
aml_append(var, arg2);
- build_append_byte(var->buf, 0x00 /* NullNameOp */);
return var;
}
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefShiftLeft */
Aml *aml_shiftleft(Aml *arg1, Aml *count)
{
- Aml *var = aml_opcode(0x79 /* ShiftLeftOp */);
- aml_append(var, arg1);
- aml_append(var, count);
- build_append_byte(var->buf, 0x00); /* NullNameOp */
- return var;
+ return build_opcode_2arg_dst(0x79 /* ShiftLeftOp */, arg1, count, NULL);
}
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefShiftRight */
-Aml *aml_shiftright(Aml *arg1, Aml *count)
+Aml *aml_shiftright(Aml *arg1, Aml *count, Aml *dst)
{
- Aml *var = aml_opcode(0x7A /* ShiftRightOp */);
- aml_append(var, arg1);
- aml_append(var, count);
- build_append_byte(var->buf, 0x00); /* NullNameOp */
- return var;
+ return build_opcode_2arg_dst(0x7A /* ShiftRightOp */, arg1, count, dst);
}
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLLess */
@@ -486,13 +565,15 @@ Aml *aml_lless(Aml *arg1, Aml *arg2)
}
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAdd */
-Aml *aml_add(Aml *arg1, Aml *arg2)
+Aml *aml_add(Aml *arg1, Aml *arg2, Aml *dst)
{
- Aml *var = aml_opcode(0x72 /* AddOp */);
- aml_append(var, arg1);
- aml_append(var, arg2);
- build_append_byte(var->buf, 0x00 /* NullNameOp */);
- return var;
+ return build_opcode_2arg_dst(0x72 /* AddOp */, arg1, arg2, dst);
+}
+
+/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefSubtract */
+Aml *aml_subtract(Aml *arg1, Aml *arg2, Aml *dst)
+{
+ return build_opcode_2arg_dst(0x74 /* SubtractOp */, arg1, arg2, dst);
}
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefIncrement */
@@ -503,14 +584,18 @@ Aml *aml_increment(Aml *arg)
return var;
}
+/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefDecrement */
+Aml *aml_decrement(Aml *arg)
+{
+ Aml *var = aml_opcode(0x76 /* DecrementOp */);
+ aml_append(var, arg);
+ return var;
+}
+
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefIndex */
Aml *aml_index(Aml *arg1, Aml *idx)
{
- Aml *var = aml_opcode(0x88 /* IndexOp */);
- aml_append(var, arg1);
- aml_append(var, idx);
- build_append_byte(var->buf, 0x00 /* NullNameOp */);
- return var;
+ return build_opcode_2arg_dst(0x88 /* IndexOp */, arg1, idx, NULL);
}
/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefNotify */
@@ -523,6 +608,14 @@ Aml *aml_notify(Aml *arg1, Aml *arg2)
}
/* helper to call method with 1 argument */
+Aml *aml_call0(const char *method)
+{
+ Aml *var = aml_alloc();
+ build_append_namestring(var->buf, "%s", method);
+ return var;
+}
+
+/* helper to call method with 1 argument */
Aml *aml_call1(const char *method, Aml *arg1)
{
Aml *var = aml_alloc();
@@ -565,6 +658,94 @@ Aml *aml_call4(const char *method, Aml *arg1, Aml *arg2, Aml *arg3, Aml *arg4)
}
/*
+ * ACPI 5.0: 6.4.3.8.1 GPIO Connection Descriptor
+ * Type 1, Large Item Name 0xC
+ */
+
+static Aml *aml_gpio_connection(AmlGpioConnectionType type,
+ AmlConsumerAndProducer con_and_pro,
+ uint8_t flags, AmlPinConfig pin_config,
+ uint16_t output_drive,
+ uint16_t debounce_timeout,
+ const uint32_t pin_list[], uint32_t pin_count,
+ const char *resource_source_name,
+ const uint8_t *vendor_data,
+ uint16_t vendor_data_len)
+{
+ Aml *var = aml_alloc();
+ const uint16_t min_desc_len = 0x16;
+ uint16_t resource_source_name_len, length;
+ uint16_t pin_table_offset, resource_source_name_offset, vendor_data_offset;
+ uint32_t i;
+
+ assert(resource_source_name);
+ resource_source_name_len = strlen(resource_source_name) + 1;
+ length = min_desc_len + resource_source_name_len + vendor_data_len;
+ pin_table_offset = min_desc_len + 1;
+ resource_source_name_offset = pin_table_offset + pin_count * 2;
+ vendor_data_offset = resource_source_name_offset + resource_source_name_len;
+
+ build_append_byte(var->buf, 0x8C); /* GPIO Connection Descriptor */
+ build_append_int_noprefix(var->buf, length, 2); /* Length */
+ build_append_byte(var->buf, 1); /* Revision ID */
+ build_append_byte(var->buf, type); /* GPIO Connection Type */
+ /* General Flags (2 bytes) */
+ build_append_int_noprefix(var->buf, con_and_pro, 2);
+ /* Interrupt and IO Flags (2 bytes) */
+ build_append_int_noprefix(var->buf, flags, 2);
+ /* Pin Configuration 0 = Default 1 = Pull-up 2 = Pull-down 3 = No Pull */
+ build_append_byte(var->buf, pin_config);
+ /* Output Drive Strength (2 bytes) */
+ build_append_int_noprefix(var->buf, output_drive, 2);
+ /* Debounce Timeout (2 bytes) */
+ build_append_int_noprefix(var->buf, debounce_timeout, 2);
+ /* Pin Table Offset (2 bytes) */
+ build_append_int_noprefix(var->buf, pin_table_offset, 2);
+ build_append_byte(var->buf, 0); /* Resource Source Index */
+ /* Resource Source Name Offset (2 bytes) */
+ build_append_int_noprefix(var->buf, resource_source_name_offset, 2);
+ /* Vendor Data Offset (2 bytes) */
+ build_append_int_noprefix(var->buf, vendor_data_offset, 2);
+ /* Vendor Data Length (2 bytes) */
+ build_append_int_noprefix(var->buf, vendor_data_len, 2);
+ /* Pin Number (2n bytes)*/
+ for (i = 0; i < pin_count; i++) {
+ build_append_int_noprefix(var->buf, pin_list[i], 2);
+ }
+
+ /* Resource Source Name */
+ build_append_namestring(var->buf, "%s", resource_source_name);
+ build_append_byte(var->buf, '\0');
+
+ /* Vendor-defined Data */
+ if (vendor_data != NULL) {
+ g_array_append_vals(var->buf, vendor_data, vendor_data_len);
+ }
+
+ return var;
+}
+
+/*
+ * ACPI 5.0: 19.5.53
+ * GpioInt(GPIO Interrupt Connection Resource Descriptor Macro)
+ */
+Aml *aml_gpio_int(AmlConsumerAndProducer con_and_pro,
+ AmlLevelAndEdge edge_level,
+ AmlActiveHighAndLow active_level, AmlShared shared,
+ AmlPinConfig pin_config, uint16_t debounce_timeout,
+ const uint32_t pin_list[], uint32_t pin_count,
+ const char *resource_source_name,
+ const uint8_t *vendor_data, uint16_t vendor_data_len)
+{
+ uint8_t flags = edge_level | (active_level << 1) | (shared << 3);
+
+ return aml_gpio_connection(AML_INTERRUPT_CONNECTION, con_and_pro, flags,
+ pin_config, 0, debounce_timeout, pin_list,
+ pin_count, resource_source_name, vendor_data,
+ vendor_data_len);
+}
+
+/*
* ACPI 1.0b: 6.4.3.4 32-Bit Fixed Location Memory Range Descriptor
* (Type 1, Large Item Name 0x6)
*/
@@ -598,23 +779,27 @@ Aml *aml_memory32_fixed(uint32_t addr, uint32_t size,
Aml *aml_interrupt(AmlConsumerAndProducer con_and_pro,
AmlLevelAndEdge level_and_edge,
AmlActiveHighAndLow high_and_low, AmlShared shared,
- uint32_t irq)
+ uint32_t *irq_list, uint8_t irq_count)
{
+ int i;
Aml *var = aml_alloc();
uint8_t irq_flags = con_and_pro | (level_and_edge << 1)
| (high_and_low << 2) | (shared << 3);
+ const int header_bytes_in_len = 2;
+ uint16_t len = header_bytes_in_len + irq_count * sizeof(uint32_t);
+
+ assert(irq_count > 0);
build_append_byte(var->buf, 0x89); /* Extended irq descriptor */
- build_append_byte(var->buf, 6); /* Length, bits[7:0] minimum value = 6 */
- build_append_byte(var->buf, 0); /* Length, bits[15:8] minimum value = 0 */
+ build_append_byte(var->buf, len & 0xFF); /* Length, bits[7:0] */
+ build_append_byte(var->buf, len >> 8); /* Length, bits[15:8] */
build_append_byte(var->buf, irq_flags); /* Interrupt Vector Information. */
- build_append_byte(var->buf, 0x01); /* Interrupt table length = 1 */
+ build_append_byte(var->buf, irq_count); /* Interrupt table length */
- /* Interrupt Number */
- build_append_byte(var->buf, extract32(irq, 0, 8)); /* bits[7:0] */
- build_append_byte(var->buf, extract32(irq, 8, 8)); /* bits[15:8] */
- build_append_byte(var->buf, extract32(irq, 16, 8)); /* bits[23:16] */
- build_append_byte(var->buf, extract32(irq, 24, 8)); /* bits[31:24] */
+ /* Interrupt Number List */
+ for (i = 0; i < irq_count; i++) {
+ build_append_int_noprefix(var->buf, irq_list[i], 4);
+ }
return var;
}
@@ -672,6 +857,26 @@ Aml *aml_equal(Aml *arg1, Aml *arg2)
return var;
}
+/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLGreater */
+Aml *aml_lgreater(Aml *arg1, Aml *arg2)
+{
+ Aml *var = aml_opcode(0x94 /* LGreaterOp */);
+ aml_append(var, arg1);
+ aml_append(var, arg2);
+ return var;
+}
+
+/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLGreaterEqual */
+Aml *aml_lgreater_equal(Aml *arg1, Aml *arg2)
+{
+ /* LGreaterEqualOp := LNotOp LLessOp */
+ Aml *var = aml_opcode(0x92 /* LNotOp */);
+ build_append_byte(var->buf, 0x95 /* LLessOp */);
+ aml_append(var, arg1);
+ aml_append(var, arg2);
+ return var;
+}
+
/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefIfElse */
Aml *aml_if(Aml *predicate)
{
@@ -696,11 +901,24 @@ Aml *aml_while(Aml *predicate)
}
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefMethod */
-Aml *aml_method(const char *name, int arg_count)
+Aml *aml_method(const char *name, int arg_count, AmlSerializeFlag sflag)
{
Aml *var = aml_bundle(0x14 /* MethodOp */, AML_PACKAGE);
+ int methodflags;
+
+ /*
+ * MethodFlags:
+ * bit 0-2: ArgCount (0-7)
+ * bit 3: SerializeFlag
+ * 0: NotSerialized
+ * 1: Serialized
+ * bit 4-7: reserved (must be 0)
+ */
+ assert(arg_count < 8);
+ methodflags = arg_count | (sflag << 3);
+
build_append_namestring(var->buf, "%s", name);
- build_append_byte(var->buf, arg_count); /* MethodFlags: ArgCount */
+ build_append_byte(var->buf, methodflags); /* MethodFlags: ArgCount */
return var;
}
@@ -752,14 +970,14 @@ Aml *aml_package(uint8_t num_elements)
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefOpRegion */
Aml *aml_operation_region(const char *name, AmlRegionSpace rs,
- uint32_t offset, uint32_t len)
+ Aml *offset, uint32_t len)
{
Aml *var = aml_alloc();
build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */
build_append_byte(var->buf, 0x80); /* OpRegionOp */
build_append_namestring(var->buf, "%s", name);
build_append_byte(var->buf, rs);
- build_append_int(var->buf, offset);
+ aml_append(var, offset);
build_append_int(var->buf, len);
return var;
}
@@ -784,27 +1002,57 @@ Aml *aml_reserved_field(unsigned length)
}
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefField */
-Aml *aml_field(const char *name, AmlAccessType type, AmlUpdateRule rule)
+Aml *aml_field(const char *name, AmlAccessType type, AmlLockRule lock,
+ AmlUpdateRule rule)
{
Aml *var = aml_bundle(0x81 /* FieldOp */, AML_EXT_PACKAGE);
uint8_t flags = rule << 5 | type;
+ flags |= lock << 4; /* LockRule at 4 bit offset */
+
build_append_namestring(var->buf, "%s", name);
build_append_byte(var->buf, flags);
return var;
}
-/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefCreateDWordField */
-Aml *aml_create_dword_field(Aml *srcbuf, Aml *index, const char *name)
+static
+Aml *create_field_common(int opcode, Aml *srcbuf, Aml *index, const char *name)
{
- Aml *var = aml_alloc();
- build_append_byte(var->buf, 0x8A); /* CreateDWordFieldOp */
+ Aml *var = aml_opcode(opcode);
aml_append(var, srcbuf);
aml_append(var, index);
build_append_namestring(var->buf, "%s", name);
return var;
}
+/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefCreateField */
+Aml *aml_create_field(Aml *srcbuf, Aml *bit_index, Aml *num_bits,
+ const char *name)
+{
+ Aml *var = aml_alloc();
+ build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */
+ build_append_byte(var->buf, 0x13); /* CreateFieldOp */
+ aml_append(var, srcbuf);
+ aml_append(var, bit_index);
+ aml_append(var, num_bits);
+ build_append_namestring(var->buf, "%s", name);
+ return var;
+}
+
+/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefCreateDWordField */
+Aml *aml_create_dword_field(Aml *srcbuf, Aml *index, const char *name)
+{
+ return create_field_common(0x8A /* CreateDWordFieldOp */,
+ srcbuf, index, name);
+}
+
+/* ACPI 2.0a: 17.2.4.2 Named Objects Encoding: DefCreateQWordField */
+Aml *aml_create_qword_field(Aml *srcbuf, Aml *index, const char *name)
+{
+ return create_field_common(0x8F /* CreateQWordFieldOp */,
+ srcbuf, index, name);
+}
+
/* ACPI 1.0b: 16.2.3 Data Objects Encoding: String */
Aml *aml_string(const char *name_format, ...)
{
@@ -1065,6 +1313,30 @@ Aml *aml_qword_memory(AmlDecode dec, AmlMinFixed min_fixed,
addr_trans, len, flags);
}
+/* ACPI 1.0b: 6.4.2.2 DMA Format/6.4.2.2.1 ASL Macro for DMA Descriptor */
+Aml *aml_dma(AmlDmaType typ, AmlDmaBusMaster bm, AmlTransferSize sz,
+ uint8_t channel)
+{
+ Aml *var = aml_alloc();
+ uint8_t flags = sz | bm << 2 | typ << 5;
+
+ assert(channel < 8);
+ build_append_byte(var->buf, 0x2A); /* Byte 0: DMA Descriptor */
+ build_append_byte(var->buf, 1U << channel); /* Byte 1: _DMA - DmaChannel */
+ build_append_byte(var->buf, flags); /* Byte 2 */
+ return var;
+}
+
+/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefSleep */
+Aml *aml_sleep(uint64_t msec)
+{
+ Aml *var = aml_alloc();
+ build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */
+ build_append_byte(var->buf, 0x22); /* SleepOp */
+ aml_append(var, aml_int(msec));
+ return var;
+}
+
static uint8_t Hex2Byte(const char *src)
{
int hi, lo;
@@ -1135,23 +1407,100 @@ Aml *aml_unicode(const char *str)
return var;
}
+/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefDerefOf */
+Aml *aml_derefof(Aml *arg)
+{
+ Aml *var = aml_opcode(0x83 /* DerefOfOp */);
+ aml_append(var, arg);
+ return var;
+}
+
+/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefSizeOf */
+Aml *aml_sizeof(Aml *arg)
+{
+ Aml *var = aml_opcode(0x87 /* SizeOfOp */);
+ aml_append(var, arg);
+ return var;
+}
+
+/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefMutex */
+Aml *aml_mutex(const char *name, uint8_t sync_level)
+{
+ Aml *var = aml_alloc();
+ build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */
+ build_append_byte(var->buf, 0x01); /* MutexOp */
+ build_append_namestring(var->buf, "%s", name);
+ assert(!(sync_level & 0xF0));
+ build_append_byte(var->buf, sync_level);
+ return var;
+}
+
+/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAcquire */
+Aml *aml_acquire(Aml *mutex, uint16_t timeout)
+{
+ Aml *var = aml_alloc();
+ build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */
+ build_append_byte(var->buf, 0x23); /* AcquireOp */
+ aml_append(var, mutex);
+ build_append_int_noprefix(var->buf, timeout, sizeof(timeout));
+ return var;
+}
+
+/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefRelease */
+Aml *aml_release(Aml *mutex)
+{
+ Aml *var = aml_alloc();
+ build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */
+ build_append_byte(var->buf, 0x27); /* ReleaseOp */
+ aml_append(var, mutex);
+ return var;
+}
+
+/* ACPI 1.0b: 16.2.5.1 Name Space Modifier Objects Encoding: DefAlias */
+Aml *aml_alias(const char *source_object, const char *alias_object)
+{
+ Aml *var = aml_opcode(0x06 /* AliasOp */);
+ aml_append(var, aml_name("%s", source_object));
+ aml_append(var, aml_name("%s", alias_object));
+ return var;
+}
+
+/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefConcat */
+Aml *aml_concatenate(Aml *source1, Aml *source2, Aml *target)
+{
+ return build_opcode_2arg_dst(0x73 /* ConcatOp */, source1, source2,
+ target);
+}
+
void
build_header(GArray *linker, GArray *table_data,
- AcpiTableHeader *h, const char *sig, int len, uint8_t rev)
+ AcpiTableHeader *h, const char *sig, int len, uint8_t rev,
+ const char *oem_id, const char *oem_table_id)
{
memcpy(&h->signature, sig, 4);
h->length = cpu_to_le32(len);
h->revision = rev;
- memcpy(h->oem_id, ACPI_BUILD_APPNAME6, 6);
- memcpy(h->oem_table_id, ACPI_BUILD_APPNAME4, 4);
- memcpy(h->oem_table_id + 4, sig, 4);
+
+ if (oem_id) {
+ strncpy((char *)h->oem_id, oem_id, sizeof h->oem_id);
+ } else {
+ memcpy(h->oem_id, ACPI_BUILD_APPNAME6, 6);
+ }
+
+ if (oem_table_id) {
+ strncpy((char *)h->oem_table_id, oem_table_id, sizeof(h->oem_table_id));
+ } else {
+ memcpy(h->oem_table_id, ACPI_BUILD_APPNAME4, 4);
+ memcpy(h->oem_table_id + 4, sig, 4);
+ }
+
h->oem_revision = cpu_to_le32(1);
memcpy(h->asl_compiler_id, ACPI_BUILD_APPNAME4, 4);
h->asl_compiler_revision = cpu_to_le32(1);
h->checksum = 0;
/* Checksum to be filled in by Guest linker */
bios_linker_loader_add_checksum(linker, ACPI_BUILD_TABLE_FILE,
- table_data->data, h, len, &h->checksum);
+ table_data, h, len, &h->checksum);
}
void *acpi_data_push(GArray *table_data, unsigned size)
@@ -1163,9 +1512,7 @@ void *acpi_data_push(GArray *table_data, unsigned size)
unsigned acpi_data_len(GArray *table)
{
-#if GLIB_CHECK_VERSION(2, 22, 0)
assert(g_array_get_element_size(table) == 1);
-#endif
return table->len;
}
@@ -1194,7 +1541,8 @@ void acpi_build_tables_cleanup(AcpiBuildTables *tables, bool mfre)
/* Build rsdt table */
void
-build_rsdt(GArray *table_data, GArray *linker, GArray *table_offsets)
+build_rsdt(GArray *table_data, GArray *linker, GArray *table_offsets,
+ const char *oem_id, const char *oem_table_id)
{
AcpiRsdtDescriptorRev1 *rsdt;
size_t rsdt_len;
@@ -1213,5 +1561,5 @@ build_rsdt(GArray *table_data, GArray *linker, GArray *table_offsets)
sizeof(uint32_t));
}
build_header(linker, table_data,
- (void *)rsdt, "RSDT", rsdt_len, 1);
+ (void *)rsdt, "RSDT", rsdt_len, 1, oem_id, oem_table_id);
}
diff --git a/qemu/hw/acpi/bios-linker-loader.c b/qemu/hw/acpi/bios-linker-loader.c
index d9382f826..5153ab151 100644
--- a/qemu/hw/acpi/bios-linker-loader.c
+++ b/qemu/hw/acpi/bios-linker-loader.c
@@ -18,12 +18,20 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "hw/acpi/bios-linker-loader.h"
#include "hw/nvram/fw_cfg.h"
#include "qemu/bswap.h"
+/*
+ * Linker/loader is a paravirtualized interface that passes commands to guest.
+ * The commands can be used to request guest to
+ * - allocate memory chunks and initialize them from QEMU FW CFG files
+ * - link allocated chunks by storing pointer to one chunk into another
+ * - calculate ACPI checksum of part of the chunk and store into same chunk
+ */
#define BIOS_LINKER_LOADER_FILESZ FW_CFG_MAX_FILE_PATH
struct BiosLinkerLoaderEntry {
@@ -87,6 +95,12 @@ enum {
BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG = 0x2,
};
+/*
+ * bios_linker_loader_init: allocate a new linker file blob array.
+ *
+ * After initialization, linker commands can be added, and will
+ * be stored in the array.
+ */
GArray *bios_linker_loader_init(void)
{
return g_array_new(false, true /* clear */, 1);
@@ -98,6 +112,16 @@ void *bios_linker_loader_cleanup(GArray *linker)
return g_array_free(linker, false);
}
+/*
+ * bios_linker_loader_alloc: ask guest to load file into guest memory.
+ *
+ * @linker: linker file blob array
+ * @file: file to be loaded
+ * @alloc_align: required minimal alignment in bytes. Must be a power of 2.
+ * @alloc_fseg: request allocation in FSEG zone (useful for the RSDP ACPI table)
+ *
+ * Note: this command must precede any other linker command using this file.
+ */
void bios_linker_loader_alloc(GArray *linker,
const char *file,
uint32_t alloc_align,
@@ -105,35 +129,90 @@ void bios_linker_loader_alloc(GArray *linker,
{
BiosLinkerLoaderEntry entry;
+ assert(!(alloc_align & (alloc_align - 1)));
+
memset(&entry, 0, sizeof entry);
strncpy(entry.alloc.file, file, sizeof entry.alloc.file - 1);
entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ALLOCATE);
entry.alloc.align = cpu_to_le32(alloc_align);
- entry.alloc.zone = cpu_to_le32(alloc_fseg ?
- BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG :
- BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH);
+ entry.alloc.zone = alloc_fseg ? BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG :
+ BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH;
/* Alloc entries must come first, so prepend them */
g_array_prepend_vals(linker, &entry, sizeof entry);
}
+/*
+ * bios_linker_loader_add_checksum: ask guest to add checksum of file data
+ * into (same) file at the specified pointer.
+ *
+ * Checksum calculation simply sums -X for each byte X in the range
+ * using 8-bit math (i.e. ACPI checksum).
+ *
+ * @linker: linker file blob array
+ * @file: file that includes the checksum to be calculated
+ * and the data to be checksummed
+ * @table: @file blob contents
+ * @start, @size: range of data to checksum
+ * @checksum: location of the checksum to be patched within file blob
+ *
+ * Notes:
+ * - checksum byte initial value must have been pushed into @table
+ * and reside at address @checksum.
+ * - @size bytes must have been pushed into @table and reside at address
+ * @start.
+ * - Guest calculates checksum of specified range of data, result is added to
+ * initial value at @checksum into copy of @file in Guest memory.
+ * - Range might include the checksum itself.
+ * - To avoid confusion, caller must always put 0x0 at @checksum.
+ * - @file must be loaded into Guest memory using bios_linker_loader_alloc
+ */
void bios_linker_loader_add_checksum(GArray *linker, const char *file,
- void *table,
+ GArray *table,
void *start, unsigned size,
uint8_t *checksum)
{
BiosLinkerLoaderEntry entry;
+ ptrdiff_t checksum_offset = (gchar *)checksum - table->data;
+ ptrdiff_t start_offset = (gchar *)start - table->data;
+
+ assert(checksum_offset >= 0);
+ assert(start_offset >= 0);
+ assert(checksum_offset + 1 <= table->len);
+ assert(start_offset + size <= table->len);
+ assert(*checksum == 0x0);
memset(&entry, 0, sizeof entry);
strncpy(entry.cksum.file, file, sizeof entry.cksum.file - 1);
entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM);
- entry.cksum.offset = cpu_to_le32(checksum - (uint8_t *)table);
- entry.cksum.start = cpu_to_le32((uint8_t *)start - (uint8_t *)table);
+ entry.cksum.offset = cpu_to_le32(checksum_offset);
+ entry.cksum.start = cpu_to_le32(start_offset);
entry.cksum.length = cpu_to_le32(size);
g_array_append_vals(linker, &entry, sizeof entry);
}
+/*
+ * bios_linker_loader_add_pointer: ask guest to add address of source file
+ * into destination file at the specified pointer.
+ *
+ * @linker: linker file blob array
+ * @dest_file: destination file that must be changed
+ * @src_file: source file who's address must be taken
+ * @table: @dest_file blob contents array
+ * @pointer: location of the pointer to be patched within destination file blob
+ * @pointer_size: size of pointer to be patched, in bytes
+ *
+ * Notes:
+ * - @pointer_size bytes must have been pushed into @table
+ * and reside at address @pointer.
+ * - Guest address is added to initial value at @pointer
+ * into copy of @dest_file in Guest memory.
+ * e.g. to get start of src_file in guest memory, put 0x0 there
+ * to get address of a field at offset 0x10 in src_file, put 0x10 there
+ * - Both @dest_file and @src_file must be
+ * loaded into Guest memory using bios_linker_loader_alloc
+ */
void bios_linker_loader_add_pointer(GArray *linker,
const char *dest_file,
const char *src_file,
@@ -141,7 +220,10 @@ void bios_linker_loader_add_pointer(GArray *linker,
uint8_t pointer_size)
{
BiosLinkerLoaderEntry entry;
- size_t offset = (gchar *)pointer - table->data;
+ ptrdiff_t offset = (gchar *)pointer - table->data;
+
+ assert(offset >= 0);
+ assert(offset + pointer_size <= table->len);
memset(&entry, 0, sizeof entry);
strncpy(entry.pointer.dest_file, dest_file,
@@ -149,7 +231,6 @@ void bios_linker_loader_add_pointer(GArray *linker,
strncpy(entry.pointer.src_file, src_file,
sizeof entry.pointer.src_file - 1);
entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ADD_POINTER);
- assert(table->len >= offset + pointer_size);
entry.pointer.offset = cpu_to_le32(offset);
entry.pointer.size = pointer_size;
assert(pointer_size == 1 || pointer_size == 2 ||
diff --git a/qemu/hw/acpi/core.c b/qemu/hw/acpi/core.c
index fe6215af4..6a2f45214 100644
--- a/qemu/hw/acpi/core.c
+++ b/qemu/hw/acpi/core.c
@@ -18,6 +18,7 @@
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "sysemu/sysemu.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
@@ -25,7 +26,6 @@
#include "hw/nvram/fw_cfg.h"
#include "qemu/config-file.h"
#include "qapi/opts-visitor.h"
-#include "qapi/dealloc-visitor.h"
#include "qapi-visit.h"
#include "qapi-event.h"
@@ -67,7 +67,7 @@ static void acpi_register_config(void)
qemu_add_opts(&qemu_acpi_opts);
}
-machine_init(acpi_register_config);
+opts_init(acpi_register_config);
static int acpi_checksum(const uint8_t *data, int len)
{
@@ -242,7 +242,7 @@ void acpi_table_add(const QemuOpts *opts, Error **errp)
OptsVisitor *ov;
ov = opts_visitor_new(opts);
- visit_type_AcpiTableOptions(opts_get_visitor(ov), &hdrs, NULL, &err);
+ visit_type_AcpiTableOptions(opts_get_visitor(ov), NULL, &hdrs, &err);
opts_visitor_cleanup(ov);
}
@@ -296,15 +296,7 @@ void acpi_table_add(const QemuOpts *opts, Error **errp)
out:
g_free(blob);
g_strfreev(pathnames);
-
- if (hdrs != NULL) {
- QapiDeallocVisitor *dv;
-
- dv = qapi_dealloc_visitor_new();
- visit_type_AcpiTableOptions(qapi_dealloc_get_visitor(dv), &hdrs, NULL,
- NULL);
- qapi_dealloc_visitor_cleanup(dv);
- }
+ qapi_free_AcpiTableOptions(hdrs);
error_propagate(errp, err);
}
@@ -349,6 +341,22 @@ uint8_t *acpi_table_next(uint8_t *current)
}
}
+int acpi_get_slic_oem(AcpiSlicOem *oem)
+{
+ uint8_t *u;
+
+ for (u = acpi_table_first(); u; u = acpi_table_next(u)) {
+ struct acpi_table_header *hdr = (void *)(u - sizeof(hdr->_length));
+
+ if (memcmp(hdr->sig, "SLIC", 4) == 0) {
+ oem->id = hdr->oem_id;
+ oem->table_id = hdr->oem_table_id;
+ return 0;
+ }
+ }
+ return -1;
+}
+
static void acpi_notify_wakeup(Notifier *notifier, void *data)
{
ACPIREGS *ar = container_of(notifier, ACPIREGS, wakeup);
@@ -381,7 +389,7 @@ uint16_t acpi_pm1_evt_get_sts(ACPIREGS *ar)
acpi_pm_tmr_update function uses ns for setting the timer. */
int64_t d = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
if (d >= muldiv64(ar->tmr.overflow_time,
- get_ticks_per_sec(), PM_TIMER_FREQUENCY)) {
+ NANOSECONDS_PER_SECOND, PM_TIMER_FREQUENCY)) {
ar->pm1.evt.sts |= ACPI_BITMASK_TIMER_STATUS;
}
return ar->pm1.evt.sts;
@@ -475,7 +483,7 @@ void acpi_pm_tmr_update(ACPIREGS *ar, bool enable)
/* schedule a timer interruption if needed */
if (enable) {
- expire_time = muldiv64(ar->tmr.overflow_time, get_ticks_per_sec(),
+ expire_time = muldiv64(ar->tmr.overflow_time, NANOSECONDS_PER_SECOND,
PM_TIMER_FREQUENCY);
timer_mod(ar->tmr.timer, expire_time);
} else {
@@ -528,7 +536,6 @@ void acpi_pm_tmr_init(ACPIREGS *ar, acpi_update_sci_fn update_sci,
ar->tmr.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, acpi_pm_tmr_timer, ar);
memory_region_init_io(&ar->tmr.io, memory_region_owner(parent),
&acpi_pm_tmr_ops, ar, "acpi-tmr", 4);
- memory_region_clear_global_locking(&ar->tmr.io);
memory_region_add_subregion(parent, 8, &ar->tmr.io);
}
@@ -625,8 +632,12 @@ void acpi_pm1_cnt_reset(ACPIREGS *ar)
void acpi_gpe_init(ACPIREGS *ar, uint8_t len)
{
ar->gpe.len = len;
- ar->gpe.sts = g_malloc0(len / 2);
- ar->gpe.en = g_malloc0(len / 2);
+ /* Only first len / 2 bytes are ever used,
+ * but the caller in ich9.c migrates full len bytes.
+ * TODO: fix ich9.c and drop the extra allocation.
+ */
+ ar->gpe.sts = g_malloc0(len);
+ ar->gpe.en = g_malloc0(len);
}
void acpi_gpe_reset(ACPIREGS *ar)
diff --git a/qemu/hw/acpi/cpu_hotplug.c b/qemu/hw/acpi/cpu_hotplug.c
index f5b9972f2..4d86743fd 100644
--- a/qemu/hw/acpi/cpu_hotplug.c
+++ b/qemu/hw/acpi/cpu_hotplug.c
@@ -9,8 +9,11 @@
* 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 "hw/hw.h"
#include "hw/acpi/cpu_hotplug.h"
+#include "qapi/error.h"
+#include "qom/cpu.h"
static uint64_t cpu_status_read(void *opaque, hwaddr addr, unsigned int size)
{
diff --git a/qemu/hw/acpi/cpu_hotplug_acpi_table.c b/qemu/hw/acpi/cpu_hotplug_acpi_table.c
new file mode 100644
index 000000000..97bb1092a
--- /dev/null
+++ b/qemu/hw/acpi/cpu_hotplug_acpi_table.c
@@ -0,0 +1,136 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/acpi/cpu_hotplug.h"
+
+void build_cpu_hotplug_aml(Aml *ctx)
+{
+ Aml *method;
+ Aml *if_ctx;
+ Aml *else_ctx;
+ Aml *sb_scope = aml_scope("_SB");
+ uint8_t madt_tmpl[8] = {0x00, 0x08, 0x00, 0x00, 0x00, 0, 0, 0};
+ Aml *cpu_id = aml_arg(0);
+ Aml *cpu_on = aml_local(0);
+ Aml *madt = aml_local(1);
+ Aml *cpus_map = aml_name(CPU_ON_BITMAP);
+ Aml *zero = aml_int(0);
+ Aml *one = aml_int(1);
+
+ /*
+ * _MAT method - creates an madt apic buffer
+ * cpu_id = Arg0 = Processor ID = Local APIC ID
+ * cpu_on = Local0 = CPON flag for this cpu
+ * madt = Local1 = Buffer (in madt apic form) to return
+ */
+ method = aml_method(CPU_MAT_METHOD, 1, AML_NOTSERIALIZED);
+ aml_append(method,
+ aml_store(aml_derefof(aml_index(cpus_map, cpu_id)), cpu_on));
+ aml_append(method,
+ aml_store(aml_buffer(sizeof(madt_tmpl), madt_tmpl), madt));
+ /* Update the processor id, lapic id, and enable/disable status */
+ aml_append(method, aml_store(cpu_id, aml_index(madt, aml_int(2))));
+ aml_append(method, aml_store(cpu_id, aml_index(madt, aml_int(3))));
+ aml_append(method, aml_store(cpu_on, aml_index(madt, aml_int(4))));
+ aml_append(method, aml_return(madt));
+ aml_append(sb_scope, method);
+
+ /*
+ * _STA method - return ON status of cpu
+ * cpu_id = Arg0 = Processor ID = Local APIC ID
+ * cpu_on = Local0 = CPON flag for this cpu
+ */
+ method = aml_method(CPU_STATUS_METHOD, 1, AML_NOTSERIALIZED);
+ aml_append(method,
+ aml_store(aml_derefof(aml_index(cpus_map, cpu_id)), cpu_on));
+ if_ctx = aml_if(cpu_on);
+ {
+ aml_append(if_ctx, aml_return(aml_int(0xF)));
+ }
+ aml_append(method, if_ctx);
+ else_ctx = aml_else();
+ {
+ aml_append(else_ctx, aml_return(zero));
+ }
+ aml_append(method, else_ctx);
+ aml_append(sb_scope, method);
+
+ method = aml_method(CPU_EJECT_METHOD, 2, AML_NOTSERIALIZED);
+ aml_append(method, aml_sleep(200));
+ aml_append(sb_scope, method);
+
+ method = aml_method(CPU_SCAN_METHOD, 0, AML_NOTSERIALIZED);
+ {
+ Aml *while_ctx, *if_ctx2, *else_ctx2;
+ Aml *bus_check_evt = aml_int(1);
+ Aml *remove_evt = aml_int(3);
+ Aml *status_map = aml_local(5); /* Local5 = active cpu bitmap */
+ Aml *byte = aml_local(2); /* Local2 = last read byte from bitmap */
+ Aml *idx = aml_local(0); /* Processor ID / APIC ID iterator */
+ Aml *is_cpu_on = aml_local(1); /* Local1 = CPON flag for cpu */
+ Aml *status = aml_local(3); /* Local3 = active state for cpu */
+
+ aml_append(method, aml_store(aml_name(CPU_STATUS_MAP), status_map));
+ aml_append(method, aml_store(zero, byte));
+ aml_append(method, aml_store(zero, idx));
+
+ /* While (idx < SizeOf(CPON)) */
+ while_ctx = aml_while(aml_lless(idx, aml_sizeof(cpus_map)));
+ aml_append(while_ctx,
+ aml_store(aml_derefof(aml_index(cpus_map, idx)), is_cpu_on));
+
+ if_ctx = aml_if(aml_and(idx, aml_int(0x07), NULL));
+ {
+ /* Shift down previously read bitmap byte */
+ aml_append(if_ctx, aml_shiftright(byte, one, byte));
+ }
+ aml_append(while_ctx, if_ctx);
+
+ else_ctx = aml_else();
+ {
+ /* Read next byte from cpu bitmap */
+ aml_append(else_ctx, aml_store(aml_derefof(aml_index(status_map,
+ aml_shiftright(idx, aml_int(3), NULL))), byte));
+ }
+ aml_append(while_ctx, else_ctx);
+
+ aml_append(while_ctx, aml_store(aml_and(byte, one, NULL), status));
+ if_ctx = aml_if(aml_lnot(aml_equal(is_cpu_on, status)));
+ {
+ /* State change - update CPON with new state */
+ aml_append(if_ctx, aml_store(status, aml_index(cpus_map, idx)));
+ if_ctx2 = aml_if(aml_equal(status, one));
+ {
+ aml_append(if_ctx2,
+ aml_call2(AML_NOTIFY_METHOD, idx, bus_check_evt));
+ }
+ aml_append(if_ctx, if_ctx2);
+ else_ctx2 = aml_else();
+ {
+ aml_append(else_ctx2,
+ aml_call2(AML_NOTIFY_METHOD, idx, remove_evt));
+ }
+ }
+ aml_append(if_ctx, else_ctx2);
+ aml_append(while_ctx, if_ctx);
+
+ aml_append(while_ctx, aml_increment(idx)); /* go to next cpu */
+ aml_append(method, while_ctx);
+ }
+ aml_append(sb_scope, method);
+
+ aml_append(ctx, sb_scope);
+}
diff --git a/qemu/hw/acpi/ich9.c b/qemu/hw/acpi/ich9.c
index 1c7fcfa9d..27e978f5f 100644
--- a/qemu/hw/acpi/ich9.c
+++ b/qemu/hw/acpi/ich9.c
@@ -23,7 +23,9 @@
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
+#include "qapi/error.h"
#include "qapi/visitor.h"
#include "hw/i386/pc.h"
#include "hw/pci/pci.h"
@@ -239,7 +241,7 @@ static void pm_powerdown_req(Notifier *n, void *opaque)
}
void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
- bool smm_enabled, bool enable_tco,
+ bool smm_enabled,
qemu_irq sci_irq)
{
memory_region_init(&pm->io, OBJECT(lpc_pci), "ich9-pm", ICH9_PMIO_SIZE);
@@ -263,10 +265,8 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
pm->smm_enabled = smm_enabled;
- pm->enable_tco = enable_tco;
- if (pm->enable_tco) {
- acpi_pm_tco_init(&pm->tco_regs, &pm->io);
- }
+ pm->enable_tco = true;
+ acpi_pm_tco_init(&pm->tco_regs, &pm->io);
pm->irq = sci_irq;
qemu_register_reset(pm_reset, pm);
@@ -282,14 +282,13 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
}
}
-static void ich9_pm_get_gpe0_blk(Object *obj, Visitor *v,
- void *opaque, const char *name,
- Error **errp)
+static void ich9_pm_get_gpe0_blk(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
ICH9LPCPMRegs *pm = opaque;
uint32_t value = pm->pm_io_base + ICH9_PMIO_GPE0_STS;
- visit_type_uint32(v, &value, name, errp);
+ visit_type_uint32(v, name, &value, errp);
}
static bool ich9_pm_get_memory_hotplug_support(Object *obj, Error **errp)
@@ -307,25 +306,23 @@ static void ich9_pm_set_memory_hotplug_support(Object *obj, bool value,
s->pm.acpi_memory_hotplug.is_enabled = value;
}
-static void ich9_pm_get_disable_s3(Object *obj, Visitor *v,
- void *opaque, const char *name,
- Error **errp)
+static void ich9_pm_get_disable_s3(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
ICH9LPCPMRegs *pm = opaque;
uint8_t value = pm->disable_s3;
- visit_type_uint8(v, &value, name, errp);
+ visit_type_uint8(v, name, &value, errp);
}
-static void ich9_pm_set_disable_s3(Object *obj, Visitor *v,
- void *opaque, const char *name,
- Error **errp)
+static void ich9_pm_set_disable_s3(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
ICH9LPCPMRegs *pm = opaque;
Error *local_err = NULL;
uint8_t value;
- visit_type_uint8(v, &value, name, &local_err);
+ visit_type_uint8(v, name, &value, &local_err);
if (local_err) {
goto out;
}
@@ -334,25 +331,23 @@ out:
error_propagate(errp, local_err);
}
-static void ich9_pm_get_disable_s4(Object *obj, Visitor *v,
- void *opaque, const char *name,
- Error **errp)
+static void ich9_pm_get_disable_s4(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
ICH9LPCPMRegs *pm = opaque;
uint8_t value = pm->disable_s4;
- visit_type_uint8(v, &value, name, errp);
+ visit_type_uint8(v, name, &value, errp);
}
-static void ich9_pm_set_disable_s4(Object *obj, Visitor *v,
- void *opaque, const char *name,
- Error **errp)
+static void ich9_pm_set_disable_s4(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
ICH9LPCPMRegs *pm = opaque;
Error *local_err = NULL;
uint8_t value;
- visit_type_uint8(v, &value, name, &local_err);
+ visit_type_uint8(v, name, &value, &local_err);
if (local_err) {
goto out;
}
@@ -361,25 +356,23 @@ out:
error_propagate(errp, local_err);
}
-static void ich9_pm_get_s4_val(Object *obj, Visitor *v,
- void *opaque, const char *name,
- Error **errp)
+static void ich9_pm_get_s4_val(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
ICH9LPCPMRegs *pm = opaque;
uint8_t value = pm->s4_val;
- visit_type_uint8(v, &value, name, errp);
+ visit_type_uint8(v, name, &value, errp);
}
-static void ich9_pm_set_s4_val(Object *obj, Visitor *v,
- void *opaque, const char *name,
- Error **errp)
+static void ich9_pm_set_s4_val(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
ICH9LPCPMRegs *pm = opaque;
Error *local_err = NULL;
uint8_t value;
- visit_type_uint8(v, &value, name, &local_err);
+ visit_type_uint8(v, name, &value, &local_err);
if (local_err) {
goto out;
}
diff --git a/qemu/hw/acpi/memory_hotplug.c b/qemu/hw/acpi/memory_hotplug.c
index 2ff0d5ce1..f65a3a21e 100644
--- a/qemu/hw/acpi/memory_hotplug.c
+++ b/qemu/hw/acpi/memory_hotplug.c
@@ -1,3 +1,4 @@
+#include "qemu/osdep.h"
#include "hw/acpi/memory_hotplug.h"
#include "hw/acpi/pc-hotplug.h"
#include "hw/mem/pc-dimm.h"
@@ -155,6 +156,7 @@ static void acpi_memory_hotplug_write(void *opaque, hwaddr addr, uint64_t data,
qapi_event_send_mem_unplug_error(dev->id,
error_get_pretty(local_err),
&error_abort);
+ error_free(local_err);
break;
}
trace_mhp_acpi_pc_dimm_deleted(mem_st->selector);
@@ -230,6 +232,11 @@ void acpi_memory_plug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st,
DeviceState *dev, Error **errp)
{
MemStatus *mdev;
+ DeviceClass *dc = DEVICE_GET_CLASS(dev);
+
+ if (!dc->hotpluggable) {
+ return;
+ }
mdev = acpi_memory_slot_status(mem_st, dev, errp);
if (!mdev) {
@@ -238,11 +245,12 @@ void acpi_memory_plug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st,
mdev->dimm = dev;
mdev->is_enabled = true;
- mdev->is_inserting = true;
+ if (dev->hotplugged) {
+ mdev->is_inserting = true;
- /* do ACPI magic */
- acpi_send_gpe_event(ar, irq, ACPI_MEMORY_HOTPLUG_STATUS);
- return;
+ /* do ACPI magic */
+ acpi_send_gpe_event(ar, irq, ACPI_MEMORY_HOTPLUG_STATUS);
+ }
}
void acpi_memory_unplug_request_cb(ACPIREGS *ar, qemu_irq irq,
diff --git a/qemu/hw/acpi/memory_hotplug_acpi_table.c b/qemu/hw/acpi/memory_hotplug_acpi_table.c
new file mode 100644
index 000000000..c75660215
--- /dev/null
+++ b/qemu/hw/acpi/memory_hotplug_acpi_table.c
@@ -0,0 +1,262 @@
+/*
+ * Memory hotplug AML code of DSDT ACPI table
+ *
+ * Copyright (C) 2015 Red Hat Inc
+ *
+ * Author: Igor Mammedov <imammedo@redhat.com>
+ *
+ * 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 "hw/acpi/memory_hotplug.h"
+#include "include/hw/acpi/pc-hotplug.h"
+#include "hw/boards.h"
+
+void build_memory_hotplug_aml(Aml *ctx, uint32_t nr_mem,
+ uint16_t io_base, uint16_t io_len)
+{
+ Aml *ifctx;
+ Aml *method;
+ Aml *pci_scope;
+ Aml *mem_ctrl_dev;
+
+ /* scope for memory hotplug controller device node */
+ pci_scope = aml_scope("_SB.PCI0");
+ mem_ctrl_dev = aml_device(MEMORY_HOTPLUG_DEVICE);
+ {
+ Aml *one = aml_int(1);
+ Aml *zero = aml_int(0);
+ Aml *ret_val = aml_local(0);
+ Aml *slot_arg0 = aml_arg(0);
+ Aml *slots_nr = aml_name(MEMORY_SLOTS_NUMBER);
+ Aml *ctrl_lock = aml_name(MEMORY_SLOT_LOCK);
+ Aml *slot_selector = aml_name(MEMORY_SLOT_SLECTOR);
+
+ aml_append(mem_ctrl_dev, aml_name_decl("_HID", aml_string("PNP0A06")));
+ aml_append(mem_ctrl_dev,
+ aml_name_decl("_UID", aml_string("Memory hotplug resources")));
+
+ method = aml_method("_STA", 0, AML_NOTSERIALIZED);
+ ifctx = aml_if(aml_equal(slots_nr, zero));
+ {
+ aml_append(ifctx, aml_return(zero));
+ }
+ aml_append(method, ifctx);
+ /* present, functioning, decoding, not shown in UI */
+ aml_append(method, aml_return(aml_int(0xB)));
+ aml_append(mem_ctrl_dev, method);
+
+ aml_append(mem_ctrl_dev, aml_mutex(MEMORY_SLOT_LOCK, 0));
+
+ method = aml_method(MEMORY_SLOT_SCAN_METHOD, 0, AML_NOTSERIALIZED);
+ {
+ Aml *else_ctx;
+ Aml *while_ctx;
+ Aml *idx = aml_local(0);
+ Aml *eject_req = aml_int(3);
+ Aml *dev_chk = aml_int(1);
+
+ ifctx = aml_if(aml_equal(slots_nr, zero));
+ {
+ aml_append(ifctx, aml_return(zero));
+ }
+ aml_append(method, ifctx);
+
+ aml_append(method, aml_store(zero, idx));
+ aml_append(method, aml_acquire(ctrl_lock, 0xFFFF));
+ /* build AML that:
+ * loops over all slots and Notifies DIMMs with
+ * Device Check or Eject Request notifications if
+ * slot has corresponding status bit set and clears
+ * slot status.
+ */
+ while_ctx = aml_while(aml_lless(idx, slots_nr));
+ {
+ Aml *ins_evt = aml_name(MEMORY_SLOT_INSERT_EVENT);
+ Aml *rm_evt = aml_name(MEMORY_SLOT_REMOVE_EVENT);
+
+ aml_append(while_ctx, aml_store(idx, slot_selector));
+ ifctx = aml_if(aml_equal(ins_evt, one));
+ {
+ aml_append(ifctx,
+ aml_call2(MEMORY_SLOT_NOTIFY_METHOD,
+ idx, dev_chk));
+ aml_append(ifctx, aml_store(one, ins_evt));
+ }
+ aml_append(while_ctx, ifctx);
+
+ else_ctx = aml_else();
+ ifctx = aml_if(aml_equal(rm_evt, one));
+ {
+ aml_append(ifctx,
+ aml_call2(MEMORY_SLOT_NOTIFY_METHOD,
+ idx, eject_req));
+ aml_append(ifctx, aml_store(one, rm_evt));
+ }
+ aml_append(else_ctx, ifctx);
+ aml_append(while_ctx, else_ctx);
+
+ aml_append(while_ctx, aml_add(idx, one, idx));
+ }
+ aml_append(method, while_ctx);
+ aml_append(method, aml_release(ctrl_lock));
+ aml_append(method, aml_return(one));
+ }
+ aml_append(mem_ctrl_dev, method);
+
+ method = aml_method(MEMORY_SLOT_STATUS_METHOD, 1, AML_NOTSERIALIZED);
+ {
+ Aml *slot_enabled = aml_name(MEMORY_SLOT_ENABLED);
+
+ aml_append(method, aml_store(zero, ret_val));
+ aml_append(method, aml_acquire(ctrl_lock, 0xFFFF));
+ aml_append(method,
+ aml_store(aml_to_integer(slot_arg0), slot_selector));
+
+ ifctx = aml_if(aml_equal(slot_enabled, one));
+ {
+ aml_append(ifctx, aml_store(aml_int(0xF), ret_val));
+ }
+ aml_append(method, ifctx);
+
+ aml_append(method, aml_release(ctrl_lock));
+ aml_append(method, aml_return(ret_val));
+ }
+ aml_append(mem_ctrl_dev, method);
+
+ method = aml_method(MEMORY_SLOT_CRS_METHOD, 1, AML_SERIALIZED);
+ {
+ Aml *mr64 = aml_name("MR64");
+ Aml *mr32 = aml_name("MR32");
+ Aml *crs_tmpl = aml_resource_template();
+ Aml *minl = aml_name("MINL");
+ Aml *minh = aml_name("MINH");
+ Aml *maxl = aml_name("MAXL");
+ Aml *maxh = aml_name("MAXH");
+ Aml *lenl = aml_name("LENL");
+ Aml *lenh = aml_name("LENH");
+
+ aml_append(method, aml_acquire(ctrl_lock, 0xFFFF));
+ aml_append(method, aml_store(aml_to_integer(slot_arg0),
+ slot_selector));
+
+ aml_append(crs_tmpl,
+ aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
+ AML_CACHEABLE, AML_READ_WRITE,
+ 0, 0x0, 0xFFFFFFFFFFFFFFFEULL, 0,
+ 0xFFFFFFFFFFFFFFFFULL));
+ aml_append(method, aml_name_decl("MR64", crs_tmpl));
+ aml_append(method,
+ aml_create_dword_field(mr64, aml_int(14), "MINL"));
+ aml_append(method,
+ aml_create_dword_field(mr64, aml_int(18), "MINH"));
+ aml_append(method,
+ aml_create_dword_field(mr64, aml_int(38), "LENL"));
+ aml_append(method,
+ aml_create_dword_field(mr64, aml_int(42), "LENH"));
+ aml_append(method,
+ aml_create_dword_field(mr64, aml_int(22), "MAXL"));
+ aml_append(method,
+ aml_create_dword_field(mr64, aml_int(26), "MAXH"));
+
+ aml_append(method,
+ aml_store(aml_name(MEMORY_SLOT_ADDR_HIGH), minh));
+ aml_append(method,
+ aml_store(aml_name(MEMORY_SLOT_ADDR_LOW), minl));
+ aml_append(method,
+ aml_store(aml_name(MEMORY_SLOT_SIZE_HIGH), lenh));
+ aml_append(method,
+ aml_store(aml_name(MEMORY_SLOT_SIZE_LOW), lenl));
+
+ /* 64-bit math: MAX = MIN + LEN - 1 */
+ aml_append(method, aml_add(minl, lenl, maxl));
+ aml_append(method, aml_add(minh, lenh, maxh));
+ ifctx = aml_if(aml_lless(maxl, minl));
+ {
+ aml_append(ifctx, aml_add(maxh, one, maxh));
+ }
+ aml_append(method, ifctx);
+ ifctx = aml_if(aml_lless(maxl, one));
+ {
+ aml_append(ifctx, aml_subtract(maxh, one, maxh));
+ }
+ aml_append(method, ifctx);
+ aml_append(method, aml_subtract(maxl, one, maxl));
+
+ /* return 32-bit _CRS if addr/size is in low mem */
+ /* TODO: remove it since all hotplugged DIMMs are in high mem */
+ ifctx = aml_if(aml_equal(maxh, zero));
+ {
+ crs_tmpl = aml_resource_template();
+ aml_append(crs_tmpl,
+ aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
+ AML_MAX_FIXED, AML_CACHEABLE,
+ AML_READ_WRITE,
+ 0, 0x0, 0xFFFFFFFE, 0,
+ 0xFFFFFFFF));
+ aml_append(ifctx, aml_name_decl("MR32", crs_tmpl));
+ aml_append(ifctx,
+ aml_create_dword_field(mr32, aml_int(10), "MIN"));
+ aml_append(ifctx,
+ aml_create_dword_field(mr32, aml_int(14), "MAX"));
+ aml_append(ifctx,
+ aml_create_dword_field(mr32, aml_int(22), "LEN"));
+ aml_append(ifctx, aml_store(minl, aml_name("MIN")));
+ aml_append(ifctx, aml_store(maxl, aml_name("MAX")));
+ aml_append(ifctx, aml_store(lenl, aml_name("LEN")));
+
+ aml_append(ifctx, aml_release(ctrl_lock));
+ aml_append(ifctx, aml_return(mr32));
+ }
+ aml_append(method, ifctx);
+
+ aml_append(method, aml_release(ctrl_lock));
+ aml_append(method, aml_return(mr64));
+ }
+ aml_append(mem_ctrl_dev, method);
+
+ method = aml_method(MEMORY_SLOT_PROXIMITY_METHOD, 1,
+ AML_NOTSERIALIZED);
+ {
+ Aml *proximity = aml_name(MEMORY_SLOT_PROXIMITY);
+
+ aml_append(method, aml_acquire(ctrl_lock, 0xFFFF));
+ aml_append(method, aml_store(aml_to_integer(slot_arg0),
+ slot_selector));
+ aml_append(method, aml_store(proximity, ret_val));
+ aml_append(method, aml_release(ctrl_lock));
+ aml_append(method, aml_return(ret_val));
+ }
+ aml_append(mem_ctrl_dev, method);
+
+ method = aml_method(MEMORY_SLOT_OST_METHOD, 4, AML_NOTSERIALIZED);
+ {
+ Aml *ost_evt = aml_name(MEMORY_SLOT_OST_EVENT);
+ Aml *ost_status = aml_name(MEMORY_SLOT_OST_STATUS);
+
+ aml_append(method, aml_acquire(ctrl_lock, 0xFFFF));
+ aml_append(method, aml_store(aml_to_integer(slot_arg0),
+ slot_selector));
+ aml_append(method, aml_store(aml_arg(1), ost_evt));
+ aml_append(method, aml_store(aml_arg(2), ost_status));
+ aml_append(method, aml_release(ctrl_lock));
+ }
+ aml_append(mem_ctrl_dev, method);
+
+ method = aml_method(MEMORY_SLOT_EJECT_METHOD, 2, AML_NOTSERIALIZED);
+ {
+ Aml *eject = aml_name(MEMORY_SLOT_EJECT);
+
+ aml_append(method, aml_acquire(ctrl_lock, 0xFFFF));
+ aml_append(method, aml_store(aml_to_integer(slot_arg0),
+ slot_selector));
+ aml_append(method, aml_store(one, eject));
+ aml_append(method, aml_release(ctrl_lock));
+ }
+ aml_append(mem_ctrl_dev, method);
+ }
+ aml_append(pci_scope, mem_ctrl_dev);
+ aml_append(ctx, pci_scope);
+}
diff --git a/qemu/hw/acpi/nvdimm.c b/qemu/hw/acpi/nvdimm.c
new file mode 100644
index 000000000..9531340e5
--- /dev/null
+++ b/qemu/hw/acpi/nvdimm.c
@@ -0,0 +1,706 @@
+/*
+ * NVDIMM ACPI Implementation
+ *
+ * Copyright(C) 2015 Intel Corporation.
+ *
+ * Author:
+ * Xiao Guangrong <guangrong.xiao@linux.intel.com>
+ *
+ * NFIT is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT)
+ * and the DSM specification can be found at:
+ * http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf
+ *
+ * Currently, it only supports PMEM Virtualization.
+ *
+ * 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 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 <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "hw/acpi/acpi.h"
+#include "hw/acpi/aml-build.h"
+#include "hw/acpi/bios-linker-loader.h"
+#include "hw/nvram/fw_cfg.h"
+#include "hw/mem/nvdimm.h"
+
+static int nvdimm_plugged_device_list(Object *obj, void *opaque)
+{
+ GSList **list = opaque;
+
+ if (object_dynamic_cast(obj, TYPE_NVDIMM)) {
+ DeviceState *dev = DEVICE(obj);
+
+ if (dev->realized) { /* only realized NVDIMMs matter */
+ *list = g_slist_append(*list, DEVICE(obj));
+ }
+ }
+
+ object_child_foreach(obj, nvdimm_plugged_device_list, opaque);
+ return 0;
+}
+
+/*
+ * inquire plugged NVDIMM devices and link them into the list which is
+ * returned to the caller.
+ *
+ * Note: it is the caller's responsibility to free the list to avoid
+ * memory leak.
+ */
+static GSList *nvdimm_get_plugged_device_list(void)
+{
+ GSList *list = NULL;
+
+ object_child_foreach(qdev_get_machine(), nvdimm_plugged_device_list,
+ &list);
+ return list;
+}
+
+#define NVDIMM_UUID_LE(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \
+ { (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \
+ (b) & 0xff, ((b) >> 8) & 0xff, (c) & 0xff, ((c) >> 8) & 0xff, \
+ (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }
+
+/*
+ * define Byte Addressable Persistent Memory (PM) Region according to
+ * ACPI 6.0: 5.2.25.1 System Physical Address Range Structure.
+ */
+static const uint8_t nvdimm_nfit_spa_uuid[] =
+ NVDIMM_UUID_LE(0x66f0d379, 0xb4f3, 0x4074, 0xac, 0x43, 0x0d, 0x33,
+ 0x18, 0xb7, 0x8c, 0xdb);
+
+/*
+ * NVDIMM Firmware Interface Table
+ * @signature: "NFIT"
+ *
+ * It provides information that allows OSPM to enumerate NVDIMM present in
+ * the platform and associate system physical address ranges created by the
+ * NVDIMMs.
+ *
+ * It is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT)
+ */
+struct NvdimmNfitHeader {
+ ACPI_TABLE_HEADER_DEF
+ uint32_t reserved;
+} QEMU_PACKED;
+typedef struct NvdimmNfitHeader NvdimmNfitHeader;
+
+/*
+ * define NFIT structures according to ACPI 6.0: 5.2.25 NVDIMM Firmware
+ * Interface Table (NFIT).
+ */
+
+/*
+ * System Physical Address Range Structure
+ *
+ * It describes the system physical address ranges occupied by NVDIMMs and
+ * the types of the regions.
+ */
+struct NvdimmNfitSpa {
+ uint16_t type;
+ uint16_t length;
+ uint16_t spa_index;
+ uint16_t flags;
+ uint32_t reserved;
+ uint32_t proximity_domain;
+ uint8_t type_guid[16];
+ uint64_t spa_base;
+ uint64_t spa_length;
+ uint64_t mem_attr;
+} QEMU_PACKED;
+typedef struct NvdimmNfitSpa NvdimmNfitSpa;
+
+/*
+ * Memory Device to System Physical Address Range Mapping Structure
+ *
+ * It enables identifying each NVDIMM region and the corresponding SPA
+ * describing the memory interleave
+ */
+struct NvdimmNfitMemDev {
+ uint16_t type;
+ uint16_t length;
+ uint32_t nfit_handle;
+ uint16_t phys_id;
+ uint16_t region_id;
+ uint16_t spa_index;
+ uint16_t dcr_index;
+ uint64_t region_len;
+ uint64_t region_offset;
+ uint64_t region_dpa;
+ uint16_t interleave_index;
+ uint16_t interleave_ways;
+ uint16_t flags;
+ uint16_t reserved;
+} QEMU_PACKED;
+typedef struct NvdimmNfitMemDev NvdimmNfitMemDev;
+
+/*
+ * NVDIMM Control Region Structure
+ *
+ * It describes the NVDIMM and if applicable, Block Control Window.
+ */
+struct NvdimmNfitControlRegion {
+ uint16_t type;
+ uint16_t length;
+ uint16_t dcr_index;
+ uint16_t vendor_id;
+ uint16_t device_id;
+ uint16_t revision_id;
+ uint16_t sub_vendor_id;
+ uint16_t sub_device_id;
+ uint16_t sub_revision_id;
+ uint8_t reserved[6];
+ uint32_t serial_number;
+ uint16_t fic;
+ uint16_t num_bcw;
+ uint64_t bcw_size;
+ uint64_t cmd_offset;
+ uint64_t cmd_size;
+ uint64_t status_offset;
+ uint64_t status_size;
+ uint16_t flags;
+ uint8_t reserved2[6];
+} QEMU_PACKED;
+typedef struct NvdimmNfitControlRegion NvdimmNfitControlRegion;
+
+/*
+ * Module serial number is a unique number for each device. We use the
+ * slot id of NVDIMM device to generate this number so that each device
+ * associates with a different number.
+ *
+ * 0x123456 is a magic number we arbitrarily chose.
+ */
+static uint32_t nvdimm_slot_to_sn(int slot)
+{
+ return 0x123456 + slot;
+}
+
+/*
+ * handle is used to uniquely associate nfit_memdev structure with NVDIMM
+ * ACPI device - nfit_memdev.nfit_handle matches with the value returned
+ * by ACPI device _ADR method.
+ *
+ * We generate the handle with the slot id of NVDIMM device and reserve
+ * 0 for NVDIMM root device.
+ */
+static uint32_t nvdimm_slot_to_handle(int slot)
+{
+ return slot + 1;
+}
+
+/*
+ * index uniquely identifies the structure, 0 is reserved which indicates
+ * that the structure is not valid or the associated structure is not
+ * present.
+ *
+ * Each NVDIMM device needs two indexes, one for nfit_spa and another for
+ * nfit_dc which are generated by the slot id of NVDIMM device.
+ */
+static uint16_t nvdimm_slot_to_spa_index(int slot)
+{
+ return (slot + 1) << 1;
+}
+
+/* See the comments of nvdimm_slot_to_spa_index(). */
+static uint32_t nvdimm_slot_to_dcr_index(int slot)
+{
+ return nvdimm_slot_to_spa_index(slot) + 1;
+}
+
+/* ACPI 6.0: 5.2.25.1 System Physical Address Range Structure */
+static void
+nvdimm_build_structure_spa(GArray *structures, DeviceState *dev)
+{
+ NvdimmNfitSpa *nfit_spa;
+ uint64_t addr = object_property_get_int(OBJECT(dev), PC_DIMM_ADDR_PROP,
+ NULL);
+ uint64_t size = object_property_get_int(OBJECT(dev), PC_DIMM_SIZE_PROP,
+ NULL);
+ uint32_t node = object_property_get_int(OBJECT(dev), PC_DIMM_NODE_PROP,
+ NULL);
+ int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP,
+ NULL);
+
+ nfit_spa = acpi_data_push(structures, sizeof(*nfit_spa));
+
+ nfit_spa->type = cpu_to_le16(0 /* System Physical Address Range
+ Structure */);
+ nfit_spa->length = cpu_to_le16(sizeof(*nfit_spa));
+ nfit_spa->spa_index = cpu_to_le16(nvdimm_slot_to_spa_index(slot));
+
+ /*
+ * Control region is strict as all the device info, such as SN, index,
+ * is associated with slot id.
+ */
+ nfit_spa->flags = cpu_to_le16(1 /* Control region is strictly for
+ management during hot add/online
+ operation */ |
+ 2 /* Data in Proximity Domain field is
+ valid*/);
+
+ /* NUMA node. */
+ nfit_spa->proximity_domain = cpu_to_le32(node);
+ /* the region reported as PMEM. */
+ memcpy(nfit_spa->type_guid, nvdimm_nfit_spa_uuid,
+ sizeof(nvdimm_nfit_spa_uuid));
+
+ nfit_spa->spa_base = cpu_to_le64(addr);
+ nfit_spa->spa_length = cpu_to_le64(size);
+
+ /* It is the PMEM and can be cached as writeback. */
+ nfit_spa->mem_attr = cpu_to_le64(0x8ULL /* EFI_MEMORY_WB */ |
+ 0x8000ULL /* EFI_MEMORY_NV */);
+}
+
+/*
+ * ACPI 6.0: 5.2.25.2 Memory Device to System Physical Address Range Mapping
+ * Structure
+ */
+static void
+nvdimm_build_structure_memdev(GArray *structures, DeviceState *dev)
+{
+ NvdimmNfitMemDev *nfit_memdev;
+ uint64_t addr = object_property_get_int(OBJECT(dev), PC_DIMM_ADDR_PROP,
+ NULL);
+ uint64_t size = object_property_get_int(OBJECT(dev), PC_DIMM_SIZE_PROP,
+ NULL);
+ int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP,
+ NULL);
+ uint32_t handle = nvdimm_slot_to_handle(slot);
+
+ nfit_memdev = acpi_data_push(structures, sizeof(*nfit_memdev));
+
+ nfit_memdev->type = cpu_to_le16(1 /* Memory Device to System Address
+ Range Map Structure*/);
+ nfit_memdev->length = cpu_to_le16(sizeof(*nfit_memdev));
+ nfit_memdev->nfit_handle = cpu_to_le32(handle);
+
+ /*
+ * associate memory device with System Physical Address Range
+ * Structure.
+ */
+ nfit_memdev->spa_index = cpu_to_le16(nvdimm_slot_to_spa_index(slot));
+ /* associate memory device with Control Region Structure. */
+ nfit_memdev->dcr_index = cpu_to_le16(nvdimm_slot_to_dcr_index(slot));
+
+ /* The memory region on the device. */
+ nfit_memdev->region_len = cpu_to_le64(size);
+ nfit_memdev->region_dpa = cpu_to_le64(addr);
+
+ /* Only one interleave for PMEM. */
+ nfit_memdev->interleave_ways = cpu_to_le16(1);
+}
+
+/*
+ * ACPI 6.0: 5.2.25.5 NVDIMM Control Region Structure.
+ */
+static void nvdimm_build_structure_dcr(GArray *structures, DeviceState *dev)
+{
+ NvdimmNfitControlRegion *nfit_dcr;
+ int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP,
+ NULL);
+ uint32_t sn = nvdimm_slot_to_sn(slot);
+
+ nfit_dcr = acpi_data_push(structures, sizeof(*nfit_dcr));
+
+ nfit_dcr->type = cpu_to_le16(4 /* NVDIMM Control Region Structure */);
+ nfit_dcr->length = cpu_to_le16(sizeof(*nfit_dcr));
+ nfit_dcr->dcr_index = cpu_to_le16(nvdimm_slot_to_dcr_index(slot));
+
+ /* vendor: Intel. */
+ nfit_dcr->vendor_id = cpu_to_le16(0x8086);
+ nfit_dcr->device_id = cpu_to_le16(1);
+
+ /* The _DSM method is following Intel's DSM specification. */
+ nfit_dcr->revision_id = cpu_to_le16(1 /* Current Revision supported
+ in ACPI 6.0 is 1. */);
+ nfit_dcr->serial_number = cpu_to_le32(sn);
+ nfit_dcr->fic = cpu_to_le16(0x201 /* Format Interface Code. See Chapter
+ 2: NVDIMM Device Specific Method
+ (DSM) in DSM Spec Rev1.*/);
+}
+
+static GArray *nvdimm_build_device_structure(GSList *device_list)
+{
+ GArray *structures = g_array_new(false, true /* clear */, 1);
+
+ for (; device_list; device_list = device_list->next) {
+ DeviceState *dev = device_list->data;
+
+ /* build System Physical Address Range Structure. */
+ nvdimm_build_structure_spa(structures, dev);
+
+ /*
+ * build Memory Device to System Physical Address Range Mapping
+ * Structure.
+ */
+ nvdimm_build_structure_memdev(structures, dev);
+
+ /* build NVDIMM Control Region Structure. */
+ nvdimm_build_structure_dcr(structures, dev);
+ }
+
+ return structures;
+}
+
+static void nvdimm_build_nfit(GSList *device_list, GArray *table_offsets,
+ GArray *table_data, GArray *linker)
+{
+ GArray *structures = nvdimm_build_device_structure(device_list);
+ unsigned int header;
+
+ acpi_add_table(table_offsets, table_data);
+
+ /* NFIT header. */
+ header = table_data->len;
+ acpi_data_push(table_data, sizeof(NvdimmNfitHeader));
+ /* NVDIMM device structures. */
+ g_array_append_vals(table_data, structures->data, structures->len);
+
+ build_header(linker, table_data,
+ (void *)(table_data->data + header), "NFIT",
+ sizeof(NvdimmNfitHeader) + structures->len, 1, NULL, NULL);
+ g_array_free(structures, true);
+}
+
+struct NvdimmDsmIn {
+ uint32_t handle;
+ uint32_t revision;
+ uint32_t function;
+ /* the remaining size in the page is used by arg3. */
+ union {
+ uint8_t arg3[0];
+ };
+} QEMU_PACKED;
+typedef struct NvdimmDsmIn NvdimmDsmIn;
+
+struct NvdimmDsmOut {
+ /* the size of buffer filled by QEMU. */
+ uint32_t len;
+ uint8_t data[0];
+} QEMU_PACKED;
+typedef struct NvdimmDsmOut NvdimmDsmOut;
+
+struct NvdimmDsmFunc0Out {
+ /* the size of buffer filled by QEMU. */
+ uint32_t len;
+ uint32_t supported_func;
+} QEMU_PACKED;
+typedef struct NvdimmDsmFunc0Out NvdimmDsmFunc0Out;
+
+struct NvdimmDsmFuncNoPayloadOut {
+ /* the size of buffer filled by QEMU. */
+ uint32_t len;
+ uint32_t func_ret_status;
+} QEMU_PACKED;
+typedef struct NvdimmDsmFuncNoPayloadOut NvdimmDsmFuncNoPayloadOut;
+
+static uint64_t
+nvdimm_dsm_read(void *opaque, hwaddr addr, unsigned size)
+{
+ nvdimm_debug("BUG: we never read _DSM IO Port.\n");
+ return 0;
+}
+
+static void
+nvdimm_dsm_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+ NvdimmDsmIn *in;
+ hwaddr dsm_mem_addr = val;
+
+ nvdimm_debug("dsm memory address %#" HWADDR_PRIx ".\n", dsm_mem_addr);
+
+ /*
+ * The DSM memory is mapped to guest address space so an evil guest
+ * can change its content while we are doing DSM emulation. Avoid
+ * this by copying DSM memory to QEMU local memory.
+ */
+ in = g_malloc(TARGET_PAGE_SIZE);
+ cpu_physical_memory_read(dsm_mem_addr, in, TARGET_PAGE_SIZE);
+
+ le32_to_cpus(&in->revision);
+ le32_to_cpus(&in->function);
+ le32_to_cpus(&in->handle);
+
+ nvdimm_debug("Revision %#x Handler %#x Function %#x.\n", in->revision,
+ in->handle, in->function);
+
+ /*
+ * function 0 is called to inquire which functions are supported by
+ * OSPM
+ */
+ if (in->function == 0) {
+ NvdimmDsmFunc0Out func0 = {
+ .len = cpu_to_le32(sizeof(func0)),
+ /* No function supported other than function 0 */
+ .supported_func = cpu_to_le32(0),
+ };
+ cpu_physical_memory_write(dsm_mem_addr, &func0, sizeof func0);
+ } else {
+ /* No function except function 0 is supported yet. */
+ NvdimmDsmFuncNoPayloadOut out = {
+ .len = cpu_to_le32(sizeof(out)),
+ .func_ret_status = cpu_to_le32(1) /* Not Supported */,
+ };
+ cpu_physical_memory_write(dsm_mem_addr, &out, sizeof(out));
+ }
+
+ g_free(in);
+}
+
+static const MemoryRegionOps nvdimm_dsm_ops = {
+ .read = nvdimm_dsm_read,
+ .write = nvdimm_dsm_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+void nvdimm_init_acpi_state(AcpiNVDIMMState *state, MemoryRegion *io,
+ FWCfgState *fw_cfg, Object *owner)
+{
+ memory_region_init_io(&state->io_mr, owner, &nvdimm_dsm_ops, state,
+ "nvdimm-acpi-io", NVDIMM_ACPI_IO_LEN);
+ memory_region_add_subregion(io, NVDIMM_ACPI_IO_BASE, &state->io_mr);
+
+ state->dsm_mem = g_array_new(false, true /* clear */, 1);
+ acpi_data_push(state->dsm_mem, TARGET_PAGE_SIZE);
+ fw_cfg_add_file(fw_cfg, NVDIMM_DSM_MEM_FILE, state->dsm_mem->data,
+ state->dsm_mem->len);
+}
+
+#define NVDIMM_COMMON_DSM "NCAL"
+#define NVDIMM_ACPI_MEM_ADDR "MEMA"
+
+static void nvdimm_build_common_dsm(Aml *dev)
+{
+ Aml *method, *ifctx, *function, *dsm_mem, *unpatched, *result_size;
+ uint8_t byte_list[1];
+
+ method = aml_method(NVDIMM_COMMON_DSM, 4, AML_SERIALIZED);
+ function = aml_arg(2);
+ dsm_mem = aml_name(NVDIMM_ACPI_MEM_ADDR);
+
+ /*
+ * do not support any method if DSM memory address has not been
+ * patched.
+ */
+ unpatched = aml_if(aml_equal(dsm_mem, aml_int(0x0)));
+
+ /*
+ * function 0 is called to inquire what functions are supported by
+ * OSPM
+ */
+ ifctx = aml_if(aml_equal(function, aml_int(0)));
+ byte_list[0] = 0 /* No function Supported */;
+ aml_append(ifctx, aml_return(aml_buffer(1, byte_list)));
+ aml_append(unpatched, ifctx);
+
+ /* No function is supported yet. */
+ byte_list[0] = 1 /* Not Supported */;
+ aml_append(unpatched, aml_return(aml_buffer(1, byte_list)));
+ aml_append(method, unpatched);
+
+ /*
+ * The HDLE indicates the DSM function is issued from which device,
+ * it is not used at this time as no function is supported yet.
+ * Currently we make it always be 0 for all the devices and will set
+ * the appropriate value once real function is implemented.
+ */
+ aml_append(method, aml_store(aml_int(0x0), aml_name("HDLE")));
+ aml_append(method, aml_store(aml_arg(1), aml_name("REVS")));
+ aml_append(method, aml_store(aml_arg(2), aml_name("FUNC")));
+
+ /*
+ * tell QEMU about the real address of DSM memory, then QEMU
+ * gets the control and fills the result in DSM memory.
+ */
+ aml_append(method, aml_store(dsm_mem, aml_name("NTFI")));
+
+ result_size = aml_local(1);
+ aml_append(method, aml_store(aml_name("RLEN"), result_size));
+ aml_append(method, aml_store(aml_shiftleft(result_size, aml_int(3)),
+ result_size));
+ aml_append(method, aml_create_field(aml_name("ODAT"), aml_int(0),
+ result_size, "OBUF"));
+ aml_append(method, aml_concatenate(aml_buffer(0, NULL), aml_name("OBUF"),
+ aml_arg(6)));
+ aml_append(method, aml_return(aml_arg(6)));
+ aml_append(dev, method);
+}
+
+static void nvdimm_build_device_dsm(Aml *dev)
+{
+ Aml *method;
+
+ method = aml_method("_DSM", 4, AML_NOTSERIALIZED);
+ aml_append(method, aml_return(aml_call4(NVDIMM_COMMON_DSM, aml_arg(0),
+ aml_arg(1), aml_arg(2), aml_arg(3))));
+ aml_append(dev, method);
+}
+
+static void nvdimm_build_nvdimm_devices(GSList *device_list, Aml *root_dev)
+{
+ for (; device_list; device_list = device_list->next) {
+ DeviceState *dev = device_list->data;
+ int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP,
+ NULL);
+ uint32_t handle = nvdimm_slot_to_handle(slot);
+ Aml *nvdimm_dev;
+
+ nvdimm_dev = aml_device("NV%02X", slot);
+
+ /*
+ * ACPI 6.0: 9.20 NVDIMM Devices:
+ *
+ * _ADR object that is used to supply OSPM with unique address
+ * of the NVDIMM device. This is done by returning the NFIT Device
+ * handle that is used to identify the associated entries in ACPI
+ * table NFIT or _FIT.
+ */
+ aml_append(nvdimm_dev, aml_name_decl("_ADR", aml_int(handle)));
+
+ nvdimm_build_device_dsm(nvdimm_dev);
+ aml_append(root_dev, nvdimm_dev);
+ }
+}
+
+static void nvdimm_build_ssdt(GSList *device_list, GArray *table_offsets,
+ GArray *table_data, GArray *linker)
+{
+ Aml *ssdt, *sb_scope, *dev, *field;
+ int mem_addr_offset, nvdimm_ssdt;
+
+ acpi_add_table(table_offsets, table_data);
+
+ ssdt = init_aml_allocator();
+ acpi_data_push(ssdt->buf, sizeof(AcpiTableHeader));
+
+ sb_scope = aml_scope("\\_SB");
+
+ dev = aml_device("NVDR");
+
+ /*
+ * ACPI 6.0: 9.20 NVDIMM Devices:
+ *
+ * The ACPI Name Space device uses _HID of ACPI0012 to identify the root
+ * NVDIMM interface device. Platform firmware is required to contain one
+ * such device in _SB scope if NVDIMMs support is exposed by platform to
+ * OSPM.
+ * For each NVDIMM present or intended to be supported by platform,
+ * platform firmware also exposes an ACPI Namespace Device under the
+ * root device.
+ */
+ aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0012")));
+
+ /* map DSM memory and IO into ACPI namespace. */
+ aml_append(dev, aml_operation_region("NPIO", AML_SYSTEM_IO,
+ aml_int(NVDIMM_ACPI_IO_BASE), NVDIMM_ACPI_IO_LEN));
+ aml_append(dev, aml_operation_region("NRAM", AML_SYSTEM_MEMORY,
+ aml_name(NVDIMM_ACPI_MEM_ADDR), TARGET_PAGE_SIZE));
+
+ /*
+ * DSM notifier:
+ * NTFI: write the address of DSM memory and notify QEMU to emulate
+ * the access.
+ *
+ * It is the IO port so that accessing them will cause VM-exit, the
+ * control will be transferred to QEMU.
+ */
+ field = aml_field("NPIO", AML_DWORD_ACC, AML_NOLOCK, AML_PRESERVE);
+ aml_append(field, aml_named_field("NTFI",
+ sizeof(uint32_t) * BITS_PER_BYTE));
+ aml_append(dev, field);
+
+ /*
+ * DSM input:
+ * HDLE: store device's handle, it's zero if the _DSM call happens
+ * on NVDIMM Root Device.
+ * REVS: store the Arg1 of _DSM call.
+ * FUNC: store the Arg2 of _DSM call.
+ * ARG3: store the Arg3 of _DSM call.
+ *
+ * They are RAM mapping on host so that these accesses never cause
+ * VM-EXIT.
+ */
+ field = aml_field("NRAM", AML_DWORD_ACC, AML_NOLOCK, AML_PRESERVE);
+ aml_append(field, aml_named_field("HDLE",
+ sizeof(typeof_field(NvdimmDsmIn, handle)) * BITS_PER_BYTE));
+ aml_append(field, aml_named_field("REVS",
+ sizeof(typeof_field(NvdimmDsmIn, revision)) * BITS_PER_BYTE));
+ aml_append(field, aml_named_field("FUNC",
+ sizeof(typeof_field(NvdimmDsmIn, function)) * BITS_PER_BYTE));
+ aml_append(field, aml_named_field("ARG3",
+ (TARGET_PAGE_SIZE - offsetof(NvdimmDsmIn, arg3)) *
+ BITS_PER_BYTE));
+ aml_append(dev, field);
+
+ /*
+ * DSM output:
+ * RLEN: the size of the buffer filled by QEMU.
+ * ODAT: the buffer QEMU uses to store the result.
+ *
+ * Since the page is reused by both input and out, the input data
+ * will be lost after storing new result into ODAT so we should fetch
+ * all the input data before writing the result.
+ */
+ field = aml_field("NRAM", AML_DWORD_ACC, AML_NOLOCK, AML_PRESERVE);
+ aml_append(field, aml_named_field("RLEN",
+ sizeof(typeof_field(NvdimmDsmOut, len)) * BITS_PER_BYTE));
+ aml_append(field, aml_named_field("ODAT",
+ (TARGET_PAGE_SIZE - offsetof(NvdimmDsmOut, data)) *
+ BITS_PER_BYTE));
+ aml_append(dev, field);
+
+ nvdimm_build_common_dsm(dev);
+ nvdimm_build_device_dsm(dev);
+
+ nvdimm_build_nvdimm_devices(device_list, dev);
+
+ aml_append(sb_scope, dev);
+ aml_append(ssdt, sb_scope);
+
+ nvdimm_ssdt = table_data->len;
+
+ /* copy AML table into ACPI tables blob and patch header there */
+ g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len);
+ mem_addr_offset = build_append_named_dword(table_data,
+ NVDIMM_ACPI_MEM_ADDR);
+
+ bios_linker_loader_alloc(linker, NVDIMM_DSM_MEM_FILE, TARGET_PAGE_SIZE,
+ false /* high memory */);
+ bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE,
+ NVDIMM_DSM_MEM_FILE, table_data,
+ table_data->data + mem_addr_offset,
+ sizeof(uint32_t));
+ build_header(linker, table_data,
+ (void *)(table_data->data + nvdimm_ssdt),
+ "SSDT", table_data->len - nvdimm_ssdt, 1, NULL, "NVDIMM");
+ free_aml_allocator();
+}
+
+void nvdimm_build_acpi(GArray *table_offsets, GArray *table_data,
+ GArray *linker)
+{
+ GSList *device_list;
+
+ /* no NVDIMM device is plugged. */
+ device_list = nvdimm_get_plugged_device_list();
+ if (!device_list) {
+ return;
+ }
+ nvdimm_build_nfit(device_list, table_offsets, table_data, linker);
+ nvdimm_build_ssdt(device_list, table_offsets, table_data, linker);
+ g_slist_free(device_list);
+}
diff --git a/qemu/hw/acpi/pcihp.c b/qemu/hw/acpi/pcihp.c
index fbbc4dde4..71f4c4e14 100644
--- a/qemu/hw/acpi/pcihp.c
+++ b/qemu/hw/acpi/pcihp.c
@@ -24,6 +24,7 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "hw/acpi/pcihp.h"
#include "hw/hw.h"
@@ -34,6 +35,7 @@
#include "exec/ioport.h"
#include "exec/address-spaces.h"
#include "hw/pci/pci_bus.h"
+#include "qapi/error.h"
#include "qom/qom-qobject.h"
#include "qapi/qmp/qint.h"
diff --git a/qemu/hw/acpi/piix4.c b/qemu/hw/acpi/piix4.c
index 2cd2fee89..16abdf162 100644
--- a/qemu/hw/acpi/piix4.c
+++ b/qemu/hw/acpi/piix4.c
@@ -18,6 +18,7 @@
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/isa/apm.h"
@@ -25,6 +26,7 @@
#include "hw/pci/pci.h"
#include "hw/acpi/acpi.h"
#include "sysemu/sysemu.h"
+#include "qapi/error.h"
#include "qemu/range.h"
#include "exec/ioport.h"
#include "hw/nvram/fw_cfg.h"
diff --git a/qemu/hw/acpi/tco.c b/qemu/hw/acpi/tco.c
index 7a026c255..8ce7daf23 100644
--- a/qemu/hw/acpi/tco.c
+++ b/qemu/hw/acpi/tco.c
@@ -6,6 +6,7 @@
* 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 "sysemu/watchdog.h"
#include "hw/i386/ich9.h"
diff --git a/qemu/hw/alpha/dp264.c b/qemu/hw/alpha/dp264.c
index f86e7bb83..f1267b544 100644
--- a/qemu/hw/alpha/dp264.c
+++ b/qemu/hw/alpha/dp264.c
@@ -6,16 +6,21 @@
* that we need to emulate as well.
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "elf.h"
#include "hw/loader.h"
#include "hw/boards.h"
#include "alpha_sys.h"
+#include "qemu/error-report.h"
#include "sysemu/sysemu.h"
#include "hw/timer/mc146818rtc.h"
#include "hw/ide.h"
#include "hw/timer/i8254.h"
#include "hw/char/serial.h"
+#include "qemu/cutils.h"
#define MAX_IDE_BUS 2
@@ -104,14 +109,14 @@ static void clipper_init(MachineState *machine)
palcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS,
bios_name ? bios_name : "palcode-clipper");
if (palcode_filename == NULL) {
- hw_error("no palcode provided\n");
+ error_report("no palcode provided");
exit(1);
}
size = load_elf(palcode_filename, cpu_alpha_superpage_to_phys,
NULL, &palcode_entry, &palcode_low, &palcode_high,
- 0, EM_ALPHA, 0);
+ 0, EM_ALPHA, 0, 0);
if (size < 0) {
- hw_error("could not load palcode '%s'\n", palcode_filename);
+ error_report("could not load palcode '%s'", palcode_filename);
exit(1);
}
g_free(palcode_filename);
@@ -129,9 +134,9 @@ static void clipper_init(MachineState *machine)
size = load_elf(kernel_filename, cpu_alpha_superpage_to_phys,
NULL, &kernel_entry, &kernel_low, &kernel_high,
- 0, EM_ALPHA, 0);
+ 0, EM_ALPHA, 0, 0);
if (size < 0) {
- hw_error("could not load kernel '%s'\n", kernel_filename);
+ error_report("could not load kernel '%s'", kernel_filename);
exit(1);
}
@@ -148,8 +153,8 @@ static void clipper_init(MachineState *machine)
initrd_size = get_image_size(initrd_filename);
if (initrd_size < 0) {
- hw_error("could not load initial ram disk '%s'\n",
- initrd_filename);
+ error_report("could not load initial ram disk '%s'",
+ initrd_filename);
exit(1);
}
@@ -168,17 +173,12 @@ static void clipper_init(MachineState *machine)
}
}
-static QEMUMachine clipper_machine = {
- .name = "clipper",
- .desc = "Alpha DP264/CLIPPER",
- .init = clipper_init,
- .max_cpus = 4,
- .is_default = 1,
-};
-
-static void clipper_machine_init(void)
+static void clipper_machine_init(MachineClass *mc)
{
- qemu_register_machine(&clipper_machine);
+ mc->desc = "Alpha DP264/CLIPPER";
+ mc->init = clipper_init;
+ mc->max_cpus = 4;
+ mc->is_default = 1;
}
-machine_init(clipper_machine_init);
+DEFINE_MACHINE("clipper", clipper_machine_init)
diff --git a/qemu/hw/alpha/pci.c b/qemu/hw/alpha/pci.c
index d839dd556..5baa0eaf1 100644
--- a/qemu/hw/alpha/pci.c
+++ b/qemu/hw/alpha/pci.c
@@ -6,10 +6,13 @@
* ??? Sparse memory access not implemented.
*/
-#include "config.h"
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "alpha_sys.h"
#include "qemu/log.h"
#include "sysemu/sysemu.h"
+#include "trace.h"
/* Fallback for unassigned PCI I/O operations. Avoids MCHK. */
@@ -73,7 +76,7 @@ static uint64_t iack_read(void *opaque, hwaddr addr, unsigned size)
static void special_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
- qemu_log("pci: special write cycle");
+ trace_alpha_pci_iack_write();
}
const MemoryRegionOps alpha_pci_iack_ops = {
diff --git a/qemu/hw/alpha/typhoon.c b/qemu/hw/alpha/typhoon.c
index 421162e1d..97721b535 100644
--- a/qemu/hw/alpha/typhoon.c
+++ b/qemu/hw/alpha/typhoon.c
@@ -6,6 +6,8 @@
* This work is licensed under the GNU GPL license version 2 or later.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "cpu.h"
#include "hw/hw.h"
#include "hw/devices.h"
@@ -920,7 +922,8 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus,
{
qemu_irq *isa_irqs;
- *isa_bus = isa_bus_new(NULL, get_system_memory(), &s->pchip.reg_io);
+ *isa_bus = isa_bus_new(NULL, get_system_memory(), &s->pchip.reg_io,
+ &error_abort);
isa_irqs = i8259_init(*isa_bus,
qemu_allocate_irq(typhoon_set_isa_irq, s, 0));
isa_bus_irqs(*isa_bus, isa_irqs);
diff --git a/qemu/hw/arm/Makefile.objs b/qemu/hw/arm/Makefile.objs
index cf346c1d0..954c9fe15 100644
--- a/qemu/hw/arm/Makefile.objs
+++ b/qemu/hw/arm/Makefile.objs
@@ -1,6 +1,6 @@
obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o
obj-$(CONFIG_DIGIC) += digic_boards.o
-obj-y += integratorcp.o kzm.o mainstone.o musicpal.o nseries.o
+obj-y += integratorcp.o mainstone.o musicpal.o nseries.o
obj-y += omap_sx1.o palm.o realview.o spitz.o stellaris.o
obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o
obj-$(CONFIG_ACPI) += virt-acpi-build.o
@@ -11,5 +11,9 @@ obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
obj-$(CONFIG_DIGIC) += digic.o
obj-y += omap1.o omap2.o strongarm.o
obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o
+obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o raspi.o
obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp.o xlnx-ep108.o
+obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o
+obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o
+obj-$(CONFIG_ASPEED_SOC) += ast2400.o palmetto-bmc.o
diff --git a/qemu/hw/arm/allwinner-a10.c b/qemu/hw/arm/allwinner-a10.c
index ff249af33..ca15d1c8c 100644
--- a/qemu/hw/arm/allwinner-a10.c
+++ b/qemu/hw/arm/allwinner-a10.c
@@ -15,6 +15,10 @@
* for more details.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/sysbus.h"
#include "hw/devices.h"
#include "hw/arm/allwinner-a10.h"
@@ -39,6 +43,9 @@ static void aw_a10_init(Object *obj)
qemu_check_nic_model(&nd_table[0], TYPE_AW_EMAC);
qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]);
}
+
+ object_initialize(&s->sata, sizeof(s->sata), TYPE_ALLWINNER_AHCI);
+ qdev_set_parent_bus(DEVICE(&s->sata), sysbus_get_default());
}
static void aw_a10_realize(DeviceState *dev, Error **errp)
@@ -93,6 +100,14 @@ static void aw_a10_realize(DeviceState *dev, Error **errp)
sysbus_mmio_map(sysbusdev, 0, AW_A10_EMAC_BASE);
sysbus_connect_irq(sysbusdev, 0, s->irq[55]);
+ object_property_set_bool(OBJECT(&s->sata), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->sata), 0, AW_A10_SATA_BASE);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->sata), 0, s->irq[56]);
+
/* FIXME use a qdev chardev prop instead of serial_hds[] */
serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE, 2, s->irq[1],
115200, serial_hds[0], DEVICE_NATIVE_ENDIAN);
@@ -103,6 +118,12 @@ static void aw_a10_class_init(ObjectClass *oc, void *data)
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = aw_a10_realize;
+
+ /*
+ * Reason: creates an ARM CPU, thus use after free(), see
+ * arm_cpu_class_init()
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo aw_a10_type_info = {
diff --git a/qemu/hw/arm/armv7m.c b/qemu/hw/arm/armv7m.c
index c6eab6de3..bb2a22d96 100644
--- a/qemu/hw/arm/armv7m.c
+++ b/qemu/hw/arm/armv7m.c
@@ -7,6 +7,10 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/sysbus.h"
#include "hw/arm/arm.h"
#include "hw/loader.h"
@@ -166,17 +170,15 @@ static void armv7m_reset(void *opaque)
mem_size is in bytes.
Returns the NVIC array. */
-qemu_irq *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq,
+DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq,
const char *kernel_filename, const char *cpu_model)
{
ARMCPU *cpu;
CPUARMState *env;
DeviceState *nvic;
- qemu_irq *pic = g_new(qemu_irq, num_irq);
int image_size;
uint64_t entry;
uint64_t lowaddr;
- int i;
int big_endian;
MemoryRegion *hack = g_new(MemoryRegion, 1);
@@ -198,9 +200,6 @@ qemu_irq *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq,
qdev_init_nofail(nvic);
sysbus_connect_irq(SYS_BUS_DEVICE(nvic), 0,
qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ));
- for (i = 0; i < num_irq; i++) {
- pic[i] = qdev_get_gpio_in(nvic, i);
- }
#ifdef TARGET_WORDS_BIGENDIAN
big_endian = 1;
@@ -215,7 +214,7 @@ qemu_irq *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq,
if (kernel_filename) {
image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr,
- NULL, big_endian, ELF_MACHINE, 1);
+ NULL, big_endian, EM_ARM, 1, 0);
if (image_size < 0) {
image_size = load_image_targphys(kernel_filename, 0, mem_size);
lowaddr = 0;
@@ -229,12 +228,12 @@ qemu_irq *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq,
/* Hack to map an additional page of ram at the top of the address
space. This stops qemu complaining about executing code outside RAM
when returning from an exception. */
- memory_region_init_ram(hack, NULL, "armv7m.hack", 0x1000, &error_abort);
+ memory_region_init_ram(hack, NULL, "armv7m.hack", 0x1000, &error_fatal);
vmstate_register_ram_global(hack);
memory_region_add_subregion(system_memory, 0xfffff000, hack);
qemu_register_reset(armv7m_reset, cpu);
- return pic;
+ return nvic;
}
static Property bitband_properties[] = {
diff --git a/qemu/hw/arm/ast2400.c b/qemu/hw/arm/ast2400.c
new file mode 100644
index 000000000..03f993863
--- /dev/null
+++ b/qemu/hw/arm/ast2400.c
@@ -0,0 +1,140 @@
+/*
+ * AST2400 SoC
+ *
+ * Andrew Jeffery <andrew@aj.id.au>
+ * Jeremy Kerr <jk@ozlabs.org>
+ *
+ * Copyright 2016 IBM Corp.
+ *
+ * This code is licensed under the GPL version 2 or later. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "exec/address-spaces.h"
+#include "hw/arm/ast2400.h"
+#include "hw/char/serial.h"
+
+#define AST2400_UART_5_BASE 0x00184000
+#define AST2400_IOMEM_SIZE 0x00200000
+#define AST2400_IOMEM_BASE 0x1E600000
+#define AST2400_VIC_BASE 0x1E6C0000
+#define AST2400_TIMER_BASE 0x1E782000
+
+static const int uart_irqs[] = { 9, 32, 33, 34, 10 };
+static const int timer_irqs[] = { 16, 17, 18, 35, 36, 37, 38, 39, };
+
+/*
+ * IO handlers: simply catch any reads/writes to IO addresses that aren't
+ * handled by a device mapping.
+ */
+
+static uint64_t ast2400_io_read(void *p, hwaddr offset, unsigned size)
+{
+ qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " [%u]\n",
+ __func__, offset, size);
+ return 0;
+}
+
+static void ast2400_io_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " <- 0x%" PRIx64 " [%u]\n",
+ __func__, offset, value, size);
+}
+
+static const MemoryRegionOps ast2400_io_ops = {
+ .read = ast2400_io_read,
+ .write = ast2400_io_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void ast2400_init(Object *obj)
+{
+ AST2400State *s = AST2400(obj);
+
+ s->cpu = cpu_arm_init("arm926");
+
+ object_initialize(&s->vic, sizeof(s->vic), TYPE_ASPEED_VIC);
+ object_property_add_child(obj, "vic", OBJECT(&s->vic), NULL);
+ qdev_set_parent_bus(DEVICE(&s->vic), sysbus_get_default());
+
+ object_initialize(&s->timerctrl, sizeof(s->timerctrl), TYPE_ASPEED_TIMER);
+ object_property_add_child(obj, "timerctrl", OBJECT(&s->timerctrl), NULL);
+ qdev_set_parent_bus(DEVICE(&s->timerctrl), sysbus_get_default());
+}
+
+static void ast2400_realize(DeviceState *dev, Error **errp)
+{
+ int i;
+ AST2400State *s = AST2400(dev);
+ Error *err = NULL;
+
+ /* IO space */
+ memory_region_init_io(&s->iomem, NULL, &ast2400_io_ops, NULL,
+ "ast2400.io", AST2400_IOMEM_SIZE);
+ memory_region_add_subregion_overlap(get_system_memory(), AST2400_IOMEM_BASE,
+ &s->iomem, -1);
+
+ /* VIC */
+ object_property_set_bool(OBJECT(&s->vic), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->vic), 0, AST2400_VIC_BASE);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 0,
+ qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_IRQ));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 1,
+ qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_FIQ));
+
+ /* Timer */
+ object_property_set_bool(OBJECT(&s->timerctrl), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->timerctrl), 0, AST2400_TIMER_BASE);
+ for (i = 0; i < ARRAY_SIZE(timer_irqs); i++) {
+ qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->vic), timer_irqs[i]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq);
+ }
+
+ /* UART - attach an 8250 to the IO space as our UART5 */
+ if (serial_hds[0]) {
+ qemu_irq uart5 = qdev_get_gpio_in(DEVICE(&s->vic), uart_irqs[4]);
+ serial_mm_init(&s->iomem, AST2400_UART_5_BASE, 2,
+ uart5, 38400, serial_hds[0], DEVICE_LITTLE_ENDIAN);
+ }
+}
+
+static void ast2400_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = ast2400_realize;
+
+ /*
+ * Reason: creates an ARM CPU, thus use after free(), see
+ * arm_cpu_class_init()
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
+}
+
+static const TypeInfo ast2400_type_info = {
+ .name = TYPE_AST2400,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AST2400State),
+ .instance_init = ast2400_init,
+ .class_init = ast2400_class_init,
+};
+
+static void ast2400_register_types(void)
+{
+ type_register_static(&ast2400_type_info);
+}
+
+type_init(ast2400_register_types)
diff --git a/qemu/hw/arm/bcm2835_peripherals.c b/qemu/hw/arm/bcm2835_peripherals.c
new file mode 100644
index 000000000..234d51843
--- /dev/null
+++ b/qemu/hw/arm/bcm2835_peripherals.c
@@ -0,0 +1,312 @@
+/*
+ * Raspberry Pi emulation (c) 2012 Gregory Estrade
+ * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous
+ *
+ * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft
+ * Written by Andrew Baumann
+ *
+ * This code is licensed under the GNU GPLv2 and later.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/arm/bcm2835_peripherals.h"
+#include "hw/misc/bcm2835_mbox_defs.h"
+#include "hw/arm/raspi_platform.h"
+#include "sysemu/char.h"
+
+/* Peripheral base address on the VC (GPU) system bus */
+#define BCM2835_VC_PERI_BASE 0x7e000000
+
+/* Capabilities for SD controller: no DMA, high-speed, default clocks etc. */
+#define BCM2835_SDHC_CAPAREG 0x52034b4
+
+static void bcm2835_peripherals_init(Object *obj)
+{
+ BCM2835PeripheralState *s = BCM2835_PERIPHERALS(obj);
+
+ /* Memory region for peripheral devices, which we export to our parent */
+ memory_region_init(&s->peri_mr, obj,"bcm2835-peripherals", 0x1000000);
+ object_property_add_child(obj, "peripheral-io", OBJECT(&s->peri_mr), NULL);
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->peri_mr);
+
+ /* Internal memory region for peripheral bus addresses (not exported) */
+ memory_region_init(&s->gpu_bus_mr, obj, "bcm2835-gpu", (uint64_t)1 << 32);
+ object_property_add_child(obj, "gpu-bus", OBJECT(&s->gpu_bus_mr), NULL);
+
+ /* Internal memory region for request/response communication with
+ * mailbox-addressable peripherals (not exported)
+ */
+ memory_region_init(&s->mbox_mr, obj, "bcm2835-mbox",
+ MBOX_CHAN_COUNT << MBOX_AS_CHAN_SHIFT);
+
+ /* Interrupt Controller */
+ object_initialize(&s->ic, sizeof(s->ic), TYPE_BCM2835_IC);
+ object_property_add_child(obj, "ic", OBJECT(&s->ic), NULL);
+ qdev_set_parent_bus(DEVICE(&s->ic), sysbus_get_default());
+
+ /* UART0 */
+ s->uart0 = SYS_BUS_DEVICE(object_new("pl011"));
+ object_property_add_child(obj, "uart0", OBJECT(s->uart0), NULL);
+ qdev_set_parent_bus(DEVICE(s->uart0), sysbus_get_default());
+
+ /* AUX / UART1 */
+ object_initialize(&s->aux, sizeof(s->aux), TYPE_BCM2835_AUX);
+ object_property_add_child(obj, "aux", OBJECT(&s->aux), NULL);
+ qdev_set_parent_bus(DEVICE(&s->aux), sysbus_get_default());
+
+ /* Mailboxes */
+ object_initialize(&s->mboxes, sizeof(s->mboxes), TYPE_BCM2835_MBOX);
+ object_property_add_child(obj, "mbox", OBJECT(&s->mboxes), NULL);
+ qdev_set_parent_bus(DEVICE(&s->mboxes), sysbus_get_default());
+
+ object_property_add_const_link(OBJECT(&s->mboxes), "mbox-mr",
+ OBJECT(&s->mbox_mr), &error_abort);
+
+ /* Framebuffer */
+ object_initialize(&s->fb, sizeof(s->fb), TYPE_BCM2835_FB);
+ object_property_add_child(obj, "fb", OBJECT(&s->fb), NULL);
+ object_property_add_alias(obj, "vcram-size", OBJECT(&s->fb), "vcram-size",
+ &error_abort);
+ qdev_set_parent_bus(DEVICE(&s->fb), sysbus_get_default());
+
+ object_property_add_const_link(OBJECT(&s->fb), "dma-mr",
+ OBJECT(&s->gpu_bus_mr), &error_abort);
+
+ /* Property channel */
+ object_initialize(&s->property, sizeof(s->property), TYPE_BCM2835_PROPERTY);
+ object_property_add_child(obj, "property", OBJECT(&s->property), NULL);
+ object_property_add_alias(obj, "board-rev", OBJECT(&s->property),
+ "board-rev", &error_abort);
+ qdev_set_parent_bus(DEVICE(&s->property), sysbus_get_default());
+
+ object_property_add_const_link(OBJECT(&s->property), "fb",
+ OBJECT(&s->fb), &error_abort);
+ object_property_add_const_link(OBJECT(&s->property), "dma-mr",
+ OBJECT(&s->gpu_bus_mr), &error_abort);
+
+ /* Extended Mass Media Controller */
+ object_initialize(&s->sdhci, sizeof(s->sdhci), TYPE_SYSBUS_SDHCI);
+ object_property_add_child(obj, "sdhci", OBJECT(&s->sdhci), NULL);
+ qdev_set_parent_bus(DEVICE(&s->sdhci), sysbus_get_default());
+
+ /* DMA Channels */
+ object_initialize(&s->dma, sizeof(s->dma), TYPE_BCM2835_DMA);
+ object_property_add_child(obj, "dma", OBJECT(&s->dma), NULL);
+ qdev_set_parent_bus(DEVICE(&s->dma), sysbus_get_default());
+
+ object_property_add_const_link(OBJECT(&s->dma), "dma-mr",
+ OBJECT(&s->gpu_bus_mr), &error_abort);
+}
+
+static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
+{
+ BCM2835PeripheralState *s = BCM2835_PERIPHERALS(dev);
+ Object *obj;
+ MemoryRegion *ram;
+ Error *err = NULL;
+ uint32_t ram_size, vcram_size;
+ CharDriverState *chr;
+ int n;
+
+ obj = object_property_get_link(OBJECT(dev), "ram", &err);
+ if (obj == NULL) {
+ error_setg(errp, "%s: required ram link not found: %s",
+ __func__, error_get_pretty(err));
+ return;
+ }
+
+ ram = MEMORY_REGION(obj);
+ ram_size = memory_region_size(ram);
+
+ /* Map peripherals and RAM into the GPU address space. */
+ memory_region_init_alias(&s->peri_mr_alias, OBJECT(s),
+ "bcm2835-peripherals", &s->peri_mr, 0,
+ memory_region_size(&s->peri_mr));
+
+ memory_region_add_subregion_overlap(&s->gpu_bus_mr, BCM2835_VC_PERI_BASE,
+ &s->peri_mr_alias, 1);
+
+ /* RAM is aliased four times (different cache configurations) on the GPU */
+ for (n = 0; n < 4; n++) {
+ memory_region_init_alias(&s->ram_alias[n], OBJECT(s),
+ "bcm2835-gpu-ram-alias[*]", ram, 0, ram_size);
+ memory_region_add_subregion_overlap(&s->gpu_bus_mr, (hwaddr)n << 30,
+ &s->ram_alias[n], 0);
+ }
+
+ /* Interrupt Controller */
+ object_property_set_bool(OBJECT(&s->ic), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ memory_region_add_subregion(&s->peri_mr, ARMCTRL_IC_OFFSET,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->ic), 0));
+ sysbus_pass_irq(SYS_BUS_DEVICE(s), SYS_BUS_DEVICE(&s->ic));
+
+ /* UART0 */
+ object_property_set_bool(OBJECT(s->uart0), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ memory_region_add_subregion(&s->peri_mr, UART0_OFFSET,
+ sysbus_mmio_get_region(s->uart0, 0));
+ sysbus_connect_irq(s->uart0, 0,
+ qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
+ INTERRUPT_UART));
+
+ /* AUX / UART1 */
+ /* TODO: don't call qemu_char_get_next_serial() here, instead set
+ * chardev properties for each uart at the board level, once pl011
+ * (uart0) has been updated to avoid qemu_char_get_next_serial()
+ */
+ chr = qemu_char_get_next_serial();
+ if (chr == NULL) {
+ chr = qemu_chr_new("bcm2835.uart1", "null", NULL);
+ }
+ qdev_prop_set_chr(DEVICE(&s->aux), "chardev", chr);
+
+ object_property_set_bool(OBJECT(&s->aux), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ memory_region_add_subregion(&s->peri_mr, UART1_OFFSET,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->aux), 0));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->aux), 0,
+ qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
+ INTERRUPT_AUX));
+
+ /* Mailboxes */
+ object_property_set_bool(OBJECT(&s->mboxes), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ memory_region_add_subregion(&s->peri_mr, ARMCTRL_0_SBM_OFFSET,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mboxes), 0));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->mboxes), 0,
+ qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_ARM_IRQ,
+ INTERRUPT_ARM_MAILBOX));
+
+ /* Framebuffer */
+ vcram_size = (uint32_t)object_property_get_int(OBJECT(s), "vcram-size",
+ &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ object_property_set_int(OBJECT(&s->fb), ram_size - vcram_size,
+ "vcram-base", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ object_property_set_bool(OBJECT(&s->fb), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ memory_region_add_subregion(&s->mbox_mr, MBOX_CHAN_FB << MBOX_AS_CHAN_SHIFT,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->fb), 0));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->fb), 0,
+ qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_FB));
+
+ /* Property channel */
+ object_property_set_bool(OBJECT(&s->property), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ memory_region_add_subregion(&s->mbox_mr,
+ MBOX_CHAN_PROPERTY << MBOX_AS_CHAN_SHIFT,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->property), 0));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->property), 0,
+ qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_PROPERTY));
+
+ /* Extended Mass Media Controller */
+ object_property_set_int(OBJECT(&s->sdhci), BCM2835_SDHC_CAPAREG, "capareg",
+ &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ object_property_set_bool(OBJECT(&s->sdhci), true, "pending-insert-quirk",
+ &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ object_property_set_bool(OBJECT(&s->sdhci), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ memory_region_add_subregion(&s->peri_mr, EMMC_OFFSET,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->sdhci), 0));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0,
+ qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
+ INTERRUPT_ARASANSDIO));
+ object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->sdhci), "sd-bus",
+ &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ /* DMA Channels */
+ object_property_set_bool(OBJECT(&s->dma), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ memory_region_add_subregion(&s->peri_mr, DMA_OFFSET,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dma), 0));
+ memory_region_add_subregion(&s->peri_mr, DMA15_OFFSET,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dma), 1));
+
+ for (n = 0; n <= 12; n++) {
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->dma), n,
+ qdev_get_gpio_in_named(DEVICE(&s->ic),
+ BCM2835_IC_GPU_IRQ,
+ INTERRUPT_DMA0 + n));
+ }
+}
+
+static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = bcm2835_peripherals_realize;
+ /* Reason: realize() method uses qemu_char_get_next_serial() */
+ dc->cannot_instantiate_with_device_add_yet = true;
+}
+
+static const TypeInfo bcm2835_peripherals_type_info = {
+ .name = TYPE_BCM2835_PERIPHERALS,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BCM2835PeripheralState),
+ .instance_init = bcm2835_peripherals_init,
+ .class_init = bcm2835_peripherals_class_init,
+};
+
+static void bcm2835_peripherals_register_types(void)
+{
+ type_register_static(&bcm2835_peripherals_type_info);
+}
+
+type_init(bcm2835_peripherals_register_types)
diff --git a/qemu/hw/arm/bcm2836.c b/qemu/hw/arm/bcm2836.c
new file mode 100644
index 000000000..8451190a1
--- /dev/null
+++ b/qemu/hw/arm/bcm2836.c
@@ -0,0 +1,184 @@
+/*
+ * Raspberry Pi emulation (c) 2012 Gregory Estrade
+ * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous
+ *
+ * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft
+ * Written by Andrew Baumann
+ *
+ * This code is licensed under the GNU GPLv2 and later.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "hw/arm/bcm2836.h"
+#include "hw/arm/raspi_platform.h"
+#include "hw/sysbus.h"
+#include "exec/address-spaces.h"
+
+/* Peripheral base address seen by the CPU */
+#define BCM2836_PERI_BASE 0x3F000000
+
+/* "QA7" (Pi2) interrupt controller and mailboxes etc. */
+#define BCM2836_CONTROL_BASE 0x40000000
+
+static void bcm2836_init(Object *obj)
+{
+ BCM2836State *s = BCM2836(obj);
+ int n;
+
+ for (n = 0; n < BCM2836_NCPUS; n++) {
+ object_initialize(&s->cpus[n], sizeof(s->cpus[n]),
+ "cortex-a15-" TYPE_ARM_CPU);
+ object_property_add_child(obj, "cpu[*]", OBJECT(&s->cpus[n]),
+ &error_abort);
+ }
+
+ object_initialize(&s->control, sizeof(s->control), TYPE_BCM2836_CONTROL);
+ object_property_add_child(obj, "control", OBJECT(&s->control), NULL);
+ qdev_set_parent_bus(DEVICE(&s->control), sysbus_get_default());
+
+ object_initialize(&s->peripherals, sizeof(s->peripherals),
+ TYPE_BCM2835_PERIPHERALS);
+ object_property_add_child(obj, "peripherals", OBJECT(&s->peripherals),
+ &error_abort);
+ object_property_add_alias(obj, "board-rev", OBJECT(&s->peripherals),
+ "board-rev", &error_abort);
+ object_property_add_alias(obj, "vcram-size", OBJECT(&s->peripherals),
+ "vcram-size", &error_abort);
+ qdev_set_parent_bus(DEVICE(&s->peripherals), sysbus_get_default());
+}
+
+static void bcm2836_realize(DeviceState *dev, Error **errp)
+{
+ BCM2836State *s = BCM2836(dev);
+ Object *obj;
+ Error *err = NULL;
+ int n;
+
+ /* common peripherals from bcm2835 */
+
+ obj = object_property_get_link(OBJECT(dev), "ram", &err);
+ if (obj == NULL) {
+ error_setg(errp, "%s: required ram link not found: %s",
+ __func__, error_get_pretty(err));
+ return;
+ }
+
+ object_property_add_const_link(OBJECT(&s->peripherals), "ram", obj, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ object_property_set_bool(OBJECT(&s->peripherals), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->peripherals),
+ "sd-bus", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->peripherals), 0,
+ BCM2836_PERI_BASE, 1);
+
+ /* bcm2836 interrupt controller (and mailboxes, etc.) */
+ object_property_set_bool(OBJECT(&s->control), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->control), 0, BCM2836_CONTROL_BASE);
+
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 0,
+ qdev_get_gpio_in_named(DEVICE(&s->control), "gpu-irq", 0));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 1,
+ qdev_get_gpio_in_named(DEVICE(&s->control), "gpu-fiq", 0));
+
+ for (n = 0; n < BCM2836_NCPUS; n++) {
+ /* Mirror bcm2836, which has clusterid set to 0xf
+ * TODO: this should be converted to a property of ARM_CPU
+ */
+ s->cpus[n].mp_affinity = 0xF00 | n;
+
+ /* set periphbase/CBAR value for CPU-local registers */
+ object_property_set_int(OBJECT(&s->cpus[n]),
+ BCM2836_PERI_BASE + MCORE_OFFSET,
+ "reset-cbar", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ /* start powered off if not enabled */
+ object_property_set_bool(OBJECT(&s->cpus[n]), n >= s->enabled_cpus,
+ "start-powered-off", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ object_property_set_bool(OBJECT(&s->cpus[n]), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ /* Connect irq/fiq outputs from the interrupt controller. */
+ qdev_connect_gpio_out_named(DEVICE(&s->control), "irq", n,
+ qdev_get_gpio_in(DEVICE(&s->cpus[n]), ARM_CPU_IRQ));
+ qdev_connect_gpio_out_named(DEVICE(&s->control), "fiq", n,
+ qdev_get_gpio_in(DEVICE(&s->cpus[n]), ARM_CPU_FIQ));
+
+ /* Connect timers from the CPU to the interrupt controller */
+ qdev_connect_gpio_out(DEVICE(&s->cpus[n]), GTIMER_PHYS,
+ qdev_get_gpio_in_named(DEVICE(&s->control), "cntpnsirq", n));
+ qdev_connect_gpio_out(DEVICE(&s->cpus[n]), GTIMER_VIRT,
+ qdev_get_gpio_in_named(DEVICE(&s->control), "cntvirq", n));
+ qdev_connect_gpio_out(DEVICE(&s->cpus[n]), GTIMER_HYP,
+ qdev_get_gpio_in_named(DEVICE(&s->control), "cnthpirq", n));
+ qdev_connect_gpio_out(DEVICE(&s->cpus[n]), GTIMER_SEC,
+ qdev_get_gpio_in_named(DEVICE(&s->control), "cntpsirq", n));
+ }
+}
+
+static Property bcm2836_props[] = {
+ DEFINE_PROP_UINT32("enabled-cpus", BCM2836State, enabled_cpus, BCM2836_NCPUS),
+ DEFINE_PROP_END_OF_LIST()
+};
+
+static void bcm2836_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->props = bcm2836_props;
+ dc->realize = bcm2836_realize;
+
+ /*
+ * Reason: creates an ARM CPU, thus use after free(), see
+ * arm_cpu_class_init()
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
+}
+
+static const TypeInfo bcm2836_type_info = {
+ .name = TYPE_BCM2836,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BCM2836State),
+ .instance_init = bcm2836_init,
+ .class_init = bcm2836_class_init,
+};
+
+static void bcm2836_register_types(void)
+{
+ type_register_static(&bcm2836_type_info);
+}
+
+type_init(bcm2836_register_types)
diff --git a/qemu/hw/arm/boot.c b/qemu/hw/arm/boot.c
index 5b969cda1..587694557 100644
--- a/qemu/hw/arm/boot.c
+++ b/qemu/hw/arm/boot.c
@@ -7,9 +7,12 @@
* This code is licensed under the GPL.
*/
-#include "config.h"
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/arm/arm.h"
+#include "hw/arm/linux-boot-if.h"
+#include "sysemu/kvm.h"
#include "sysemu/sysemu.h"
#include "hw/boards.h"
#include "hw/loader.h"
@@ -27,14 +30,15 @@
#define KERNEL64_LOAD_ADDR 0x00080000
typedef enum {
- FIXUP_NONE = 0, /* do nothing */
- FIXUP_TERMINATOR, /* end of insns */
- FIXUP_BOARDID, /* overwrite with board ID number */
- FIXUP_ARGPTR, /* overwrite with pointer to kernel args */
- FIXUP_ENTRYPOINT, /* overwrite with kernel entry point */
- FIXUP_GIC_CPU_IF, /* overwrite with GIC CPU interface address */
- FIXUP_BOOTREG, /* overwrite with boot register address */
- FIXUP_DSB, /* overwrite with correct DSB insn for cpu */
+ FIXUP_NONE = 0, /* do nothing */
+ FIXUP_TERMINATOR, /* end of insns */
+ FIXUP_BOARDID, /* overwrite with board ID number */
+ FIXUP_BOARD_SETUP, /* overwrite with board specific setup code address */
+ FIXUP_ARGPTR, /* overwrite with pointer to kernel args */
+ FIXUP_ENTRYPOINT, /* overwrite with kernel entry point */
+ FIXUP_GIC_CPU_IF, /* overwrite with GIC CPU interface address */
+ FIXUP_BOOTREG, /* overwrite with boot register address */
+ FIXUP_DSB, /* overwrite with correct DSB insn for cpu */
FIXUP_MAX,
} FixupType;
@@ -57,8 +61,17 @@ static const ARMInsnFixup bootloader_aarch64[] = {
{ 0, FIXUP_TERMINATOR }
};
-/* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */
+/* A very small bootloader: call the board-setup code (if needed),
+ * set r0-r2, then jump to the kernel.
+ * If we're not calling boot setup code then we don't copy across
+ * the first BOOTLOADER_NO_BOARD_SETUP_OFFSET insns in this array.
+ */
+
static const ARMInsnFixup bootloader[] = {
+ { 0xe28fe004 }, /* add lr, pc, #4 */
+ { 0xe51ff004 }, /* ldr pc, [pc, #-4] */
+ { 0, FIXUP_BOARD_SETUP },
+#define BOOTLOADER_NO_BOARD_SETUP_OFFSET 3
{ 0xe3a00000 }, /* mov r0, #0 */
{ 0xe59f1004 }, /* ldr r1, [pc, #4] */
{ 0xe59f2004 }, /* ldr r2, [pc, #4] */
@@ -130,6 +143,7 @@ static void write_bootloader(const char *name, hwaddr addr,
case FIXUP_NONE:
break;
case FIXUP_BOARDID:
+ case FIXUP_BOARD_SETUP:
case FIXUP_ARGPTR:
case FIXUP_ENTRYPOINT:
case FIXUP_GIC_CPU_IF:
@@ -165,6 +179,57 @@ static void default_write_secondary(ARMCPU *cpu,
smpboot, fixupcontext);
}
+void arm_write_secure_board_setup_dummy_smc(ARMCPU *cpu,
+ const struct arm_boot_info *info,
+ hwaddr mvbar_addr)
+{
+ int n;
+ uint32_t mvbar_blob[] = {
+ /* mvbar_addr: secure monitor vectors
+ * Default unimplemented and unused vectors to spin. Makes it
+ * easier to debug (as opposed to the CPU running away).
+ */
+ 0xeafffffe, /* (spin) */
+ 0xeafffffe, /* (spin) */
+ 0xe1b0f00e, /* movs pc, lr ;SMC exception return */
+ 0xeafffffe, /* (spin) */
+ 0xeafffffe, /* (spin) */
+ 0xeafffffe, /* (spin) */
+ 0xeafffffe, /* (spin) */
+ 0xeafffffe, /* (spin) */
+ };
+ uint32_t board_setup_blob[] = {
+ /* board setup addr */
+ 0xe3a00e00 + (mvbar_addr >> 4), /* mov r0, #mvbar_addr */
+ 0xee0c0f30, /* mcr p15, 0, r0, c12, c0, 1 ;set MVBAR */
+ 0xee110f11, /* mrc p15, 0, r0, c1 , c1, 0 ;read SCR */
+ 0xe3800031, /* orr r0, #0x31 ;enable AW, FW, NS */
+ 0xee010f11, /* mcr p15, 0, r0, c1, c1, 0 ;write SCR */
+ 0xe1a0100e, /* mov r1, lr ;save LR across SMC */
+ 0xe1600070, /* smc #0 ;call monitor to flush SCR */
+ 0xe1a0f001, /* mov pc, r1 ;return */
+ };
+
+ /* check that mvbar_addr is correctly aligned and relocatable (using MOV) */
+ assert((mvbar_addr & 0x1f) == 0 && (mvbar_addr >> 4) < 0x100);
+
+ /* check that these blobs don't overlap */
+ assert((mvbar_addr + sizeof(mvbar_blob) <= info->board_setup_addr)
+ || (info->board_setup_addr + sizeof(board_setup_blob) <= mvbar_addr));
+
+ for (n = 0; n < ARRAY_SIZE(mvbar_blob); n++) {
+ mvbar_blob[n] = tswap32(mvbar_blob[n]);
+ }
+ rom_add_blob_fixed("board-setup-mvbar", mvbar_blob, sizeof(mvbar_blob),
+ mvbar_addr);
+
+ for (n = 0; n < ARRAY_SIZE(board_setup_blob); n++) {
+ board_setup_blob[n] = tswap32(board_setup_blob[n]);
+ }
+ rom_add_blob_fixed("board-setup", board_setup_blob,
+ sizeof(board_setup_blob), info->board_setup_addr);
+}
+
static void default_reset_secondary(ARMCPU *cpu,
const struct arm_boot_info *info)
{
@@ -373,8 +438,10 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
return 0;
}
- acells = qemu_fdt_getprop_cell(fdt, "/", "#address-cells");
- scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells");
+ acells = qemu_fdt_getprop_cell(fdt, "/", "#address-cells",
+ NULL, &error_fatal);
+ scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells",
+ NULL, &error_fatal);
if (acells == 0 || scells == 0) {
fprintf(stderr, "dtb file invalid (#address-cells or #size-cells 0)\n");
goto fail;
@@ -452,9 +519,34 @@ static void do_cpu_reset(void *opaque)
cpu_reset(cs);
if (info) {
if (!info->is_linux) {
+ int i;
/* Jump to the entry point. */
uint64_t entry = info->entry;
+ switch (info->endianness) {
+ case ARM_ENDIANNESS_LE:
+ env->cp15.sctlr_el[1] &= ~SCTLR_E0E;
+ for (i = 1; i < 4; ++i) {
+ env->cp15.sctlr_el[i] &= ~SCTLR_EE;
+ }
+ env->uncached_cpsr &= ~CPSR_E;
+ break;
+ case ARM_ENDIANNESS_BE8:
+ env->cp15.sctlr_el[1] |= SCTLR_E0E;
+ for (i = 1; i < 4; ++i) {
+ env->cp15.sctlr_el[i] |= SCTLR_EE;
+ }
+ env->uncached_cpsr |= CPSR_E;
+ break;
+ case ARM_ENDIANNESS_BE32:
+ env->cp15.sctlr_el[1] |= SCTLR_B;
+ break;
+ case ARM_ENDIANNESS_UNKNOWN:
+ break; /* Board's decision */
+ default:
+ g_assert_not_reached();
+ }
+
if (!env->aarch64) {
env->thumb = info->entry & 1;
entry &= 0xfffffffe;
@@ -475,7 +567,9 @@ static void do_cpu_reset(void *opaque)
* adjust.
*/
if (env->aarch64) {
+ env->cp15.scr_el3 |= SCR_RW;
if (arm_feature(env, ARM_FEATURE_EL2)) {
+ env->cp15.hcr_el2 |= HCR_RW;
env->pstate = PSTATE_MODE_EL2h;
} else {
env->pstate = PSTATE_MODE_EL1h;
@@ -483,7 +577,8 @@ static void do_cpu_reset(void *opaque)
}
/* Set to non-secure if not a secure boot */
- if (!info->secure_boot) {
+ if (!info->secure_boot &&
+ (cs != first_cpu || !info->secure_board_setup)) {
/* Linux expects non-secure state */
env->cp15.scr_el3 |= SCR_NS;
}
@@ -555,6 +650,76 @@ static void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key,
fw_cfg_add_bytes(fw_cfg, data_key, data, size);
}
+static int do_arm_linux_init(Object *obj, void *opaque)
+{
+ if (object_dynamic_cast(obj, TYPE_ARM_LINUX_BOOT_IF)) {
+ ARMLinuxBootIf *albif = ARM_LINUX_BOOT_IF(obj);
+ ARMLinuxBootIfClass *albifc = ARM_LINUX_BOOT_IF_GET_CLASS(obj);
+ struct arm_boot_info *info = opaque;
+
+ if (albifc->arm_linux_init) {
+ albifc->arm_linux_init(albif, info->secure_boot);
+ }
+ }
+ return 0;
+}
+
+static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry,
+ uint64_t *lowaddr, uint64_t *highaddr,
+ int elf_machine)
+{
+ bool elf_is64;
+ union {
+ Elf32_Ehdr h32;
+ Elf64_Ehdr h64;
+ } elf_header;
+ int data_swab = 0;
+ bool big_endian;
+ uint64_t ret = -1;
+ Error *err = NULL;
+
+
+ load_elf_hdr(info->kernel_filename, &elf_header, &elf_is64, &err);
+ if (err) {
+ return ret;
+ }
+
+ if (elf_is64) {
+ big_endian = elf_header.h64.e_ident[EI_DATA] == ELFDATA2MSB;
+ info->endianness = big_endian ? ARM_ENDIANNESS_BE8
+ : ARM_ENDIANNESS_LE;
+ } else {
+ big_endian = elf_header.h32.e_ident[EI_DATA] == ELFDATA2MSB;
+ if (big_endian) {
+ if (bswap32(elf_header.h32.e_flags) & EF_ARM_BE8) {
+ info->endianness = ARM_ENDIANNESS_BE8;
+ } else {
+ info->endianness = ARM_ENDIANNESS_BE32;
+ /* In BE32, the CPU has a different view of the per-byte
+ * address map than the rest of the system. BE32 ELF files
+ * are organised such that they can be programmed through
+ * the CPU's per-word byte-reversed view of the world. QEMU
+ * however loads ELF files independently of the CPU. So
+ * tell the ELF loader to byte reverse the data for us.
+ */
+ data_swab = 2;
+ }
+ } else {
+ info->endianness = ARM_ENDIANNESS_LE;
+ }
+ }
+
+ ret = load_elf(info->kernel_filename, NULL, NULL,
+ pentry, lowaddr, highaddr, big_endian, elf_machine,
+ 1, data_swab);
+ if (ret <= 0) {
+ /* The header loaded but the image didn't */
+ exit(1);
+ }
+
+ return ret;
+}
+
static void arm_load_kernel_notify(Notifier *notifier, void *data)
{
CPUState *cs;
@@ -564,7 +729,6 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
uint64_t elf_entry, elf_low_addr, elf_high_addr;
int elf_machine;
hwaddr entry, kernel_load_offset;
- int big_endian;
static const ARMInsnFixup *primary_loader;
ArmLoadKernelNotifier *n = DO_UPCAST(ArmLoadKernelNotifier,
notifier, notifier);
@@ -572,6 +736,12 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
struct arm_boot_info *info =
container_of(n, struct arm_boot_info, load_kernel_notifier);
+ /* The board code is not supposed to set secure_board_setup unless
+ * running its code in secure mode is actually possible, and KVM
+ * doesn't support secure.
+ */
+ assert(!(info->secure_board_setup && kvm_enabled()));
+
/* Load the kernel. */
if (!info->kernel_filename || info->firmware_loaded) {
@@ -625,6 +795,9 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
elf_machine = EM_AARCH64;
} else {
primary_loader = bootloader;
+ if (!info->write_board_setup) {
+ primary_loader += BOOTLOADER_NO_BOARD_SETUP_OFFSET;
+ }
kernel_load_offset = KERNEL_LOAD_ADDR;
elf_machine = EM_ARM;
}
@@ -641,12 +814,6 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
if (info->nb_cpus == 0)
info->nb_cpus = 1;
-#ifdef TARGET_WORDS_BIGENDIAN
- big_endian = 1;
-#else
- big_endian = 0;
-#endif
-
/* We want to put the initrd far enough into RAM that when the
* kernel is uncompressed it will not clobber the initrd. However
* on boards without much RAM we must ensure that we still leave
@@ -661,9 +828,8 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
MIN(info->ram_size / 2, 128 * 1024 * 1024);
/* Assume that raw images are linux kernels, and ELF images are not. */
- kernel_size = load_elf(info->kernel_filename, NULL, NULL, &elf_entry,
- &elf_low_addr, &elf_high_addr, big_endian,
- elf_machine, 1);
+ kernel_size = arm_load_elf(info, &elf_entry, &elf_low_addr,
+ &elf_high_addr, elf_machine);
if (kernel_size > 0 && have_dtb(info)) {
/* If there is still some room left at the base of RAM, try and put
* the DTB there like we do for images loaded with -bios or -pflash.
@@ -730,6 +896,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
info->initrd_size = initrd_size;
fixupcontext[FIXUP_BOARDID] = info->board_id;
+ fixupcontext[FIXUP_BOARD_SETUP] = info->board_setup_addr;
/* for device tree boot, we pass the DTB directly in r2. Otherwise
* we point to the kernel args.
@@ -778,6 +945,15 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
if (info->nb_cpus > 1) {
info->write_secondary_boot(cpu, info);
}
+ if (info->write_board_setup) {
+ info->write_board_setup(cpu, info);
+ }
+
+ /* Notify devices which need to fake up firmware initialization
+ * that we're doing a direct kernel boot.
+ */
+ object_child_foreach_recursive(object_get_root(),
+ do_arm_linux_init, info);
}
info->is_linux = is_linux;
@@ -803,3 +979,16 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
qemu_register_reset(do_cpu_reset, ARM_CPU(cs));
}
}
+
+static const TypeInfo arm_linux_boot_if_info = {
+ .name = TYPE_ARM_LINUX_BOOT_IF,
+ .parent = TYPE_INTERFACE,
+ .class_size = sizeof(ARMLinuxBootIfClass),
+};
+
+static void arm_linux_boot_register_types(void)
+{
+ type_register_static(&arm_linux_boot_if_info);
+}
+
+type_init(arm_linux_boot_register_types)
diff --git a/qemu/hw/arm/collie.c b/qemu/hw/arm/collie.c
index 6c9b82fc5..8bb308a42 100644
--- a/qemu/hw/arm/collie.c
+++ b/qemu/hw/arm/collie.c
@@ -8,6 +8,7 @@
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "hw/boards.h"
@@ -58,15 +59,10 @@ static void collie_init(MachineState *machine)
arm_load_kernel(s->cpu, &collie_binfo);
}
-static QEMUMachine collie_machine = {
- .name = "collie",
- .desc = "Collie PDA (SA-1110)",
- .init = collie_init,
-};
-
-static void collie_machine_init(void)
+static void collie_machine_init(MachineClass *mc)
{
- qemu_register_machine(&collie_machine);
+ mc->desc = "Sharp SL-5500 (Collie) PDA (SA-1110)";
+ mc->init = collie_init;
}
-machine_init(collie_machine_init)
+DEFINE_MACHINE("collie", collie_machine_init)
diff --git a/qemu/hw/arm/cubieboard.c b/qemu/hw/arm/cubieboard.c
index 1582250eb..fbd78ed01 100644
--- a/qemu/hw/arm/cubieboard.c
+++ b/qemu/hw/arm/cubieboard.c
@@ -15,6 +15,10 @@
* for more details.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/sysbus.h"
#include "hw/devices.h"
#include "hw/boards.h"
@@ -39,27 +43,26 @@ static void cubieboard_init(MachineState *machine)
object_property_set_int(OBJECT(&s->a10->emac), 1, "phy-addr", &err);
if (err != NULL) {
- error_report("Couldn't set phy address: %s", error_get_pretty(err));
+ error_reportf_err(err, "Couldn't set phy address: ");
exit(1);
}
object_property_set_int(OBJECT(&s->a10->timer), 32768, "clk0-freq", &err);
if (err != NULL) {
- error_report("Couldn't set clk0 frequency: %s", error_get_pretty(err));
+ error_reportf_err(err, "Couldn't set clk0 frequency: ");
exit(1);
}
object_property_set_int(OBJECT(&s->a10->timer), 24000000, "clk1-freq",
&err);
if (err != NULL) {
- error_report("Couldn't set clk1 frequency: %s", error_get_pretty(err));
+ error_reportf_err(err, "Couldn't set clk1 frequency: ");
exit(1);
}
object_property_set_bool(OBJECT(s->a10), true, "realized", &err);
if (err != NULL) {
- error_report("Couldn't realize Allwinner A10: %s",
- error_get_pretty(err));
+ error_reportf_err(err, "Couldn't realize Allwinner A10: ");
exit(1);
}
@@ -74,16 +77,10 @@ static void cubieboard_init(MachineState *machine)
arm_load_kernel(&s->a10->cpu, &cubieboard_binfo);
}
-static QEMUMachine cubieboard_machine = {
- .name = "cubieboard",
- .desc = "cubietech cubieboard",
- .init = cubieboard_init,
-};
-
-
-static void cubieboard_machine_init(void)
+static void cubieboard_machine_init(MachineClass *mc)
{
- qemu_register_machine(&cubieboard_machine);
+ mc->desc = "cubietech cubieboard";
+ mc->init = cubieboard_init;
}
-machine_init(cubieboard_machine_init)
+DEFINE_MACHINE("cubieboard", cubieboard_machine_init)
diff --git a/qemu/hw/arm/digic.c b/qemu/hw/arm/digic.c
index ec8c33060..e0f973032 100644
--- a/qemu/hw/arm/digic.c
+++ b/qemu/hw/arm/digic.c
@@ -20,6 +20,8 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/arm/digic.h"
#define DIGIC4_TIMER_BASE(n) (0xc0210000 + (n) * 0x100)
@@ -97,6 +99,12 @@ static void digic_class_init(ObjectClass *oc, void *data)
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = digic_realize;
+
+ /*
+ * Reason: creates an ARM CPU, thus use after free(), see
+ * arm_cpu_class_init()
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo digic_type_info = {
diff --git a/qemu/hw/arm/digic_boards.c b/qemu/hw/arm/digic_boards.c
index f8ba9e595..520c8e9ff 100644
--- a/qemu/hw/arm/digic_boards.c
+++ b/qemu/hw/arm/digic_boards.c
@@ -23,6 +23,10 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/boards.h"
#include "exec/address-spaces.h"
#include "qemu/error-report.h"
@@ -64,8 +68,7 @@ static void digic4_board_init(DigicBoard *board)
s->digic = DIGIC(object_new(TYPE_DIGIC));
object_property_set_bool(OBJECT(s->digic), true, "realized", &err);
if (err != NULL) {
- error_report("Couldn't realize DIGIC SoC: %s",
- error_get_pretty(err));
+ error_reportf_err(err, "Couldn't realize DIGIC SoC: ");
exit(1);
}
@@ -148,15 +151,10 @@ static void canon_a1100_init(MachineState *machine)
digic4_board_init(&digic4_board_canon_a1100);
}
-static QEMUMachine canon_a1100 = {
- .name = "canon-a1100",
- .desc = "Canon PowerShot A1100 IS",
- .init = &canon_a1100_init,
-};
-
-static void digic_register_machines(void)
+static void canon_a1100_machine_init(MachineClass *mc)
{
- qemu_register_machine(&canon_a1100);
+ mc->desc = "Canon PowerShot A1100 IS";
+ mc->init = &canon_a1100_init;
}
-machine_init(digic_register_machines)
+DEFINE_MACHINE("canon-a1100", canon_a1100_machine_init)
diff --git a/qemu/hw/arm/exynos4210.c b/qemu/hw/arm/exynos4210.c
index c55fab813..be3c96d21 100644
--- a/qemu/hw/arm/exynos4210.c
+++ b/qemu/hw/arm/exynos4210.c
@@ -21,6 +21,10 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/boards.h"
#include "sysemu/sysemu.h"
#include "hw/sysbus.h"
@@ -150,27 +154,18 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
for (n = 0; n < EXYNOS4210_NCPUS; n++) {
Object *cpuobj = object_new(object_class_get_name(cpu_oc));
- Error *err = NULL;
/* By default A9 CPUs have EL3 enabled. This board does not currently
* support EL3 so the CPU EL3 property is disabled before realization.
*/
if (object_property_find(cpuobj, "has_el3", NULL)) {
- object_property_set_bool(cpuobj, false, "has_el3", &err);
- if (err) {
- error_report_err(err);
- exit(1);
- }
+ object_property_set_bool(cpuobj, false, "has_el3", &error_fatal);
}
s->cpu[n] = ARM_CPU(cpuobj);
object_property_set_int(cpuobj, EXYNOS4210_SMP_PRIVATE_BASE_ADDR,
"reset-cbar", &error_abort);
- object_property_set_bool(cpuobj, true, "realized", &err);
- if (err) {
- error_report_err(err);
- exit(1);
- }
+ object_property_set_bool(cpuobj, true, "realized", &error_fatal);
}
/*** IRQs ***/
@@ -259,7 +254,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
/* Internal ROM */
memory_region_init_ram(&s->irom_mem, NULL, "exynos4210.irom",
- EXYNOS4210_IROM_SIZE, &error_abort);
+ EXYNOS4210_IROM_SIZE, &error_fatal);
vmstate_register_ram_global(&s->irom_mem);
memory_region_set_readonly(&s->irom_mem, true);
memory_region_add_subregion(system_mem, EXYNOS4210_IROM_BASE_ADDR,
@@ -275,7 +270,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
/* Internal RAM */
memory_region_init_ram(&s->iram_mem, NULL, "exynos4210.iram",
- EXYNOS4210_IRAM_SIZE, &error_abort);
+ EXYNOS4210_IRAM_SIZE, &error_fatal);
vmstate_register_ram_global(&s->iram_mem);
memory_region_add_subregion(system_mem, EXYNOS4210_IRAM_BASE_ADDR,
&s->iram_mem);
@@ -284,14 +279,14 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
mem_size = ram_size;
if (mem_size > EXYNOS4210_DRAM_MAX_SIZE) {
memory_region_init_ram(&s->dram1_mem, NULL, "exynos4210.dram1",
- mem_size - EXYNOS4210_DRAM_MAX_SIZE, &error_abort);
+ mem_size - EXYNOS4210_DRAM_MAX_SIZE, &error_fatal);
vmstate_register_ram_global(&s->dram1_mem);
memory_region_add_subregion(system_mem, EXYNOS4210_DRAM1_BASE_ADDR,
&s->dram1_mem);
mem_size = EXYNOS4210_DRAM_MAX_SIZE;
}
memory_region_init_ram(&s->dram0_mem, NULL, "exynos4210.dram0", mem_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&s->dram0_mem);
memory_region_add_subregion(system_mem, EXYNOS4210_DRAM0_BASE_ADDR,
&s->dram0_mem);
diff --git a/qemu/hw/arm/exynos4_boards.c b/qemu/hw/arm/exynos4_boards.c
index d644db1ef..0efa19405 100644
--- a/qemu/hw/arm/exynos4_boards.c
+++ b/qemu/hw/arm/exynos4_boards.c
@@ -21,6 +21,9 @@
*
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "sysemu/sysemu.h"
#include "sysemu/qtest.h"
#include "hw/sysbus.h"
@@ -74,8 +77,6 @@ static struct arm_boot_info exynos4_board_binfo = {
.write_secondary_boot = exynos4210_write_secondary,
};
-static QEMUMachine exynos4_machines[EXYNOS4_NUM_OF_BOARDS];
-
static void lan9215_init(uint32_t base, qemu_irq irq)
{
DeviceState *dev;
@@ -97,11 +98,12 @@ static void lan9215_init(uint32_t base, qemu_irq irq)
static Exynos4210State *exynos4_boards_init_common(MachineState *machine,
Exynos4BoardType board_type)
{
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
+
if (smp_cpus != EXYNOS4210_NCPUS && !qtest_enabled()) {
fprintf(stderr, "%s board supports only %d CPU cores. Ignoring smp_cpus"
" value.\n",
- exynos4_machines[board_type].name,
- exynos4_machines[board_type].max_cpus);
+ mc->name, EXYNOS4210_NCPUS);
}
exynos4_board_binfo.ram_size = exynos4_board_ram_size[board_type];
@@ -145,25 +147,40 @@ static void smdkc210_init(MachineState *machine)
arm_load_kernel(ARM_CPU(first_cpu), &exynos4_board_binfo);
}
-static QEMUMachine exynos4_machines[EXYNOS4_NUM_OF_BOARDS] = {
- [EXYNOS4_BOARD_NURI] = {
- .name = "nuri",
- .desc = "Samsung NURI board (Exynos4210)",
- .init = nuri_init,
- .max_cpus = EXYNOS4210_NCPUS,
- },
- [EXYNOS4_BOARD_SMDKC210] = {
- .name = "smdkc210",
- .desc = "Samsung SMDKC210 board (Exynos4210)",
- .init = smdkc210_init,
- .max_cpus = EXYNOS4210_NCPUS,
- },
+static void nuri_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Samsung NURI board (Exynos4210)";
+ mc->init = nuri_init;
+ mc->max_cpus = EXYNOS4210_NCPUS;
+}
+
+static const TypeInfo nuri_type = {
+ .name = MACHINE_TYPE_NAME("nuri"),
+ .parent = TYPE_MACHINE,
+ .class_init = nuri_class_init,
+};
+
+static void smdkc210_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Samsung SMDKC210 board (Exynos4210)";
+ mc->init = smdkc210_init;
+ mc->max_cpus = EXYNOS4210_NCPUS;
+}
+
+static const TypeInfo smdkc210_type = {
+ .name = MACHINE_TYPE_NAME("smdkc210"),
+ .parent = TYPE_MACHINE,
+ .class_init = smdkc210_class_init,
};
-static void exynos4_machine_init(void)
+static void exynos4_machines_init(void)
{
- qemu_register_machine(&exynos4_machines[EXYNOS4_BOARD_NURI]);
- qemu_register_machine(&exynos4_machines[EXYNOS4_BOARD_SMDKC210]);
+ type_register_static(&nuri_type);
+ type_register_static(&smdkc210_type);
}
-machine_init(exynos4_machine_init);
+type_init(exynos4_machines_init)
diff --git a/qemu/hw/arm/fsl-imx25.c b/qemu/hw/arm/fsl-imx25.c
new file mode 100644
index 000000000..2f878b935
--- /dev/null
+++ b/qemu/hw/arm/fsl-imx25.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2013 Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * i.MX25 SOC emulation.
+ *
+ * Based on hw/arm/xlnx-zynqmp.c
+ *
+ * Copyright (C) 2015 Xilinx Inc
+ * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "hw/arm/fsl-imx25.h"
+#include "sysemu/sysemu.h"
+#include "exec/address-spaces.h"
+#include "hw/boards.h"
+#include "sysemu/char.h"
+
+static void fsl_imx25_init(Object *obj)
+{
+ FslIMX25State *s = FSL_IMX25(obj);
+ int i;
+
+ object_initialize(&s->cpu, sizeof(s->cpu), "arm926-" TYPE_ARM_CPU);
+
+ object_initialize(&s->avic, sizeof(s->avic), TYPE_IMX_AVIC);
+ qdev_set_parent_bus(DEVICE(&s->avic), sysbus_get_default());
+
+ object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX25_CCM);
+ qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default());
+
+ for (i = 0; i < FSL_IMX25_NUM_UARTS; i++) {
+ object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_IMX_SERIAL);
+ qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default());
+ }
+
+ for (i = 0; i < FSL_IMX25_NUM_GPTS; i++) {
+ object_initialize(&s->gpt[i], sizeof(s->gpt[i]), TYPE_IMX_GPT);
+ qdev_set_parent_bus(DEVICE(&s->gpt[i]), sysbus_get_default());
+ }
+
+ for (i = 0; i < FSL_IMX25_NUM_EPITS; i++) {
+ object_initialize(&s->epit[i], sizeof(s->epit[i]), TYPE_IMX_EPIT);
+ qdev_set_parent_bus(DEVICE(&s->epit[i]), sysbus_get_default());
+ }
+
+ object_initialize(&s->fec, sizeof(s->fec), TYPE_IMX_FEC);
+ qdev_set_parent_bus(DEVICE(&s->fec), sysbus_get_default());
+
+ for (i = 0; i < FSL_IMX25_NUM_I2CS; i++) {
+ object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C);
+ qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default());
+ }
+
+ for (i = 0; i < FSL_IMX25_NUM_GPIOS; i++) {
+ object_initialize(&s->gpio[i], sizeof(s->gpio[i]), TYPE_IMX_GPIO);
+ qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus_get_default());
+ }
+}
+
+static void fsl_imx25_realize(DeviceState *dev, Error **errp)
+{
+ FslIMX25State *s = FSL_IMX25(dev);
+ uint8_t i;
+ Error *err = NULL;
+
+ object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ object_property_set_bool(OBJECT(&s->avic), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->avic), 0, FSL_IMX25_AVIC_ADDR);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->avic), 0,
+ qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->avic), 1,
+ qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_FIQ));
+
+ object_property_set_bool(OBJECT(&s->ccm), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, FSL_IMX25_CCM_ADDR);
+
+ /* Initialize all UARTs */
+ for (i = 0; i < FSL_IMX25_NUM_UARTS; i++) {
+ static const struct {
+ hwaddr addr;
+ unsigned int irq;
+ } serial_table[FSL_IMX25_NUM_UARTS] = {
+ { FSL_IMX25_UART1_ADDR, FSL_IMX25_UART1_IRQ },
+ { FSL_IMX25_UART2_ADDR, FSL_IMX25_UART2_IRQ },
+ { FSL_IMX25_UART3_ADDR, FSL_IMX25_UART3_IRQ },
+ { FSL_IMX25_UART4_ADDR, FSL_IMX25_UART4_IRQ },
+ { FSL_IMX25_UART5_ADDR, FSL_IMX25_UART5_IRQ }
+ };
+
+ if (i < MAX_SERIAL_PORTS) {
+ CharDriverState *chr;
+
+ chr = serial_hds[i];
+
+ if (!chr) {
+ char label[20];
+ snprintf(label, sizeof(label), "imx31.uart%d", i);
+ chr = qemu_chr_new(label, "null", NULL);
+ }
+
+ qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", chr);
+ }
+
+ object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, serial_table[i].addr);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0,
+ qdev_get_gpio_in(DEVICE(&s->avic),
+ serial_table[i].irq));
+ }
+
+ /* Initialize all GPT timers */
+ for (i = 0; i < FSL_IMX25_NUM_GPTS; i++) {
+ static const struct {
+ hwaddr addr;
+ unsigned int irq;
+ } gpt_table[FSL_IMX25_NUM_GPTS] = {
+ { FSL_IMX25_GPT1_ADDR, FSL_IMX25_GPT1_IRQ },
+ { FSL_IMX25_GPT2_ADDR, FSL_IMX25_GPT2_IRQ },
+ { FSL_IMX25_GPT3_ADDR, FSL_IMX25_GPT3_IRQ },
+ { FSL_IMX25_GPT4_ADDR, FSL_IMX25_GPT4_IRQ }
+ };
+
+ s->gpt[i].ccm = IMX_CCM(&s->ccm);
+
+ object_property_set_bool(OBJECT(&s->gpt[i]), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt[i]), 0, gpt_table[i].addr);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt[i]), 0,
+ qdev_get_gpio_in(DEVICE(&s->avic),
+ gpt_table[i].irq));
+ }
+
+ /* Initialize all EPIT timers */
+ for (i = 0; i < FSL_IMX25_NUM_EPITS; i++) {
+ static const struct {
+ hwaddr addr;
+ unsigned int irq;
+ } epit_table[FSL_IMX25_NUM_EPITS] = {
+ { FSL_IMX25_EPIT1_ADDR, FSL_IMX25_EPIT1_IRQ },
+ { FSL_IMX25_EPIT2_ADDR, FSL_IMX25_EPIT2_IRQ }
+ };
+
+ s->epit[i].ccm = IMX_CCM(&s->ccm);
+
+ object_property_set_bool(OBJECT(&s->epit[i]), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->epit[i]), 0, epit_table[i].addr);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->epit[i]), 0,
+ qdev_get_gpio_in(DEVICE(&s->avic),
+ epit_table[i].irq));
+ }
+
+ qdev_set_nic_properties(DEVICE(&s->fec), &nd_table[0]);
+ object_property_set_bool(OBJECT(&s->fec), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->fec), 0, FSL_IMX25_FEC_ADDR);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->fec), 0,
+ qdev_get_gpio_in(DEVICE(&s->avic), FSL_IMX25_FEC_IRQ));
+
+
+ /* Initialize all I2C */
+ for (i = 0; i < FSL_IMX25_NUM_I2CS; i++) {
+ static const struct {
+ hwaddr addr;
+ unsigned int irq;
+ } i2c_table[FSL_IMX25_NUM_I2CS] = {
+ { FSL_IMX25_I2C1_ADDR, FSL_IMX25_I2C1_IRQ },
+ { FSL_IMX25_I2C2_ADDR, FSL_IMX25_I2C2_IRQ },
+ { FSL_IMX25_I2C3_ADDR, FSL_IMX25_I2C3_IRQ }
+ };
+
+ object_property_set_bool(OBJECT(&s->i2c[i]), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, i2c_table[i].addr);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0,
+ qdev_get_gpio_in(DEVICE(&s->avic),
+ i2c_table[i].irq));
+ }
+
+ /* Initialize all GPIOs */
+ for (i = 0; i < FSL_IMX25_NUM_GPIOS; i++) {
+ static const struct {
+ hwaddr addr;
+ unsigned int irq;
+ } gpio_table[FSL_IMX25_NUM_GPIOS] = {
+ { FSL_IMX25_GPIO1_ADDR, FSL_IMX25_GPIO1_IRQ },
+ { FSL_IMX25_GPIO2_ADDR, FSL_IMX25_GPIO2_IRQ },
+ { FSL_IMX25_GPIO3_ADDR, FSL_IMX25_GPIO3_IRQ },
+ { FSL_IMX25_GPIO4_ADDR, FSL_IMX25_GPIO4_IRQ }
+ };
+
+ object_property_set_bool(OBJECT(&s->gpio[i]), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, gpio_table[i].addr);
+ /* Connect GPIO IRQ to PIC */
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0,
+ qdev_get_gpio_in(DEVICE(&s->avic),
+ gpio_table[i].irq));
+ }
+
+ /* initialize 2 x 16 KB ROM */
+ memory_region_init_rom_device(&s->rom[0], NULL, NULL, NULL,
+ "imx25.rom0", FSL_IMX25_ROM0_SIZE, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ memory_region_add_subregion(get_system_memory(), FSL_IMX25_ROM0_ADDR,
+ &s->rom[0]);
+ memory_region_init_rom_device(&s->rom[1], NULL, NULL, NULL,
+ "imx25.rom1", FSL_IMX25_ROM1_SIZE, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ memory_region_add_subregion(get_system_memory(), FSL_IMX25_ROM1_ADDR,
+ &s->rom[1]);
+
+ /* initialize internal RAM (128 KB) */
+ memory_region_init_ram(&s->iram, NULL, "imx25.iram", FSL_IMX25_IRAM_SIZE,
+ &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ memory_region_add_subregion(get_system_memory(), FSL_IMX25_IRAM_ADDR,
+ &s->iram);
+ vmstate_register_ram_global(&s->iram);
+
+ /* internal RAM (128 KB) is aliased over 128 MB - 128 KB */
+ memory_region_init_alias(&s->iram_alias, NULL, "imx25.iram_alias",
+ &s->iram, 0, FSL_IMX25_IRAM_ALIAS_SIZE);
+ memory_region_add_subregion(get_system_memory(), FSL_IMX25_IRAM_ALIAS_ADDR,
+ &s->iram_alias);
+}
+
+static void fsl_imx25_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = fsl_imx25_realize;
+
+ /*
+ * Reason: creates an ARM CPU, thus use after free(), see
+ * arm_cpu_class_init()
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
+ dc->desc = "i.MX25 SOC";
+}
+
+static const TypeInfo fsl_imx25_type_info = {
+ .name = TYPE_FSL_IMX25,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(FslIMX25State),
+ .instance_init = fsl_imx25_init,
+ .class_init = fsl_imx25_class_init,
+};
+
+static void fsl_imx25_register_types(void)
+{
+ type_register_static(&fsl_imx25_type_info);
+}
+
+type_init(fsl_imx25_register_types)
diff --git a/qemu/hw/arm/fsl-imx31.c b/qemu/hw/arm/fsl-imx31.c
new file mode 100644
index 000000000..31a3a8791
--- /dev/null
+++ b/qemu/hw/arm/fsl-imx31.c
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 2013 Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * i.MX31 SOC emulation.
+ *
+ * Based on hw/arm/fsl-imx31.c
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "hw/arm/fsl-imx31.h"
+#include "sysemu/sysemu.h"
+#include "exec/address-spaces.h"
+#include "hw/boards.h"
+#include "sysemu/char.h"
+
+static void fsl_imx31_init(Object *obj)
+{
+ FslIMX31State *s = FSL_IMX31(obj);
+ int i;
+
+ object_initialize(&s->cpu, sizeof(s->cpu), "arm1136-" TYPE_ARM_CPU);
+
+ object_initialize(&s->avic, sizeof(s->avic), TYPE_IMX_AVIC);
+ qdev_set_parent_bus(DEVICE(&s->avic), sysbus_get_default());
+
+ object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX31_CCM);
+ qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default());
+
+ for (i = 0; i < FSL_IMX31_NUM_UARTS; i++) {
+ object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_IMX_SERIAL);
+ qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default());
+ }
+
+ object_initialize(&s->gpt, sizeof(s->gpt), TYPE_IMX_GPT);
+ qdev_set_parent_bus(DEVICE(&s->gpt), sysbus_get_default());
+
+ for (i = 0; i < FSL_IMX31_NUM_EPITS; i++) {
+ object_initialize(&s->epit[i], sizeof(s->epit[i]), TYPE_IMX_EPIT);
+ qdev_set_parent_bus(DEVICE(&s->epit[i]), sysbus_get_default());
+ }
+
+ for (i = 0; i < FSL_IMX31_NUM_I2CS; i++) {
+ object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C);
+ qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default());
+ }
+
+ for (i = 0; i < FSL_IMX31_NUM_GPIOS; i++) {
+ object_initialize(&s->gpio[i], sizeof(s->gpio[i]), TYPE_IMX_GPIO);
+ qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus_get_default());
+ }
+}
+
+static void fsl_imx31_realize(DeviceState *dev, Error **errp)
+{
+ FslIMX31State *s = FSL_IMX31(dev);
+ uint16_t i;
+ Error *err = NULL;
+
+ object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ object_property_set_bool(OBJECT(&s->avic), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->avic), 0, FSL_IMX31_AVIC_ADDR);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->avic), 0,
+ qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->avic), 1,
+ qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_FIQ));
+
+ object_property_set_bool(OBJECT(&s->ccm), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, FSL_IMX31_CCM_ADDR);
+
+ /* Initialize all UARTS */
+ for (i = 0; i < FSL_IMX31_NUM_UARTS; i++) {
+ static const struct {
+ hwaddr addr;
+ unsigned int irq;
+ } serial_table[FSL_IMX31_NUM_UARTS] = {
+ { FSL_IMX31_UART1_ADDR, FSL_IMX31_UART1_IRQ },
+ { FSL_IMX31_UART2_ADDR, FSL_IMX31_UART2_IRQ },
+ };
+
+ if (i < MAX_SERIAL_PORTS) {
+ CharDriverState *chr;
+
+ chr = serial_hds[i];
+
+ if (!chr) {
+ char label[20];
+ snprintf(label, sizeof(label), "imx31.uart%d", i);
+ chr = qemu_chr_new(label, "null", NULL);
+ }
+
+ qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", chr);
+ }
+
+ object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, serial_table[i].addr);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0,
+ qdev_get_gpio_in(DEVICE(&s->avic),
+ serial_table[i].irq));
+ }
+
+ s->gpt.ccm = IMX_CCM(&s->ccm);
+
+ object_property_set_bool(OBJECT(&s->gpt), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt), 0, FSL_IMX31_GPT_ADDR);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt), 0,
+ qdev_get_gpio_in(DEVICE(&s->avic), FSL_IMX31_GPT_IRQ));
+
+ /* Initialize all EPIT timers */
+ for (i = 0; i < FSL_IMX31_NUM_EPITS; i++) {
+ static const struct {
+ hwaddr addr;
+ unsigned int irq;
+ } epit_table[FSL_IMX31_NUM_EPITS] = {
+ { FSL_IMX31_EPIT1_ADDR, FSL_IMX31_EPIT1_IRQ },
+ { FSL_IMX31_EPIT2_ADDR, FSL_IMX31_EPIT2_IRQ },
+ };
+
+ s->epit[i].ccm = IMX_CCM(&s->ccm);
+
+ object_property_set_bool(OBJECT(&s->epit[i]), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->epit[i]), 0, epit_table[i].addr);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->epit[i]), 0,
+ qdev_get_gpio_in(DEVICE(&s->avic),
+ epit_table[i].irq));
+ }
+
+ /* Initialize all I2C */
+ for (i = 0; i < FSL_IMX31_NUM_I2CS; i++) {
+ static const struct {
+ hwaddr addr;
+ unsigned int irq;
+ } i2c_table[FSL_IMX31_NUM_I2CS] = {
+ { FSL_IMX31_I2C1_ADDR, FSL_IMX31_I2C1_IRQ },
+ { FSL_IMX31_I2C2_ADDR, FSL_IMX31_I2C2_IRQ },
+ { FSL_IMX31_I2C3_ADDR, FSL_IMX31_I2C3_IRQ }
+ };
+
+ /* Initialize the I2C */
+ object_property_set_bool(OBJECT(&s->i2c[i]), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ /* Map I2C memory */
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, i2c_table[i].addr);
+ /* Connect I2C IRQ to PIC */
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0,
+ qdev_get_gpio_in(DEVICE(&s->avic),
+ i2c_table[i].irq));
+ }
+
+ /* Initialize all GPIOs */
+ for (i = 0; i < FSL_IMX31_NUM_GPIOS; i++) {
+ static const struct {
+ hwaddr addr;
+ unsigned int irq;
+ } gpio_table[FSL_IMX31_NUM_GPIOS] = {
+ { FSL_IMX31_GPIO1_ADDR, FSL_IMX31_GPIO1_IRQ },
+ { FSL_IMX31_GPIO2_ADDR, FSL_IMX31_GPIO2_IRQ },
+ { FSL_IMX31_GPIO3_ADDR, FSL_IMX31_GPIO3_IRQ }
+ };
+
+ object_property_set_bool(OBJECT(&s->gpio[i]), false, "has-edge-sel",
+ &error_abort);
+ object_property_set_bool(OBJECT(&s->gpio[i]), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, gpio_table[i].addr);
+ /* Connect GPIO IRQ to PIC */
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0,
+ qdev_get_gpio_in(DEVICE(&s->avic),
+ gpio_table[i].irq));
+ }
+
+ /* On a real system, the first 16k is a `secure boot rom' */
+ memory_region_init_rom_device(&s->secure_rom, NULL, NULL, NULL,
+ "imx31.secure_rom",
+ FSL_IMX31_SECURE_ROM_SIZE, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ memory_region_add_subregion(get_system_memory(), FSL_IMX31_SECURE_ROM_ADDR,
+ &s->secure_rom);
+
+ /* There is also a 16k ROM */
+ memory_region_init_rom_device(&s->rom, NULL, NULL, NULL, "imx31.rom",
+ FSL_IMX31_ROM_SIZE, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ memory_region_add_subregion(get_system_memory(), FSL_IMX31_ROM_ADDR,
+ &s->rom);
+
+ /* initialize internal RAM (16 KB) */
+ memory_region_init_ram(&s->iram, NULL, "imx31.iram", FSL_IMX31_IRAM_SIZE,
+ &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ memory_region_add_subregion(get_system_memory(), FSL_IMX31_IRAM_ADDR,
+ &s->iram);
+ vmstate_register_ram_global(&s->iram);
+
+ /* internal RAM (16 KB) is aliased over 256 MB - 16 KB */
+ memory_region_init_alias(&s->iram_alias, NULL, "imx31.iram_alias",
+ &s->iram, 0, FSL_IMX31_IRAM_ALIAS_SIZE);
+ memory_region_add_subregion(get_system_memory(), FSL_IMX31_IRAM_ALIAS_ADDR,
+ &s->iram_alias);
+}
+
+static void fsl_imx31_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = fsl_imx31_realize;
+
+ /*
+ * Reason: creates an ARM CPU, thus use after free(), see
+ * arm_cpu_class_init()
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
+ dc->desc = "i.MX31 SOC";
+}
+
+static const TypeInfo fsl_imx31_type_info = {
+ .name = TYPE_FSL_IMX31,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(FslIMX31State),
+ .instance_init = fsl_imx31_init,
+ .class_init = fsl_imx31_class_init,
+};
+
+static void fsl_imx31_register_types(void)
+{
+ type_register_static(&fsl_imx31_type_info);
+}
+
+type_init(fsl_imx31_register_types)
diff --git a/qemu/hw/arm/gumstix.c b/qemu/hw/arm/gumstix.c
index 8103278b1..d59d9ba4e 100644
--- a/qemu/hw/arm/gumstix.c
+++ b/qemu/hw/arm/gumstix.c
@@ -34,6 +34,7 @@
* # qemu-system-arm -M verdex -pflash flash -monitor null -nographic -m 289
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/arm/pxa.h"
#include "net/net.h"
@@ -121,22 +122,38 @@ static void verdex_init(MachineState *machine)
qdev_get_gpio_in(cpu->gpio, 99));
}
-static QEMUMachine connex_machine = {
- .name = "connex",
- .desc = "Gumstix Connex (PXA255)",
- .init = connex_init,
+static void connex_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Gumstix Connex (PXA255)";
+ mc->init = connex_init;
+}
+
+static const TypeInfo connex_type = {
+ .name = MACHINE_TYPE_NAME("connex"),
+ .parent = TYPE_MACHINE,
+ .class_init = connex_class_init,
};
-static QEMUMachine verdex_machine = {
- .name = "verdex",
- .desc = "Gumstix Verdex (PXA270)",
- .init = verdex_init,
+static void verdex_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Gumstix Verdex (PXA270)";
+ mc->init = verdex_init;
+}
+
+static const TypeInfo verdex_type = {
+ .name = MACHINE_TYPE_NAME("verdex"),
+ .parent = TYPE_MACHINE,
+ .class_init = verdex_class_init,
};
static void gumstix_machine_init(void)
{
- qemu_register_machine(&connex_machine);
- qemu_register_machine(&verdex_machine);
+ type_register_static(&connex_type);
+ type_register_static(&verdex_type);
}
-machine_init(gumstix_machine_init);
+type_init(gumstix_machine_init)
diff --git a/qemu/hw/arm/highbank.c b/qemu/hw/arm/highbank.c
index f8353a787..d9930c0d3 100644
--- a/qemu/hw/arm/highbank.c
+++ b/qemu/hw/arm/highbank.c
@@ -17,11 +17,14 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/sysbus.h"
#include "hw/arm/arm.h"
#include "hw/devices.h"
#include "hw/loader.h"
#include "net/net.h"
+#include "sysemu/kvm.h"
#include "sysemu/sysemu.h"
#include "hw/boards.h"
#include "sysemu/block-backend.h"
@@ -32,10 +35,19 @@
#define SMP_BOOT_REG 0x40
#define MPCORE_PERIPHBASE 0xfff10000
+#define MVBAR_ADDR 0x200
+#define BOARD_SETUP_ADDR (MVBAR_ADDR + 8 * sizeof(uint32_t))
+
#define NIRQ_GIC 160
/* Board init. */
+static void hb_write_board_setup(ARMCPU *cpu,
+ const struct arm_boot_info *info)
+{
+ arm_write_secure_board_setup_dummy_smc(cpu, info, MVBAR_ADDR);
+}
+
static void hb_write_secondary(ARMCPU *cpu, const struct arm_boot_info *info)
{
int n;
@@ -223,52 +235,37 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
MemoryRegion *sysmem;
char *sysboot_filename;
- if (!cpu_model) {
- switch (machine_id) {
- case CALXEDA_HIGHBANK:
- cpu_model = "cortex-a9";
- break;
- case CALXEDA_MIDWAY:
- cpu_model = "cortex-a15";
- break;
- }
+ switch (machine_id) {
+ case CALXEDA_HIGHBANK:
+ cpu_model = "cortex-a9";
+ break;
+ case CALXEDA_MIDWAY:
+ cpu_model = "cortex-a15";
+ break;
}
for (n = 0; n < smp_cpus; n++) {
ObjectClass *oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model);
Object *cpuobj;
ARMCPU *cpu;
- Error *err = NULL;
-
- if (!oc) {
- error_report("Unable to find CPU definition");
- exit(1);
- }
cpuobj = object_new(object_class_get_name(oc));
cpu = ARM_CPU(cpuobj);
- /* By default A9 and A15 CPUs have EL3 enabled. This board does not
- * currently support EL3 so the CPU EL3 property is disabled before
- * realization.
- */
- if (object_property_find(cpuobj, "has_el3", NULL)) {
- object_property_set_bool(cpuobj, false, "has_el3", &err);
- if (err) {
- error_report_err(err);
- exit(1);
- }
+ object_property_set_int(cpuobj, QEMU_PSCI_CONDUIT_SMC,
+ "psci-conduit", &error_abort);
+
+ if (n) {
+ /* Secondary CPUs start in PSCI powered-down state */
+ object_property_set_bool(cpuobj, true,
+ "start-powered-off", &error_abort);
}
if (object_property_find(cpuobj, "reset-cbar", NULL)) {
object_property_set_int(cpuobj, MPCORE_PERIPHBASE,
"reset-cbar", &error_abort);
}
- object_property_set_bool(cpuobj, true, "realized", &err);
- if (err) {
- error_report_err(err);
- exit(1);
- }
+ object_property_set_bool(cpuobj, true, "realized", &error_fatal);
cpu_irq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ);
cpu_fiq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_FIQ);
}
@@ -281,17 +278,19 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
sysram = g_new(MemoryRegion, 1);
memory_region_init_ram(sysram, NULL, "highbank.sysram", 0x8000,
- &error_abort);
+ &error_fatal);
memory_region_add_subregion(sysmem, 0xfff88000, sysram);
if (bios_name != NULL) {
sysboot_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
if (sysboot_filename != NULL) {
if (load_image_targphys(sysboot_filename, 0xfff88000, 0x8000) < 0) {
- hw_error("Unable to load %s\n", bios_name);
+ error_report("Unable to load %s", bios_name);
+ exit(1);
}
g_free(sysboot_filename);
} else {
- hw_error("Unable to find %s\n", bios_name);
+ error_report("Unable to find %s", bios_name);
+ exit(1);
}
}
@@ -378,6 +377,16 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
highbank_binfo.loader_start = 0;
highbank_binfo.write_secondary_boot = hb_write_secondary;
highbank_binfo.secondary_cpu_reset_hook = hb_reset_secondary;
+ if (!kvm_enabled()) {
+ highbank_binfo.board_setup_addr = BOARD_SETUP_ADDR;
+ highbank_binfo.write_board_setup = hb_write_board_setup;
+ highbank_binfo.secure_board_setup = true;
+ } else {
+ error_report("WARNING: cannot load built-in Monitor support "
+ "if KVM is enabled. Some guests (such as Linux) "
+ "may not boot.");
+ }
+
arm_load_kernel(ARM_CPU(first_cpu), &highbank_binfo);
}
@@ -391,26 +400,42 @@ static void midway_init(MachineState *machine)
calxeda_init(machine, CALXEDA_MIDWAY);
}
-static QEMUMachine highbank_machine = {
- .name = "highbank",
- .desc = "Calxeda Highbank (ECX-1000)",
- .init = highbank_init,
- .block_default_type = IF_SCSI,
- .max_cpus = 4,
+static void highbank_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Calxeda Highbank (ECX-1000)";
+ mc->init = highbank_init;
+ mc->block_default_type = IF_SCSI;
+ mc->max_cpus = 4;
+}
+
+static const TypeInfo highbank_type = {
+ .name = MACHINE_TYPE_NAME("highbank"),
+ .parent = TYPE_MACHINE,
+ .class_init = highbank_class_init,
};
-static QEMUMachine midway_machine = {
- .name = "midway",
- .desc = "Calxeda Midway (ECX-2000)",
- .init = midway_init,
- .block_default_type = IF_SCSI,
- .max_cpus = 4,
+static void midway_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Calxeda Midway (ECX-2000)";
+ mc->init = midway_init;
+ mc->block_default_type = IF_SCSI;
+ mc->max_cpus = 4;
+}
+
+static const TypeInfo midway_type = {
+ .name = MACHINE_TYPE_NAME("midway"),
+ .parent = TYPE_MACHINE,
+ .class_init = midway_class_init,
};
static void calxeda_machines_init(void)
{
- qemu_register_machine(&highbank_machine);
- qemu_register_machine(&midway_machine);
+ type_register_static(&highbank_type);
+ type_register_static(&midway_type);
}
-machine_init(calxeda_machines_init);
+type_init(calxeda_machines_init)
diff --git a/qemu/hw/arm/imx25_pdk.c b/qemu/hw/arm/imx25_pdk.c
new file mode 100644
index 000000000..025b60843
--- /dev/null
+++ b/qemu/hw/arm/imx25_pdk.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2013 Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * PDK Board System emulation.
+ *
+ * Based on hw/arm/kzm.c
+ *
+ * Copyright (c) 2008 OKL and 2011 NICTA
+ * Written by Hans at OK-Labs
+ * Updated by Peter Chubb.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "hw/arm/fsl-imx25.h"
+#include "hw/boards.h"
+#include "qemu/error-report.h"
+#include "exec/address-spaces.h"
+#include "sysemu/qtest.h"
+#include "hw/i2c/i2c.h"
+
+/* Memory map for PDK Emulation Baseboard:
+ * 0x00000000-0x7fffffff See i.MX25 SOC fr support
+ * 0x80000000-0x87ffffff RAM + Alias EMULATED
+ * 0x90000000-0x9fffffff RAM + Alias EMULATED
+ * 0xa0000000-0xa7ffffff Flash IGNORED
+ * 0xa8000000-0xafffffff Flash IGNORED
+ * 0xb0000000-0xb1ffffff SRAM IGNORED
+ * 0xb2000000-0xb3ffffff SRAM IGNORED
+ * 0xb4000000-0xb5ffffff CS4 IGNORED
+ * 0xb6000000-0xb8000fff Reserved IGNORED
+ * 0xb8001000-0xb8001fff SDRAM CTRL reg IGNORED
+ * 0xb8002000-0xb8002fff WEIM CTRL reg IGNORED
+ * 0xb8003000-0xb8003fff M3IF CTRL reg IGNORED
+ * 0xb8004000-0xb8004fff EMI CTRL reg IGNORED
+ * 0xb8005000-0xbaffffff Reserved IGNORED
+ * 0xbb000000-0xbb000fff NAND flash area buf IGNORED
+ * 0xbb001000-0xbb0011ff NAND flash reserved IGNORED
+ * 0xbb001200-0xbb001dff Reserved IGNORED
+ * 0xbb001e00-0xbb001fff NAN flash CTRL reg IGNORED
+ * 0xbb012000-0xbfffffff Reserved IGNORED
+ * 0xc0000000-0xffffffff Reserved IGNORED
+ */
+
+typedef struct IMX25PDK {
+ FslIMX25State soc;
+ MemoryRegion ram;
+ MemoryRegion ram_alias;
+} IMX25PDK;
+
+static struct arm_boot_info imx25_pdk_binfo;
+
+static void imx25_pdk_init(MachineState *machine)
+{
+ IMX25PDK *s = g_new0(IMX25PDK, 1);
+ unsigned int ram_size;
+ unsigned int alias_offset;
+ int i;
+
+ object_initialize(&s->soc, sizeof(s->soc), TYPE_FSL_IMX25);
+ object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
+ &error_abort);
+
+ object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_fatal);
+
+ /* We need to initialize our memory */
+ if (machine->ram_size > (FSL_IMX25_SDRAM0_SIZE + FSL_IMX25_SDRAM1_SIZE)) {
+ error_report("WARNING: RAM size " RAM_ADDR_FMT " above max supported, "
+ "reduced to %x", machine->ram_size,
+ FSL_IMX25_SDRAM0_SIZE + FSL_IMX25_SDRAM1_SIZE);
+ machine->ram_size = FSL_IMX25_SDRAM0_SIZE + FSL_IMX25_SDRAM1_SIZE;
+ }
+
+ memory_region_allocate_system_memory(&s->ram, NULL, "imx25.ram",
+ machine->ram_size);
+ memory_region_add_subregion(get_system_memory(), FSL_IMX25_SDRAM0_ADDR,
+ &s->ram);
+
+ /* initialize the alias memory if any */
+ for (i = 0, ram_size = machine->ram_size, alias_offset = 0;
+ (i < 2) && ram_size; i++) {
+ unsigned int size;
+ static const struct {
+ hwaddr addr;
+ unsigned int size;
+ } ram[2] = {
+ { FSL_IMX25_SDRAM0_ADDR, FSL_IMX25_SDRAM0_SIZE },
+ { FSL_IMX25_SDRAM1_ADDR, FSL_IMX25_SDRAM1_SIZE },
+ };
+
+ size = MIN(ram_size, ram[i].size);
+
+ ram_size -= size;
+
+ if (size < ram[i].size) {
+ memory_region_init_alias(&s->ram_alias, NULL, "ram.alias",
+ &s->ram, alias_offset, ram[i].size - size);
+ memory_region_add_subregion(get_system_memory(),
+ ram[i].addr + size, &s->ram_alias);
+ }
+
+ alias_offset += ram[i].size;
+ }
+
+ imx25_pdk_binfo.ram_size = machine->ram_size;
+ imx25_pdk_binfo.kernel_filename = machine->kernel_filename;
+ imx25_pdk_binfo.kernel_cmdline = machine->kernel_cmdline;
+ imx25_pdk_binfo.initrd_filename = machine->initrd_filename;
+ imx25_pdk_binfo.loader_start = FSL_IMX25_SDRAM0_ADDR;
+ imx25_pdk_binfo.board_id = 1771,
+ imx25_pdk_binfo.nb_cpus = 1;
+
+ /*
+ * We test explicitly for qtest here as it is not done (yet?) in
+ * arm_load_kernel(). Without this the "make check" command would
+ * fail.
+ */
+ if (!qtest_enabled()) {
+ arm_load_kernel(&s->soc.cpu, &imx25_pdk_binfo);
+ } else {
+ /*
+ * This I2C device doesn't exist on the real board.
+ * We add it here (only on qtest usage) to be able to do a bit
+ * of simple qtest. See "make check" for details.
+ */
+ i2c_create_slave((I2CBus *)qdev_get_child_bus(DEVICE(&s->soc.i2c[0]),
+ "i2c"),
+ "ds1338", 0x68);
+ }
+}
+
+static void imx25_pdk_machine_init(MachineClass *mc)
+{
+ mc->desc = "ARM i.MX25 PDK board (ARM926)";
+ mc->init = imx25_pdk_init;
+}
+
+DEFINE_MACHINE("imx25-pdk", imx25_pdk_machine_init)
diff --git a/qemu/hw/arm/integratorcp.c b/qemu/hw/arm/integratorcp.c
index 0fbbf997e..e31bca6e7 100644
--- a/qemu/hw/arm/integratorcp.c
+++ b/qemu/hw/arm/integratorcp.c
@@ -7,6 +7,10 @@
* This code is licensed under the GPL
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/sysbus.h"
#include "hw/devices.h"
#include "hw/boards.h"
@@ -266,7 +270,7 @@ static int integratorcm_init(SysBusDevice *dev)
s->cm_refcnt_offset = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 24,
1000);
memory_region_init_ram(&s->flash, OBJECT(s), "integrator.flash", 0x100000,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&s->flash);
memory_region_init_io(&s->iomem, OBJECT(s), &integratorcm_ops, s,
@@ -533,7 +537,6 @@ static void integratorcp_init(MachineState *machine)
qemu_irq pic[32];
DeviceState *dev, *sic, *icp;
int i;
- Error *err = NULL;
if (!cpu_model) {
cpu_model = "arm926";
@@ -552,18 +555,10 @@ static void integratorcp_init(MachineState *machine)
* realization.
*/
if (object_property_find(cpuobj, "has_el3", NULL)) {
- object_property_set_bool(cpuobj, false, "has_el3", &err);
- if (err) {
- error_report_err(err);
- exit(1);
- }
+ object_property_set_bool(cpuobj, false, "has_el3", &error_fatal);
}
- object_property_set_bool(cpuobj, true, "realized", &err);
- if (err) {
- error_report_err(err);
- exit(1);
- }
+ object_property_set_bool(cpuobj, true, "realized", &error_fatal);
cpu = ARM_CPU(cpuobj);
@@ -619,18 +614,13 @@ static void integratorcp_init(MachineState *machine)
arm_load_kernel(cpu, &integrator_binfo);
}
-static QEMUMachine integratorcp_machine = {
- .name = "integratorcp",
- .desc = "ARM Integrator/CP (ARM926EJ-S)",
- .init = integratorcp_init,
-};
-
-static void integratorcp_machine_init(void)
+static void integratorcp_machine_init(MachineClass *mc)
{
- qemu_register_machine(&integratorcp_machine);
+ mc->desc = "ARM Integrator/CP (ARM926EJ-S)";
+ mc->init = integratorcp_init;
}
-machine_init(integratorcp_machine_init);
+DEFINE_MACHINE("integratorcp", integratorcp_machine_init)
static Property core_properties[] = {
DEFINE_PROP_UINT32("memsz", IntegratorCMState, memsz, 0),
diff --git a/qemu/hw/arm/kzm.c b/qemu/hw/arm/kzm.c
index 5be0369a5..2c96ee33b 100644
--- a/qemu/hw/arm/kzm.c
+++ b/qemu/hw/arm/kzm.c
@@ -13,141 +13,135 @@
* i.MX31 SoC
*/
-#include "hw/sysbus.h"
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "hw/arm/fsl-imx31.h"
+#include "hw/boards.h"
+#include "qemu/error-report.h"
#include "exec/address-spaces.h"
-#include "hw/hw.h"
-#include "hw/arm/arm.h"
-#include "hw/devices.h"
#include "net/net.h"
-#include "sysemu/sysemu.h"
-#include "hw/boards.h"
+#include "hw/devices.h"
#include "hw/char/serial.h"
-#include "hw/arm/imx.h"
-
- /* Memory map for Kzm Emulation Baseboard:
- * 0x00000000-0x00003fff 16k secure ROM IGNORED
- * 0x00004000-0x00407fff Reserved IGNORED
- * 0x00404000-0x00407fff ROM IGNORED
- * 0x00408000-0x0fffffff Reserved IGNORED
- * 0x10000000-0x1fffbfff RAM aliasing IGNORED
- * 0x1fffc000-0x1fffffff RAM EMULATED
- * 0x20000000-0x2fffffff Reserved IGNORED
- * 0x30000000-0x7fffffff I.MX31 Internal Register Space
- * 0x43f00000 IO_AREA0
- * 0x43f90000 UART1 EMULATED
- * 0x43f94000 UART2 EMULATED
- * 0x68000000 AVIC EMULATED
- * 0x53f80000 CCM EMULATED
- * 0x53f94000 PIT 1 EMULATED
- * 0x53f98000 PIT 2 EMULATED
- * 0x53f90000 GPT EMULATED
- * 0x80000000-0x87ffffff RAM EMULATED
- * 0x88000000-0x8fffffff RAM Aliasing EMULATED
- * 0xa0000000-0xafffffff NAND Flash IGNORED
- * 0xb0000000-0xb3ffffff Unavailable IGNORED
- * 0xb4000000-0xb4000fff 8-bit free space IGNORED
- * 0xb4001000-0xb400100f Board control IGNORED
- * 0xb4001003 DIP switch
- * 0xb4001010-0xb400101f 7-segment LED IGNORED
- * 0xb4001020-0xb400102f LED IGNORED
- * 0xb4001030-0xb400103f LED IGNORED
- * 0xb4001040-0xb400104f FPGA, UART EMULATED
- * 0xb4001050-0xb400105f FPGA, UART EMULATED
- * 0xb4001060-0xb40fffff FPGA IGNORED
- * 0xb6000000-0xb61fffff LAN controller EMULATED
- * 0xb6200000-0xb62fffff FPGA NAND Controller IGNORED
- * 0xb6300000-0xb7ffffff Free IGNORED
- * 0xb8000000-0xb8004fff Memory control registers IGNORED
- * 0xc0000000-0xc3ffffff PCMCIA/CF IGNORED
- * 0xc4000000-0xffffffff Reserved IGNORED
- */
-
-#define KZM_RAMADDRESS (0x80000000)
-#define KZM_FPGA (0xb4001040)
+#include "sysemu/qtest.h"
+
+/* Memory map for Kzm Emulation Baseboard:
+ * 0x00000000-0x7fffffff See i.MX31 SOC for support
+ * 0x80000000-0x8fffffff RAM EMULATED
+ * 0x90000000-0x9fffffff RAM EMULATED
+ * 0xa0000000-0xafffffff Flash IGNORED
+ * 0xb0000000-0xb3ffffff Unavailable IGNORED
+ * 0xb4000000-0xb4000fff 8-bit free space IGNORED
+ * 0xb4001000-0xb400100f Board control IGNORED
+ * 0xb4001003 DIP switch
+ * 0xb4001010-0xb400101f 7-segment LED IGNORED
+ * 0xb4001020-0xb400102f LED IGNORED
+ * 0xb4001030-0xb400103f LED IGNORED
+ * 0xb4001040-0xb400104f FPGA, UART EMULATED
+ * 0xb4001050-0xb400105f FPGA, UART EMULATED
+ * 0xb4001060-0xb40fffff FPGA IGNORED
+ * 0xb6000000-0xb61fffff LAN controller EMULATED
+ * 0xb6200000-0xb62fffff FPGA NAND Controller IGNORED
+ * 0xb6300000-0xb7ffffff Free IGNORED
+ * 0xb8000000-0xb8004fff Memory control registers IGNORED
+ * 0xc0000000-0xc3ffffff PCMCIA/CF IGNORED
+ * 0xc4000000-0xffffffff Reserved IGNORED
+ */
+
+typedef struct IMX31KZM {
+ FslIMX31State soc;
+ MemoryRegion ram;
+ MemoryRegion ram_alias;
+} IMX31KZM;
+
+#define KZM_RAM_ADDR (FSL_IMX31_SDRAM0_ADDR)
+#define KZM_FPGA_ADDR (FSL_IMX31_CS4_ADDR + 0x1040)
+#define KZM_LAN9118_ADDR (FSL_IMX31_CS5_ADDR)
static struct arm_boot_info kzm_binfo = {
- .loader_start = KZM_RAMADDRESS,
+ .loader_start = KZM_RAM_ADDR,
.board_id = 1722,
};
static void kzm_init(MachineState *machine)
{
- ram_addr_t ram_size = machine->ram_size;
- const char *cpu_model = machine->cpu_model;
- const char *kernel_filename = machine->kernel_filename;
- const char *kernel_cmdline = machine->kernel_cmdline;
- const char *initrd_filename = machine->initrd_filename;
- ARMCPU *cpu;
- MemoryRegion *address_space_mem = get_system_memory();
- MemoryRegion *ram = g_new(MemoryRegion, 1);
- MemoryRegion *sram = g_new(MemoryRegion, 1);
- MemoryRegion *ram_alias = g_new(MemoryRegion, 1);
- DeviceState *dev;
- DeviceState *ccm;
-
- if (!cpu_model) {
- cpu_model = "arm1136";
+ IMX31KZM *s = g_new0(IMX31KZM, 1);
+ unsigned int ram_size;
+ unsigned int alias_offset;
+ unsigned int i;
+
+ object_initialize(&s->soc, sizeof(s->soc), TYPE_FSL_IMX31);
+ object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
+ &error_abort);
+
+ object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_fatal);
+
+ /* Check the amount of memory is compatible with the SOC */
+ if (machine->ram_size > (FSL_IMX31_SDRAM0_SIZE + FSL_IMX31_SDRAM1_SIZE)) {
+ error_report("WARNING: RAM size " RAM_ADDR_FMT " above max supported, "
+ "reduced to %x", machine->ram_size,
+ FSL_IMX31_SDRAM0_SIZE + FSL_IMX31_SDRAM1_SIZE);
+ machine->ram_size = FSL_IMX31_SDRAM0_SIZE + FSL_IMX31_SDRAM1_SIZE;
}
- cpu = cpu_arm_init(cpu_model);
- if (!cpu) {
- fprintf(stderr, "Unable to find CPU definition\n");
- exit(1);
+ memory_region_allocate_system_memory(&s->ram, NULL, "kzm.ram",
+ machine->ram_size);
+ memory_region_add_subregion(get_system_memory(), FSL_IMX31_SDRAM0_ADDR,
+ &s->ram);
+
+ /* initialize the alias memory if any */
+ for (i = 0, ram_size = machine->ram_size, alias_offset = 0;
+ (i < 2) && ram_size; i++) {
+ unsigned int size;
+ static const struct {
+ hwaddr addr;
+ unsigned int size;
+ } ram[2] = {
+ { FSL_IMX31_SDRAM0_ADDR, FSL_IMX31_SDRAM0_SIZE },
+ { FSL_IMX31_SDRAM1_ADDR, FSL_IMX31_SDRAM1_SIZE },
+ };
+
+ size = MIN(ram_size, ram[i].size);
+
+ ram_size -= size;
+
+ if (size < ram[i].size) {
+ memory_region_init_alias(&s->ram_alias, NULL, "ram.alias",
+ &s->ram, alias_offset, ram[i].size - size);
+ memory_region_add_subregion(get_system_memory(),
+ ram[i].addr + size, &s->ram_alias);
+ }
+
+ alias_offset += ram[i].size;
}
- /* On a real system, the first 16k is a `secure boot rom' */
-
- memory_region_allocate_system_memory(ram, NULL, "kzm.ram", ram_size);
- memory_region_add_subregion(address_space_mem, KZM_RAMADDRESS, ram);
-
- memory_region_init_alias(ram_alias, NULL, "ram.alias", ram, 0, ram_size);
- memory_region_add_subregion(address_space_mem, 0x88000000, ram_alias);
-
- memory_region_init_ram(sram, NULL, "kzm.sram", 0x4000, &error_abort);
- memory_region_add_subregion(address_space_mem, 0x1FFFC000, sram);
-
- dev = sysbus_create_varargs("imx_avic", 0x68000000,
- qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ),
- qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_FIQ),
- NULL);
-
- imx_serial_create(0, 0x43f90000, qdev_get_gpio_in(dev, 45));
- imx_serial_create(1, 0x43f94000, qdev_get_gpio_in(dev, 32));
-
- ccm = sysbus_create_simple("imx_ccm", 0x53f80000, NULL);
-
- imx_timerp_create(0x53f94000, qdev_get_gpio_in(dev, 28), ccm);
- imx_timerp_create(0x53f98000, qdev_get_gpio_in(dev, 27), ccm);
- imx_timerg_create(0x53f90000, qdev_get_gpio_in(dev, 29), ccm);
-
if (nd_table[0].used) {
- lan9118_init(&nd_table[0], 0xb6000000, qdev_get_gpio_in(dev, 52));
+ lan9118_init(&nd_table[0], KZM_LAN9118_ADDR,
+ qdev_get_gpio_in(DEVICE(&s->soc.avic), 52));
}
if (serial_hds[2]) { /* touchscreen */
- serial_mm_init(address_space_mem, KZM_FPGA+0x10, 0,
- qdev_get_gpio_in(dev, 52),
- 14745600, serial_hds[2],
- DEVICE_NATIVE_ENDIAN);
+ serial_mm_init(get_system_memory(), KZM_FPGA_ADDR+0x10, 0,
+ qdev_get_gpio_in(DEVICE(&s->soc.avic), 52),
+ 14745600, serial_hds[2], DEVICE_NATIVE_ENDIAN);
}
- kzm_binfo.ram_size = ram_size;
- kzm_binfo.kernel_filename = kernel_filename;
- kzm_binfo.kernel_cmdline = kernel_cmdline;
- kzm_binfo.initrd_filename = initrd_filename;
+ kzm_binfo.ram_size = machine->ram_size;
+ kzm_binfo.kernel_filename = machine->kernel_filename;
+ kzm_binfo.kernel_cmdline = machine->kernel_cmdline;
+ kzm_binfo.initrd_filename = machine->initrd_filename;
kzm_binfo.nb_cpus = 1;
- arm_load_kernel(cpu, &kzm_binfo);
-}
-static QEMUMachine kzm_machine = {
- .name = "kzm",
- .desc = "ARM KZM Emulation Baseboard (ARM1136)",
- .init = kzm_init,
-};
+ if (!qtest_enabled()) {
+ arm_load_kernel(&s->soc.cpu, &kzm_binfo);
+ }
+}
-static void kzm_machine_init(void)
+static void kzm_machine_init(MachineClass *mc)
{
- qemu_register_machine(&kzm_machine);
+ mc->desc = "ARM KZM Emulation Baseboard (ARM1136)";
+ mc->init = kzm_init;
}
-machine_init(kzm_machine_init)
+DEFINE_MACHINE("kzm", kzm_machine_init)
diff --git a/qemu/hw/arm/mainstone.c b/qemu/hw/arm/mainstone.c
index 0da02a67e..454acc5d2 100644
--- a/qemu/hw/arm/mainstone.c
+++ b/qemu/hw/arm/mainstone.c
@@ -11,6 +11,8 @@
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/arm/pxa.h"
#include "hw/arm/arm.h"
@@ -124,7 +126,7 @@ static void mainstone_common_init(MemoryRegion *address_space_mem,
/* Setup CPU & memory */
mpu = pxa270_init(address_space_mem, mainstone_binfo.ram_size, cpu_model);
memory_region_init_ram(rom, NULL, "mainstone.rom", MAINSTONE_ROM,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(rom);
memory_region_set_readonly(rom, true);
memory_region_add_subregion(address_space_mem, 0, rom);
@@ -188,15 +190,10 @@ static void mainstone_init(MachineState *machine)
mainstone_common_init(get_system_memory(), machine, mainstone, 0x196);
}
-static QEMUMachine mainstone2_machine = {
- .name = "mainstone",
- .desc = "Mainstone II (PXA27x)",
- .init = mainstone_init,
-};
-
-static void mainstone_machine_init(void)
+static void mainstone2_machine_init(MachineClass *mc)
{
- qemu_register_machine(&mainstone2_machine);
+ mc->desc = "Mainstone II (PXA27x)";
+ mc->init = mainstone_init;
}
-machine_init(mainstone_machine_init);
+DEFINE_MACHINE("mainstone", mainstone2_machine_init)
diff --git a/qemu/hw/arm/musicpal.c b/qemu/hw/arm/musicpal.c
index 42f66b33e..7a4cc07dd 100644
--- a/qemu/hw/arm/musicpal.c
+++ b/qemu/hw/arm/musicpal.c
@@ -9,6 +9,10 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/sysbus.h"
#include "hw/arm/arm.h"
#include "hw/devices.h"
@@ -1599,7 +1603,7 @@ static void musicpal_init(MachineState *machine)
memory_region_add_subregion(address_space_mem, 0, ram);
memory_region_init_ram(sram, NULL, "musicpal.sram", MP_SRAM_SIZE,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(sram);
memory_region_add_subregion(address_space_mem, MP_SRAM_BASE, sram);
@@ -1709,18 +1713,13 @@ static void musicpal_init(MachineState *machine)
arm_load_kernel(cpu, &musicpal_binfo);
}
-static QEMUMachine musicpal_machine = {
- .name = "musicpal",
- .desc = "Marvell 88w8618 / MusicPal (ARM926EJ-S)",
- .init = musicpal_init,
-};
-
-static void musicpal_machine_init(void)
+static void musicpal_machine_init(MachineClass *mc)
{
- qemu_register_machine(&musicpal_machine);
+ mc->desc = "Marvell 88w8618 / MusicPal (ARM926EJ-S)";
+ mc->init = musicpal_init;
}
-machine_init(musicpal_machine_init);
+DEFINE_MACHINE("musicpal", musicpal_machine_init)
static void mv88w8618_wlan_class_init(ObjectClass *klass, void *data)
{
diff --git a/qemu/hw/arm/netduino2.c b/qemu/hw/arm/netduino2.c
index 8f26780ef..23d792837 100644
--- a/qemu/hw/arm/netduino2.c
+++ b/qemu/hw/arm/netduino2.c
@@ -22,6 +22,8 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/boards.h"
#include "qemu/error-report.h"
#include "hw/arm/stm32f205_soc.h"
@@ -29,29 +31,19 @@
static void netduino2_init(MachineState *machine)
{
DeviceState *dev;
- Error *err = NULL;
dev = qdev_create(NULL, TYPE_STM32F205_SOC);
if (machine->kernel_filename) {
qdev_prop_set_string(dev, "kernel-filename", machine->kernel_filename);
}
qdev_prop_set_string(dev, "cpu-model", "cortex-m3");
- object_property_set_bool(OBJECT(dev), true, "realized", &err);
- if (err != NULL) {
- error_report("%s", error_get_pretty(err));
- exit(1);
- }
+ object_property_set_bool(OBJECT(dev), true, "realized", &error_fatal);
}
-static QEMUMachine netduino2_machine = {
- .name = "netduino2",
- .desc = "Netduino 2 Machine",
- .init = netduino2_init,
-};
-
-static void netduino2_machine_init(void)
+static void netduino2_machine_init(MachineClass *mc)
{
- qemu_register_machine(&netduino2_machine);
+ mc->desc = "Netduino 2 Machine";
+ mc->init = netduino2_init;
}
-machine_init(netduino2_machine_init);
+DEFINE_MACHINE("netduino2", netduino2_machine_init)
diff --git a/qemu/hw/arm/nseries.c b/qemu/hw/arm/nseries.c
index a659e8525..538250555 100644
--- a/qemu/hw/arm/nseries.c
+++ b/qemu/hw/arm/nseries.c
@@ -18,7 +18,9 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
-#include "qemu-common.h"
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/cutils.h"
#include "sysemu/sysemu.h"
#include "hw/arm/omap.h"
#include "hw/arm/arm.h"
@@ -172,8 +174,8 @@ static void n8x0_nand_setup(struct n800_s *s)
qdev_prop_set_int32(s->nand, "shift", 1);
dinfo = drive_get(IF_MTD, 0, 0);
if (dinfo) {
- qdev_prop_set_drive_nofail(s->nand, "drive",
- blk_by_legacy_dinfo(dinfo));
+ qdev_prop_set_drive(s->nand, "drive", blk_by_legacy_dinfo(dinfo),
+ &error_fatal);
}
qdev_init_nofail(s->nand);
sysbus_connect_irq(SYS_BUS_DEVICE(s->nand), 0,
@@ -1275,7 +1277,7 @@ static int n8x0_atag_setup(void *p, int model)
strcpy((void *) w, "hw-build"); /* char component[12] */
w += 6;
strcpy((void *) w, "QEMU ");
- pstrcat((void *) w, 12, qemu_get_version()); /* char version[12] */
+ pstrcat((void *) w, 12, qemu_hw_version()); /* char version[12] */
w += 6;
tag = (model == 810) ? "1.1.10-qemu" : "1.1.6-qemu";
@@ -1413,24 +1415,40 @@ static void n810_init(MachineState *machine)
n8x0_init(machine, &n810_binfo, 810);
}
-static QEMUMachine n800_machine = {
- .name = "n800",
- .desc = "Nokia N800 tablet aka. RX-34 (OMAP2420)",
- .init = n800_init,
- .default_boot_order = "",
+static void n800_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Nokia N800 tablet aka. RX-34 (OMAP2420)";
+ mc->init = n800_init;
+ mc->default_boot_order = "";
+}
+
+static const TypeInfo n800_type = {
+ .name = MACHINE_TYPE_NAME("n800"),
+ .parent = TYPE_MACHINE,
+ .class_init = n800_class_init,
};
-static QEMUMachine n810_machine = {
- .name = "n810",
- .desc = "Nokia N810 tablet aka. RX-44 (OMAP2420)",
- .init = n810_init,
- .default_boot_order = "",
+static void n810_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Nokia N810 tablet aka. RX-44 (OMAP2420)";
+ mc->init = n810_init;
+ mc->default_boot_order = "";
+}
+
+static const TypeInfo n810_type = {
+ .name = MACHINE_TYPE_NAME("n810"),
+ .parent = TYPE_MACHINE,
+ .class_init = n810_class_init,
};
static void nseries_machine_init(void)
{
- qemu_register_machine(&n800_machine);
- qemu_register_machine(&n810_machine);
+ type_register_static(&n800_type);
+ type_register_static(&n810_type);
}
-machine_init(nseries_machine_init);
+type_init(nseries_machine_init)
diff --git a/qemu/hw/arm/omap1.c b/qemu/hw/arm/omap1.c
index de2b28925..b3cf0ec69 100644
--- a/qemu/hw/arm/omap1.c
+++ b/qemu/hw/arm/omap1.c
@@ -17,6 +17,10 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/boards.h"
#include "hw/hw.h"
#include "hw/arm/arm.h"
@@ -27,6 +31,8 @@
#include "sysemu/blockdev.h"
#include "qemu/range.h"
#include "hw/sysbus.h"
+#include "qemu/cutils.h"
+#include "qemu/bcd.h"
/* Should signal the TCMI/GPMC */
uint32_t omap_badwidth_read8(void *opaque, hwaddr addr)
@@ -106,7 +112,7 @@ static inline uint32_t omap_timer_read(struct omap_mpu_timer_s *timer)
if (timer->st && timer->enable && timer->rate)
return timer->val - muldiv64(distance >> (timer->ptv + 1),
- timer->rate, get_ticks_per_sec());
+ timer->rate, NANOSECONDS_PER_SECOND);
else
return timer->val;
}
@@ -124,7 +130,7 @@ static inline void omap_timer_update(struct omap_mpu_timer_s *timer)
if (timer->enable && timer->st && timer->rate) {
timer->val = timer->reset_val; /* Should skip this on clk enable */
expires = muldiv64((uint64_t) timer->val << (timer->ptv + 1),
- get_ticks_per_sec(), timer->rate);
+ NANOSECONDS_PER_SECOND, timer->rate);
/* If timer expiry would be sooner than in about 1 ms and
* auto-reload isn't set, then fire immediately. This is a hack
@@ -132,10 +138,11 @@ static inline void omap_timer_update(struct omap_mpu_timer_s *timer)
* sets the interval to a very low value and polls the status bit
* in a busy loop when it wants to sleep just a couple of CPU
* ticks. */
- if (expires > (get_ticks_per_sec() >> 10) || timer->ar)
+ if (expires > (NANOSECONDS_PER_SECOND >> 10) || timer->ar) {
timer_mod(timer->timer, timer->time + expires);
- else
+ } else {
qemu_bh_schedule(timer->tick);
+ }
} else
timer_del(timer->timer);
}
@@ -258,8 +265,7 @@ static struct omap_mpu_timer_s *omap_mpu_timer_init(MemoryRegion *system_memory,
hwaddr base,
qemu_irq irq, omap_clk clk)
{
- struct omap_mpu_timer_s *s = (struct omap_mpu_timer_s *)
- g_malloc0(sizeof(struct omap_mpu_timer_s));
+ struct omap_mpu_timer_s *s = g_new0(struct omap_mpu_timer_s, 1);
s->irq = irq;
s->clk = clk;
@@ -388,8 +394,7 @@ static struct omap_watchdog_timer_s *omap_wd_timer_init(MemoryRegion *memory,
hwaddr base,
qemu_irq irq, omap_clk clk)
{
- struct omap_watchdog_timer_s *s = (struct omap_watchdog_timer_s *)
- g_malloc0(sizeof(struct omap_watchdog_timer_s));
+ struct omap_watchdog_timer_s *s = g_new0(struct omap_watchdog_timer_s, 1);
s->timer.irq = irq;
s->timer.clk = clk;
@@ -495,8 +500,7 @@ static struct omap_32khz_timer_s *omap_os_timer_init(MemoryRegion *memory,
hwaddr base,
qemu_irq irq, omap_clk clk)
{
- struct omap_32khz_timer_s *s = (struct omap_32khz_timer_s *)
- g_malloc0(sizeof(struct omap_32khz_timer_s));
+ struct omap_32khz_timer_s *s = g_new0(struct omap_32khz_timer_s, 1);
s->timer.irq = irq;
s->timer.clk = clk;
@@ -615,14 +619,14 @@ static void omap_ulpd_pm_write(void *opaque, hwaddr addr,
now -= s->ulpd_gauge_start;
/* 32-kHz ticks */
- ticks = muldiv64(now, 32768, get_ticks_per_sec());
+ ticks = muldiv64(now, 32768, NANOSECONDS_PER_SECOND);
s->ulpd_pm_regs[0x00 >> 2] = (ticks >> 0) & 0xffff;
s->ulpd_pm_regs[0x04 >> 2] = (ticks >> 16) & 0xffff;
if (ticks >> 32) /* OVERFLOW_32K */
s->ulpd_pm_regs[0x14 >> 2] |= 1 << 2;
/* High frequency ticks */
- ticks = muldiv64(now, 12000000, get_ticks_per_sec());
+ ticks = muldiv64(now, 12000000, NANOSECONDS_PER_SECOND);
s->ulpd_pm_regs[0x08 >> 2] = (ticks >> 0) & 0xffff;
s->ulpd_pm_regs[0x0c >> 2] = (ticks >> 16) & 0xffff;
if (ticks >> 32) /* OVERFLOW_HI_FREQ */
@@ -1236,8 +1240,7 @@ static struct omap_tipb_bridge_s *omap_tipb_bridge_init(
MemoryRegion *memory, hwaddr base,
qemu_irq abort_irq, omap_clk clk)
{
- struct omap_tipb_bridge_s *s = (struct omap_tipb_bridge_s *)
- g_malloc0(sizeof(struct omap_tipb_bridge_s));
+ struct omap_tipb_bridge_s *s = g_new0(struct omap_tipb_bridge_s, 1);
s->abort = abort_irq;
omap_tipb_bridge_reset(s);
@@ -2099,8 +2102,7 @@ static struct omap_mpuio_s *omap_mpuio_init(MemoryRegion *memory,
qemu_irq kbd_int, qemu_irq gpio_int, qemu_irq wakeup,
omap_clk clk)
{
- struct omap_mpuio_s *s = (struct omap_mpuio_s *)
- g_malloc0(sizeof(struct omap_mpuio_s));
+ struct omap_mpuio_s *s = g_new0(struct omap_mpuio_s, 1);
s->irq = gpio_int;
s->kbd_irq = kbd_int;
@@ -2292,8 +2294,7 @@ static struct omap_uwire_s *omap_uwire_init(MemoryRegion *system_memory,
qemu_irq dma,
omap_clk clk)
{
- struct omap_uwire_s *s = (struct omap_uwire_s *)
- g_malloc0(sizeof(struct omap_uwire_s));
+ struct omap_uwire_s *s = g_new0(struct omap_uwire_s, 1);
s->txirq = txirq;
s->rxirq = rxirq;
@@ -2932,8 +2933,7 @@ static struct omap_rtc_s *omap_rtc_init(MemoryRegion *system_memory,
qemu_irq timerirq, qemu_irq alarmirq,
omap_clk clk)
{
- struct omap_rtc_s *s = (struct omap_rtc_s *)
- g_malloc0(sizeof(struct omap_rtc_s));
+ struct omap_rtc_s *s = g_new0(struct omap_rtc_s, 1);
s->irq = timerirq;
s->alarm = alarmirq;
@@ -3032,7 +3032,7 @@ static void omap_mcbsp_source_tick(void *opaque)
omap_mcbsp_rx_newdata(s);
timer_mod(s->source_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
- get_ticks_per_sec());
+ NANOSECONDS_PER_SECOND);
}
static void omap_mcbsp_rx_start(struct omap_mcbsp_s *s)
@@ -3078,7 +3078,7 @@ static void omap_mcbsp_sink_tick(void *opaque)
omap_mcbsp_tx_newdata(s);
timer_mod(s->sink_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
- get_ticks_per_sec());
+ NANOSECONDS_PER_SECOND);
}
static void omap_mcbsp_tx_start(struct omap_mcbsp_s *s)
@@ -3468,8 +3468,7 @@ static struct omap_mcbsp_s *omap_mcbsp_init(MemoryRegion *system_memory,
qemu_irq txirq, qemu_irq rxirq,
qemu_irq *dma, omap_clk clk)
{
- struct omap_mcbsp_s *s = (struct omap_mcbsp_s *)
- g_malloc0(sizeof(struct omap_mcbsp_s));
+ struct omap_mcbsp_s *s = g_new0(struct omap_mcbsp_s, 1);
s->txirq = txirq;
s->rxirq = rxirq;
@@ -3648,8 +3647,7 @@ static void omap_lpg_clk_update(void *opaque, int line, int on)
static struct omap_lpg_s *omap_lpg_init(MemoryRegion *system_memory,
hwaddr base, omap_clk clk)
{
- struct omap_lpg_s *s = (struct omap_lpg_s *)
- g_malloc0(sizeof(struct omap_lpg_s));
+ struct omap_lpg_s *s = g_new0(struct omap_lpg_s, 1);
s->tm = timer_new_ms(QEMU_CLOCK_VIRTUAL, omap_lpg_tick, s);
@@ -3853,8 +3851,7 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory,
const char *core)
{
int i;
- struct omap_mpu_state_s *s = (struct omap_mpu_state_s *)
- g_malloc0(sizeof(struct omap_mpu_state_s));
+ struct omap_mpu_state_s *s = g_new0(struct omap_mpu_state_s, 1);
qemu_irq dma_irqs[6];
DriveInfo *dinfo;
SysBusDevice *busdev;
@@ -3882,7 +3879,7 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory,
s->sdram_size);
memory_region_add_subregion(system_memory, OMAP_EMIFF_BASE, &s->emiff_ram);
memory_region_init_ram(&s->imif_ram, NULL, "omap1.sram", s->sram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&s->imif_ram);
memory_region_add_subregion(system_memory, OMAP_IMIF_BASE, &s->imif_ram);
diff --git a/qemu/hw/arm/omap2.c b/qemu/hw/arm/omap2.c
index e39b31729..3a0d77714 100644
--- a/qemu/hw/arm/omap2.c
+++ b/qemu/hw/arm/omap2.c
@@ -18,6 +18,10 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
#include "hw/boards.h"
@@ -596,8 +600,7 @@ static const MemoryRegionOps omap_eac_ops = {
static struct omap_eac_s *omap_eac_init(struct omap_target_agent_s *ta,
qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk)
{
- struct omap_eac_s *s = (struct omap_eac_s *)
- g_malloc0(sizeof(struct omap_eac_s));
+ struct omap_eac_s *s = g_new0(struct omap_eac_s, 1);
s->irq = irq;
s->codec.rxdrq = *drq ++;
@@ -788,8 +791,7 @@ static struct omap_sti_s *omap_sti_init(struct omap_target_agent_s *ta,
hwaddr channel_base, qemu_irq irq, omap_clk clk,
CharDriverState *chr)
{
- struct omap_sti_s *s = (struct omap_sti_s *)
- g_malloc0(sizeof(struct omap_sti_s));
+ struct omap_sti_s *s = g_new0(struct omap_sti_s, 1);
s->irq = irq;
omap_sti_reset(s);
@@ -1806,8 +1808,7 @@ static struct omap_prcm_s *omap_prcm_init(struct omap_target_agent_s *ta,
qemu_irq mpu_int, qemu_irq dsp_int, qemu_irq iva_int,
struct omap_mpu_state_s *mpu)
{
- struct omap_prcm_s *s = (struct omap_prcm_s *)
- g_malloc0(sizeof(struct omap_prcm_s));
+ struct omap_prcm_s *s = g_new0(struct omap_prcm_s, 1);
s->irq[0] = mpu_int;
s->irq[1] = dsp_int;
@@ -2185,8 +2186,7 @@ static void omap_sysctl_reset(struct omap_sysctl_s *s)
static struct omap_sysctl_s *omap_sysctl_init(struct omap_target_agent_s *ta,
omap_clk iclk, struct omap_mpu_state_s *mpu)
{
- struct omap_sysctl_s *s = (struct omap_sysctl_s *)
- g_malloc0(sizeof(struct omap_sysctl_s));
+ struct omap_sysctl_s *s = g_new0(struct omap_sysctl_s, 1);
s->mpu = mpu;
omap_sysctl_reset(s);
@@ -2248,8 +2248,7 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem,
unsigned long sdram_size,
const char *core)
{
- struct omap_mpu_state_s *s = (struct omap_mpu_state_s *)
- g_malloc0(sizeof(struct omap_mpu_state_s));
+ struct omap_mpu_state_s *s = g_new0(struct omap_mpu_state_s, 1);
qemu_irq dma_irqs[4];
DriveInfo *dinfo;
int i;
@@ -2276,7 +2275,7 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem,
s->sdram_size);
memory_region_add_subregion(sysmem, OMAP2_Q2_BASE, &s->sdram);
memory_region_init_ram(&s->sram, NULL, "omap2.sram", s->sram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&s->sram);
memory_region_add_subregion(sysmem, OMAP2_SRAM_BASE, &s->sram);
diff --git a/qemu/hw/arm/omap_sx1.c b/qemu/hw/arm/omap_sx1.c
index 4b0f7f9c4..5d74026cb 100644
--- a/qemu/hw/arm/omap_sx1.c
+++ b/qemu/hw/arm/omap_sx1.c
@@ -25,6 +25,8 @@
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "ui/console.h"
#include "hw/arm/omap.h"
@@ -122,7 +124,7 @@ static void sx1_init(MachineState *machine, const int version)
/* External Flash (EMIFS) */
memory_region_init_ram(flash, NULL, "omap_sx1.flash0-0", flash_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(flash);
memory_region_set_readonly(flash, true);
memory_region_add_subregion(address_space, OMAP_CS0_BASE, flash);
@@ -166,7 +168,7 @@ static void sx1_init(MachineState *machine, const int version)
(dinfo = drive_get(IF_PFLASH, 0, fl_idx)) != NULL) {
MemoryRegion *flash_1 = g_new(MemoryRegion, 1);
memory_region_init_ram(flash_1, NULL, "omap_sx1.flash1-0", flash1_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(flash_1);
memory_region_set_readonly(flash_1, true);
memory_region_add_subregion(address_space, OMAP_CS1_BASE, flash_1);
@@ -217,22 +219,38 @@ static void sx1_init_v2(MachineState *machine)
sx1_init(machine, 2);
}
-static QEMUMachine sx1_machine_v2 = {
- .name = "sx1",
- .desc = "Siemens SX1 (OMAP310) V2",
- .init = sx1_init_v2,
+static void sx1_machine_v2_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Siemens SX1 (OMAP310) V2";
+ mc->init = sx1_init_v2;
+}
+
+static const TypeInfo sx1_machine_v2_type = {
+ .name = MACHINE_TYPE_NAME("sx1"),
+ .parent = TYPE_MACHINE,
+ .class_init = sx1_machine_v2_class_init,
};
-static QEMUMachine sx1_machine_v1 = {
- .name = "sx1-v1",
- .desc = "Siemens SX1 (OMAP310) V1",
- .init = sx1_init_v1,
+static void sx1_machine_v1_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Siemens SX1 (OMAP310) V1";
+ mc->init = sx1_init_v1;
+}
+
+static const TypeInfo sx1_machine_v1_type = {
+ .name = MACHINE_TYPE_NAME("sx1-v1"),
+ .parent = TYPE_MACHINE,
+ .class_init = sx1_machine_v1_class_init,
};
static void sx1_machine_init(void)
{
- qemu_register_machine(&sx1_machine_v2);
- qemu_register_machine(&sx1_machine_v1);
+ type_register_static(&sx1_machine_v1_type);
+ type_register_static(&sx1_machine_v2_type);
}
-machine_init(sx1_machine_init);
+type_init(sx1_machine_init)
diff --git a/qemu/hw/arm/palm.c b/qemu/hw/arm/palm.c
index 7f1cfb8f6..7f460732e 100644
--- a/qemu/hw/arm/palm.c
+++ b/qemu/hw/arm/palm.c
@@ -16,6 +16,8 @@
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "audio/audio.h"
#include "sysemu/sysemu.h"
@@ -213,7 +215,7 @@ static void palmte_init(MachineState *machine)
/* External Flash (EMIFS) */
memory_region_init_ram(flash, NULL, "palmte.flash", flash_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(flash);
memory_region_set_readonly(flash, true);
memory_region_add_subregion(address_space_mem, OMAP_CS0_BASE, flash);
@@ -269,15 +271,10 @@ static void palmte_init(MachineState *machine)
arm_load_kernel(mpu->cpu, &palmte_binfo);
}
-static QEMUMachine palmte_machine = {
- .name = "cheetah",
- .desc = "Palm Tungsten|E aka. Cheetah PDA (OMAP310)",
- .init = palmte_init,
-};
-
-static void palmte_machine_init(void)
+static void palmte_machine_init(MachineClass *mc)
{
- qemu_register_machine(&palmte_machine);
+ mc->desc = "Palm Tungsten|E aka. Cheetah PDA (OMAP310)";
+ mc->init = palmte_init;
}
-machine_init(palmte_machine_init);
+DEFINE_MACHINE("cheetah", palmte_machine_init)
diff --git a/qemu/hw/arm/palmetto-bmc.c b/qemu/hw/arm/palmetto-bmc.c
new file mode 100644
index 000000000..89ebd92b9
--- /dev/null
+++ b/qemu/hw/arm/palmetto-bmc.c
@@ -0,0 +1,68 @@
+/*
+ * OpenPOWER Palmetto BMC
+ *
+ * Andrew Jeffery <andrew@aj.id.au>
+ *
+ * Copyright 2016 IBM Corp.
+ *
+ * This code is licensed under the GPL version 2 or later. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "exec/address-spaces.h"
+#include "hw/arm/arm.h"
+#include "hw/arm/ast2400.h"
+#include "hw/boards.h"
+
+static struct arm_boot_info palmetto_bmc_binfo = {
+ .loader_start = AST2400_SDRAM_BASE,
+ .board_id = 0,
+ .nb_cpus = 1,
+};
+
+typedef struct PalmettoBMCState {
+ AST2400State soc;
+ MemoryRegion ram;
+} PalmettoBMCState;
+
+static void palmetto_bmc_init(MachineState *machine)
+{
+ PalmettoBMCState *bmc;
+
+ bmc = g_new0(PalmettoBMCState, 1);
+ object_initialize(&bmc->soc, (sizeof(bmc->soc)), TYPE_AST2400);
+ object_property_add_child(OBJECT(machine), "soc", OBJECT(&bmc->soc),
+ &error_abort);
+
+ memory_region_allocate_system_memory(&bmc->ram, NULL, "ram", ram_size);
+ memory_region_add_subregion(get_system_memory(), AST2400_SDRAM_BASE,
+ &bmc->ram);
+ object_property_add_const_link(OBJECT(&bmc->soc), "ram", OBJECT(&bmc->ram),
+ &error_abort);
+ object_property_set_bool(OBJECT(&bmc->soc), true, "realized",
+ &error_abort);
+
+ palmetto_bmc_binfo.kernel_filename = machine->kernel_filename;
+ palmetto_bmc_binfo.initrd_filename = machine->initrd_filename;
+ palmetto_bmc_binfo.kernel_cmdline = machine->kernel_cmdline;
+ palmetto_bmc_binfo.ram_size = ram_size;
+ arm_load_kernel(ARM_CPU(first_cpu), &palmetto_bmc_binfo);
+}
+
+static void palmetto_bmc_machine_init(MachineClass *mc)
+{
+ mc->desc = "OpenPOWER Palmetto BMC";
+ mc->init = palmetto_bmc_init;
+ mc->max_cpus = 1;
+ mc->no_sdcard = 1;
+ mc->no_floppy = 1;
+ mc->no_cdrom = 1;
+ mc->no_sdcard = 1;
+ mc->no_parallel = 1;
+}
+
+DEFINE_MACHINE("palmetto-bmc", palmetto_bmc_machine_init);
diff --git a/qemu/hw/arm/pxa2xx.c b/qemu/hw/arm/pxa2xx.c
index ec353f79c..1a8c36033 100644
--- a/qemu/hw/arm/pxa2xx.c
+++ b/qemu/hw/arm/pxa2xx.c
@@ -7,15 +7,20 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/sysbus.h"
#include "hw/arm/pxa.h"
#include "sysemu/sysemu.h"
#include "hw/char/serial.h"
#include "hw/i2c/i2c.h"
-#include "hw/ssi.h"
+#include "hw/ssi/ssi.h"
#include "sysemu/char.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
+#include "qemu/cutils.h"
static struct {
hwaddr io_base;
@@ -1731,8 +1736,7 @@ static PXA2xxI2SState *pxa2xx_i2s_init(MemoryRegion *sysmem,
hwaddr base,
qemu_irq irq, qemu_irq rx_dma, qemu_irq tx_dma)
{
- PXA2xxI2SState *s = (PXA2xxI2SState *)
- g_malloc0(sizeof(PXA2xxI2SState));
+ PXA2xxI2SState *s = g_new0(PXA2xxI2SState, 1);
s->irq = irq;
s->rx_dma = rx_dma;
@@ -1959,7 +1963,7 @@ static void pxa2xx_fir_instance_init(Object *obj)
PXA2xxFIrState *s = PXA2XX_FIR(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
- memory_region_init_io(&s->iomem, NULL, &pxa2xx_fir_ops, s,
+ memory_region_init_io(&s->iomem, obj, &pxa2xx_fir_ops, s,
"pxa2xx-fir", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
@@ -2061,7 +2065,7 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space,
PXA2xxState *s;
int i;
DriveInfo *dinfo;
- s = (PXA2xxState *) g_malloc0(sizeof(PXA2xxState));
+ s = g_new0(PXA2xxState, 1);
if (revision && strncmp(revision, "pxa27", 5)) {
fprintf(stderr, "Machine requires a PXA27x processor.\n");
@@ -2079,11 +2083,11 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space,
/* SDRAM & Internal Memory Storage */
memory_region_init_ram(&s->sdram, NULL, "pxa270.sdram", sdram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&s->sdram);
memory_region_add_subregion(address_space, PXA2XX_SDRAM_BASE, &s->sdram);
memory_region_init_ram(&s->internal, NULL, "pxa270.internal", 0x40000,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&s->internal);
memory_region_add_subregion(address_space, PXA2XX_INTERNAL_BASE,
&s->internal);
@@ -2157,7 +2161,7 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space,
vmstate_register(NULL, 0, &vmstate_pxa2xx_pm, s);
for (i = 0; pxa27x_ssp[i].io_base; i ++);
- s->ssp = (SSIBus **)g_malloc0(sizeof(SSIBus *) * i);
+ s->ssp = g_new0(SSIBus *, i);
for (i = 0; pxa27x_ssp[i].io_base; i ++) {
DeviceState *dev;
dev = sysbus_create_simple(TYPE_PXA2XX_SSP, pxa27x_ssp[i].io_base,
@@ -2202,7 +2206,7 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size)
int i;
DriveInfo *dinfo;
- s = (PXA2xxState *) g_malloc0(sizeof(PXA2xxState));
+ s = g_new0(PXA2xxState, 1);
s->cpu = cpu_arm_init("pxa255");
if (s->cpu == NULL) {
@@ -2213,11 +2217,11 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size)
/* SDRAM & Internal Memory Storage */
memory_region_init_ram(&s->sdram, NULL, "pxa255.sdram", sdram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&s->sdram);
memory_region_add_subregion(address_space, PXA2XX_SDRAM_BASE, &s->sdram);
memory_region_init_ram(&s->internal, NULL, "pxa255.internal",
- PXA2XX_INTERNAL_SIZE, &error_abort);
+ PXA2XX_INTERNAL_SIZE, &error_fatal);
vmstate_register_ram_global(&s->internal);
memory_region_add_subregion(address_space, PXA2XX_INTERNAL_BASE,
&s->internal);
@@ -2290,7 +2294,7 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size)
vmstate_register(NULL, 0, &vmstate_pxa2xx_pm, s);
for (i = 0; pxa255_ssp[i].io_base; i ++);
- s->ssp = (SSIBus **)g_malloc0(sizeof(SSIBus *) * i);
+ s->ssp = g_new0(SSIBus *, i);
for (i = 0; pxa255_ssp[i].io_base; i ++) {
DeviceState *dev;
dev = sysbus_create_simple(TYPE_PXA2XX_SSP, pxa255_ssp[i].io_base,
diff --git a/qemu/hw/arm/pxa2xx_gpio.c b/qemu/hw/arm/pxa2xx_gpio.c
index c89c8045c..67e7e7094 100644
--- a/qemu/hw/arm/pxa2xx_gpio.c
+++ b/qemu/hw/arm/pxa2xx_gpio.c
@@ -7,6 +7,7 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "hw/arm/pxa.h"
diff --git a/qemu/hw/arm/pxa2xx_pic.c b/qemu/hw/arm/pxa2xx_pic.c
index d41ac9341..7e51532cd 100644
--- a/qemu/hw/arm/pxa2xx_pic.c
+++ b/qemu/hw/arm/pxa2xx_pic.c
@@ -8,6 +8,9 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "hw/arm/pxa.h"
#include "hw/sysbus.h"
diff --git a/qemu/hw/arm/raspi.c b/qemu/hw/arm/raspi.c
new file mode 100644
index 000000000..2b295f14c
--- /dev/null
+++ b/qemu/hw/arm/raspi.c
@@ -0,0 +1,172 @@
+/*
+ * Raspberry Pi emulation (c) 2012 Gregory Estrade
+ * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous
+ *
+ * Rasperry Pi 2 emulation Copyright (c) 2015, Microsoft
+ * Written by Andrew Baumann
+ *
+ * This code is licensed under the GNU GPLv2 and later.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "hw/arm/bcm2836.h"
+#include "qemu/error-report.h"
+#include "hw/boards.h"
+#include "hw/loader.h"
+#include "hw/arm/arm.h"
+#include "sysemu/sysemu.h"
+
+#define SMPBOOT_ADDR 0x300 /* this should leave enough space for ATAGS */
+#define MVBAR_ADDR 0x400 /* secure vectors */
+#define BOARDSETUP_ADDR (MVBAR_ADDR + 0x20) /* board setup code */
+#define FIRMWARE_ADDR 0x8000 /* Pi loads kernel.img here by default */
+
+/* Table of Linux board IDs for different Pi versions */
+static const int raspi_boardid[] = {[1] = 0xc42, [2] = 0xc43};
+
+typedef struct RasPiState {
+ BCM2836State soc;
+ MemoryRegion ram;
+} RasPiState;
+
+static void write_smpboot(ARMCPU *cpu, const struct arm_boot_info *info)
+{
+ static const uint32_t smpboot[] = {
+ 0xe1a0e00f, /* mov lr, pc */
+ 0xe3a0fe00 + (BOARDSETUP_ADDR >> 4), /* mov pc, BOARDSETUP_ADDR */
+ 0xee100fb0, /* mrc p15, 0, r0, c0, c0, 5;get core ID */
+ 0xe7e10050, /* ubfx r0, r0, #0, #2 ;extract LSB */
+ 0xe59f5014, /* ldr r5, =0x400000CC ;load mbox base */
+ 0xe320f001, /* 1: yield */
+ 0xe7953200, /* ldr r3, [r5, r0, lsl #4] ;read mbox for our core*/
+ 0xe3530000, /* cmp r3, #0 ;spin while zero */
+ 0x0afffffb, /* beq 1b */
+ 0xe7853200, /* str r3, [r5, r0, lsl #4] ;clear mbox */
+ 0xe12fff13, /* bx r3 ;jump to target */
+ 0x400000cc, /* (constant: mailbox 3 read/clear base) */
+ };
+
+ /* check that we don't overrun board setup vectors */
+ QEMU_BUILD_BUG_ON(SMPBOOT_ADDR + sizeof(smpboot) > MVBAR_ADDR);
+ /* check that board setup address is correctly relocated */
+ QEMU_BUILD_BUG_ON((BOARDSETUP_ADDR & 0xf) != 0
+ || (BOARDSETUP_ADDR >> 4) >= 0x100);
+
+ rom_add_blob_fixed("raspi_smpboot", smpboot, sizeof(smpboot),
+ info->smp_loader_start);
+}
+
+static void write_board_setup(ARMCPU *cpu, const struct arm_boot_info *info)
+{
+ arm_write_secure_board_setup_dummy_smc(cpu, info, MVBAR_ADDR);
+}
+
+static void reset_secondary(ARMCPU *cpu, const struct arm_boot_info *info)
+{
+ CPUState *cs = CPU(cpu);
+ cpu_set_pc(cs, info->smp_loader_start);
+}
+
+static void setup_boot(MachineState *machine, int version, size_t ram_size)
+{
+ static struct arm_boot_info binfo;
+ int r;
+
+ binfo.board_id = raspi_boardid[version];
+ binfo.ram_size = ram_size;
+ binfo.nb_cpus = smp_cpus;
+ binfo.board_setup_addr = BOARDSETUP_ADDR;
+ binfo.write_board_setup = write_board_setup;
+ binfo.secure_board_setup = true;
+ binfo.secure_boot = true;
+
+ /* Pi2 requires SMP setup */
+ if (version == 2) {
+ binfo.smp_loader_start = SMPBOOT_ADDR;
+ binfo.write_secondary_boot = write_smpboot;
+ binfo.secondary_cpu_reset_hook = reset_secondary;
+ }
+
+ /* If the user specified a "firmware" image (e.g. UEFI), we bypass
+ * the normal Linux boot process
+ */
+ if (machine->firmware) {
+ /* load the firmware image (typically kernel.img) */
+ r = load_image_targphys(machine->firmware, FIRMWARE_ADDR,
+ ram_size - FIRMWARE_ADDR);
+ if (r < 0) {
+ error_report("Failed to load firmware from %s", machine->firmware);
+ exit(1);
+ }
+
+ binfo.entry = FIRMWARE_ADDR;
+ binfo.firmware_loaded = true;
+ } else {
+ binfo.kernel_filename = machine->kernel_filename;
+ binfo.kernel_cmdline = machine->kernel_cmdline;
+ binfo.initrd_filename = machine->initrd_filename;
+ }
+
+ arm_load_kernel(ARM_CPU(first_cpu), &binfo);
+}
+
+static void raspi2_init(MachineState *machine)
+{
+ RasPiState *s = g_new0(RasPiState, 1);
+ uint32_t vcram_size;
+ DriveInfo *di;
+ BlockBackend *blk;
+ BusState *bus;
+ DeviceState *carddev;
+
+ object_initialize(&s->soc, sizeof(s->soc), TYPE_BCM2836);
+ object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
+ &error_abort);
+
+ /* Allocate and map RAM */
+ memory_region_allocate_system_memory(&s->ram, OBJECT(machine), "ram",
+ machine->ram_size);
+ /* FIXME: Remove when we have custom CPU address space support */
+ memory_region_add_subregion_overlap(get_system_memory(), 0, &s->ram, 0);
+
+ /* Setup the SOC */
+ object_property_add_const_link(OBJECT(&s->soc), "ram", OBJECT(&s->ram),
+ &error_abort);
+ object_property_set_int(OBJECT(&s->soc), smp_cpus, "enabled-cpus",
+ &error_abort);
+ object_property_set_int(OBJECT(&s->soc), 0xa21041, "board-rev",
+ &error_abort);
+ object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_abort);
+
+ /* Create and plug in the SD cards */
+ di = drive_get_next(IF_SD);
+ blk = di ? blk_by_legacy_dinfo(di) : NULL;
+ bus = qdev_get_child_bus(DEVICE(&s->soc), "sd-bus");
+ if (bus == NULL) {
+ error_report("No SD bus found in SOC object");
+ exit(1);
+ }
+ carddev = qdev_create(bus, TYPE_SD_CARD);
+ qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
+ object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal);
+
+ vcram_size = object_property_get_int(OBJECT(&s->soc), "vcram-size",
+ &error_abort);
+ setup_boot(machine, 2, machine->ram_size - vcram_size);
+}
+
+static void raspi2_machine_init(MachineClass *mc)
+{
+ mc->desc = "Raspberry Pi 2";
+ mc->init = raspi2_init;
+ mc->block_default_type = IF_SD;
+ mc->no_parallel = 1;
+ mc->no_floppy = 1;
+ mc->no_cdrom = 1;
+ mc->max_cpus = BCM2836_NCPUS;
+ mc->default_ram_size = 1024 * 1024 * 1024;
+};
+DEFINE_MACHINE("raspi2", raspi2_machine_init)
diff --git a/qemu/hw/arm/realview.c b/qemu/hw/arm/realview.c
index ef2788d3e..3222b360e 100644
--- a/qemu/hw/arm/realview.c
+++ b/qemu/hw/arm/realview.c
@@ -7,6 +7,10 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/sysbus.h"
#include "hw/arm/arm.h"
#include "hw/arm/primecell.h"
@@ -99,33 +103,21 @@ static void realview_init(MachineState *machine,
for (n = 0; n < smp_cpus; n++) {
Object *cpuobj = object_new(object_class_get_name(cpu_oc));
- Error *err = NULL;
/* By default A9,A15 and ARM1176 CPUs have EL3 enabled. This board
* does not currently support EL3 so the CPU EL3 property is disabled
* before realization.
*/
if (object_property_find(cpuobj, "has_el3", NULL)) {
- object_property_set_bool(cpuobj, false, "has_el3", &err);
- if (err) {
- error_report_err(err);
- exit(1);
- }
+ object_property_set_bool(cpuobj, false, "has_el3", &error_fatal);
}
if (is_pb && is_mpcore) {
- object_property_set_int(cpuobj, periphbase, "reset-cbar", &err);
- if (err) {
- error_report_err(err);
- exit(1);
- }
+ object_property_set_int(cpuobj, periphbase, "reset-cbar",
+ &error_fatal);
}
- object_property_set_bool(cpuobj, true, "realized", &err);
- if (err) {
- error_report_err(err);
- exit(1);
- }
+ object_property_set_bool(cpuobj, true, "realized", &error_fatal);
cpu_irq[n] = qdev_get_gpio_in(DEVICE(cpuobj), ARM_CPU_IRQ);
}
@@ -151,13 +143,13 @@ static void realview_init(MachineState *machine,
low_ram_size = ram_size - 0x20000000;
ram_size = 0x20000000;
memory_region_init_ram(ram_lo, NULL, "realview.lowmem", low_ram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(ram_lo);
memory_region_add_subregion(sysmem, 0x20000000, ram_lo);
}
memory_region_init_ram(ram_hi, NULL, "realview.highmem", ram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(ram_hi);
low_ram_size = ram_size;
if (low_ram_size > 0x10000000)
@@ -353,7 +345,7 @@ static void realview_init(MachineState *machine,
BootROM happens to be in ROM/flash or in memory that isn't clobbered
until after Linux boots the secondary CPUs. */
memory_region_init_ram(ram_hack, NULL, "realview.hack", 0x1000,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(ram_hack);
memory_region_add_subregion(sysmem, SMP_BOOT_ADDR, ram_hack);
@@ -399,41 +391,73 @@ static void realview_pbx_a9_init(MachineState *machine)
realview_init(machine, BOARD_PBX_A9);
}
-static QEMUMachine realview_eb_machine = {
- .name = "realview-eb",
- .desc = "ARM RealView Emulation Baseboard (ARM926EJ-S)",
- .init = realview_eb_init,
- .block_default_type = IF_SCSI,
+static void realview_eb_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "ARM RealView Emulation Baseboard (ARM926EJ-S)";
+ mc->init = realview_eb_init;
+ mc->block_default_type = IF_SCSI;
+}
+
+static const TypeInfo realview_eb_type = {
+ .name = MACHINE_TYPE_NAME("realview-eb"),
+ .parent = TYPE_MACHINE,
+ .class_init = realview_eb_class_init,
};
-static QEMUMachine realview_eb_mpcore_machine = {
- .name = "realview-eb-mpcore",
- .desc = "ARM RealView Emulation Baseboard (ARM11MPCore)",
- .init = realview_eb_mpcore_init,
- .block_default_type = IF_SCSI,
- .max_cpus = 4,
+static void realview_eb_mpcore_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "ARM RealView Emulation Baseboard (ARM11MPCore)";
+ mc->init = realview_eb_mpcore_init;
+ mc->block_default_type = IF_SCSI;
+ mc->max_cpus = 4;
+}
+
+static const TypeInfo realview_eb_mpcore_type = {
+ .name = MACHINE_TYPE_NAME("realview-eb-mpcore"),
+ .parent = TYPE_MACHINE,
+ .class_init = realview_eb_mpcore_class_init,
};
-static QEMUMachine realview_pb_a8_machine = {
- .name = "realview-pb-a8",
- .desc = "ARM RealView Platform Baseboard for Cortex-A8",
- .init = realview_pb_a8_init,
+static void realview_pb_a8_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "ARM RealView Platform Baseboard for Cortex-A8";
+ mc->init = realview_pb_a8_init;
+}
+
+static const TypeInfo realview_pb_a8_type = {
+ .name = MACHINE_TYPE_NAME("realview-pb-a8"),
+ .parent = TYPE_MACHINE,
+ .class_init = realview_pb_a8_class_init,
};
-static QEMUMachine realview_pbx_a9_machine = {
- .name = "realview-pbx-a9",
- .desc = "ARM RealView Platform Baseboard Explore for Cortex-A9",
- .init = realview_pbx_a9_init,
- .block_default_type = IF_SCSI,
- .max_cpus = 4,
+static void realview_pbx_a9_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "ARM RealView Platform Baseboard Explore for Cortex-A9";
+ mc->init = realview_pbx_a9_init;
+ mc->block_default_type = IF_SCSI;
+ mc->max_cpus = 4;
+}
+
+static const TypeInfo realview_pbx_a9_type = {
+ .name = MACHINE_TYPE_NAME("realview-pbx-a9"),
+ .parent = TYPE_MACHINE,
+ .class_init = realview_pbx_a9_class_init,
};
static void realview_machine_init(void)
{
- qemu_register_machine(&realview_eb_machine);
- qemu_register_machine(&realview_eb_mpcore_machine);
- qemu_register_machine(&realview_pb_a8_machine);
- qemu_register_machine(&realview_pbx_a9_machine);
+ type_register_static(&realview_eb_type);
+ type_register_static(&realview_eb_mpcore_type);
+ type_register_static(&realview_pb_a8_type);
+ type_register_static(&realview_pbx_a9_type);
}
-machine_init(realview_machine_init);
+type_init(realview_machine_init)
diff --git a/qemu/hw/arm/spitz.c b/qemu/hw/arm/spitz.c
index 5bf032a63..bf61d63b5 100644
--- a/qemu/hw/arm/spitz.c
+++ b/qemu/hw/arm/spitz.c
@@ -10,13 +10,15 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/arm/pxa.h"
#include "hw/arm/arm.h"
#include "sysemu/sysemu.h"
#include "hw/pcmcia.h"
#include "hw/i2c/i2c.h"
-#include "hw/ssi.h"
+#include "hw/ssi/ssi.h"
#include "hw/block/flash.h"
#include "qemu/timer.h"
#include "hw/devices.h"
@@ -403,7 +405,7 @@ static void spitz_keyboard_tick(void *opaque)
}
timer_mod(s->kbdtimer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
- get_ticks_per_sec() / 32);
+ NANOSECONDS_PER_SECOND / 32);
}
static void spitz_keyboard_pre_map(SpitzKeyboardState *s)
@@ -913,7 +915,7 @@ static void spitz_common_init(MachineState *machine,
sl_flash_register(mpu, (model == spitz) ? FLASH_128M : FLASH_1024M);
- memory_region_init_ram(rom, NULL, "spitz.rom", SPITZ_ROM, &error_abort);
+ memory_region_init_ram(rom, NULL, "spitz.rom", SPITZ_ROM, &error_fatal);
vmstate_register_ram_global(rom);
memory_region_set_readonly(rom, true);
memory_region_add_subregion(address_space_mem, 0, rom);
@@ -972,39 +974,71 @@ static void terrier_init(MachineState *machine)
spitz_common_init(machine, terrier, 0x33f);
}
-static QEMUMachine akitapda_machine = {
- .name = "akita",
- .desc = "Akita PDA (PXA270)",
- .init = akita_init,
+static void akitapda_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sharp SL-C1000 (Akita) PDA (PXA270)";
+ mc->init = akita_init;
+}
+
+static const TypeInfo akitapda_type = {
+ .name = MACHINE_TYPE_NAME("akita"),
+ .parent = TYPE_MACHINE,
+ .class_init = akitapda_class_init,
};
-static QEMUMachine spitzpda_machine = {
- .name = "spitz",
- .desc = "Spitz PDA (PXA270)",
- .init = spitz_init,
+static void spitzpda_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sharp SL-C3000 (Spitz) PDA (PXA270)";
+ mc->init = spitz_init;
+}
+
+static const TypeInfo spitzpda_type = {
+ .name = MACHINE_TYPE_NAME("spitz"),
+ .parent = TYPE_MACHINE,
+ .class_init = spitzpda_class_init,
};
-static QEMUMachine borzoipda_machine = {
- .name = "borzoi",
- .desc = "Borzoi PDA (PXA270)",
- .init = borzoi_init,
+static void borzoipda_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sharp SL-C3100 (Borzoi) PDA (PXA270)";
+ mc->init = borzoi_init;
+}
+
+static const TypeInfo borzoipda_type = {
+ .name = MACHINE_TYPE_NAME("borzoi"),
+ .parent = TYPE_MACHINE,
+ .class_init = borzoipda_class_init,
};
-static QEMUMachine terrierpda_machine = {
- .name = "terrier",
- .desc = "Terrier PDA (PXA270)",
- .init = terrier_init,
+static void terrierpda_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sharp SL-C3200 (Terrier) PDA (PXA270)";
+ mc->init = terrier_init;
+}
+
+static const TypeInfo terrierpda_type = {
+ .name = MACHINE_TYPE_NAME("terrier"),
+ .parent = TYPE_MACHINE,
+ .class_init = terrierpda_class_init,
};
static void spitz_machine_init(void)
{
- qemu_register_machine(&akitapda_machine);
- qemu_register_machine(&spitzpda_machine);
- qemu_register_machine(&borzoipda_machine);
- qemu_register_machine(&terrierpda_machine);
+ type_register_static(&akitapda_type);
+ type_register_static(&spitzpda_type);
+ type_register_static(&borzoipda_type);
+ type_register_static(&terrierpda_type);
}
-machine_init(spitz_machine_init);
+type_init(spitz_machine_init)
static bool is_version_0(void *opaque, int version_id)
{
@@ -1060,10 +1094,6 @@ static VMStateDescription vmstate_spitz_kbd = {
},
};
-static Property spitz_keyboard_properties[] = {
- DEFINE_PROP_END_OF_LIST(),
-};
-
static void spitz_keyboard_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -1071,7 +1101,6 @@ static void spitz_keyboard_class_init(ObjectClass *klass, void *data)
k->init = spitz_keyboard_init;
dc->vmsd = &vmstate_spitz_kbd;
- dc->props = spitz_keyboard_properties;
}
static const TypeInfo spitz_keyboard_info = {
diff --git a/qemu/hw/arm/stellaris.c b/qemu/hw/arm/stellaris.c
index cb515ec76..c1766f856 100644
--- a/qemu/hw/arm/stellaris.c
+++ b/qemu/hw/arm/stellaris.c
@@ -7,8 +7,10 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/sysbus.h"
-#include "hw/ssi.h"
+#include "hw/ssi/ssi.h"
#include "hw/arm/arm.h"
#include "hw/devices.h"
#include "qemu/timer.h"
@@ -16,6 +18,7 @@
#include "net/net.h"
#include "hw/boards.h"
#include "exec/address-spaces.h"
+#include "sysemu/sysemu.h"
#define GPIO_A 0
#define GPIO_B 1
@@ -98,7 +101,7 @@ static void gptm_reload(gptm_state *s, int n, int reset)
tick += (int64_t)count * system_clock_scale;
} else if (s->config == 1) {
/* 32-bit RTC. 1Hz tick. */
- tick += get_ticks_per_sec();
+ tick += NANOSECONDS_PER_SECOND;
} else if (s->mode[n] == 0xa) {
/* PWM mode. Not implemented. */
} else {
@@ -675,7 +678,7 @@ static int stellaris_sys_init(uint32_t base, qemu_irq irq,
{
ssys_state *s;
- s = (ssys_state *)g_malloc0(sizeof(ssys_state));
+ s = g_new0(ssys_state, 1);
s->irq = irq;
s->board = board;
/* Most devices come preprogrammed with a MAC address in the user data. */
@@ -1176,6 +1179,14 @@ static int stellaris_adc_init(SysBusDevice *sbd)
return 0;
}
+static
+void do_sys_reset(void *opaque, int n, int level)
+{
+ if (level) {
+ qemu_system_reset_request();
+ }
+}
+
/* Board init. */
static stellaris_board_info stellaris_boards[] = {
{ "LM3S811EVB",
@@ -1210,8 +1221,7 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model,
0x40024000, 0x40025000, 0x40026000};
static const int gpio_irq[7] = {0, 1, 2, 3, 4, 30, 31};
- qemu_irq *pic;
- DeviceState *gpio_dev[7];
+ DeviceState *gpio_dev[7], *nvic;
qemu_irq gpio_in[7][8];
qemu_irq gpio_out[7][8];
qemu_irq adc;
@@ -1231,22 +1241,29 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model,
/* Flash programming is done via the SCU, so pretend it is ROM. */
memory_region_init_ram(flash, NULL, "stellaris.flash", flash_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(flash);
memory_region_set_readonly(flash, true);
memory_region_add_subregion(system_memory, 0, flash);
memory_region_init_ram(sram, NULL, "stellaris.sram", sram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(sram);
memory_region_add_subregion(system_memory, 0x20000000, sram);
- pic = armv7m_init(system_memory, flash_size, NUM_IRQ_LINES,
+ nvic = armv7m_init(system_memory, flash_size, NUM_IRQ_LINES,
kernel_filename, cpu_model);
+ qdev_connect_gpio_out_named(nvic, "SYSRESETREQ", 0,
+ qemu_allocate_irq(&do_sys_reset, NULL, 0));
+
if (board->dc1 & (1 << 16)) {
dev = sysbus_create_varargs(TYPE_STELLARIS_ADC, 0x40038000,
- pic[14], pic[15], pic[16], pic[17], NULL);
+ qdev_get_gpio_in(nvic, 14),
+ qdev_get_gpio_in(nvic, 15),
+ qdev_get_gpio_in(nvic, 16),
+ qdev_get_gpio_in(nvic, 17),
+ NULL);
adc = qdev_get_gpio_in(dev, 0);
} else {
adc = NULL;
@@ -1255,19 +1272,21 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model,
if (board->dc2 & (0x10000 << i)) {
dev = sysbus_create_simple(TYPE_STELLARIS_GPTM,
0x40030000 + i * 0x1000,
- pic[timer_irq[i]]);
+ qdev_get_gpio_in(nvic, timer_irq[i]));
/* TODO: This is incorrect, but we get away with it because
the ADC output is only ever pulsed. */
qdev_connect_gpio_out(dev, 0, adc);
}
}
- stellaris_sys_init(0x400fe000, pic[28], board, nd_table[0].macaddr.a);
+ stellaris_sys_init(0x400fe000, qdev_get_gpio_in(nvic, 28),
+ board, nd_table[0].macaddr.a);
for (i = 0; i < 7; i++) {
if (board->dc4 & (1 << i)) {
gpio_dev[i] = sysbus_create_simple("pl061_luminary", gpio_addr[i],
- pic[gpio_irq[i]]);
+ qdev_get_gpio_in(nvic,
+ gpio_irq[i]));
for (j = 0; j < 8; j++) {
gpio_in[i][j] = qdev_get_gpio_in(gpio_dev[i], j);
gpio_out[i][j] = NULL;
@@ -1276,7 +1295,8 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model,
}
if (board->dc2 & (1 << 12)) {
- dev = sysbus_create_simple(TYPE_STELLARIS_I2C, 0x40020000, pic[8]);
+ dev = sysbus_create_simple(TYPE_STELLARIS_I2C, 0x40020000,
+ qdev_get_gpio_in(nvic, 8));
i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c");
if (board->peripherals & BP_OLED_I2C) {
i2c_create_slave(i2c, "ssd0303", 0x3d);
@@ -1286,11 +1306,12 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model,
for (i = 0; i < 4; i++) {
if (board->dc2 & (1 << i)) {
sysbus_create_simple("pl011_luminary", 0x4000c000 + i * 0x1000,
- pic[uart_irq[i]]);
+ qdev_get_gpio_in(nvic, uart_irq[i]));
}
}
if (board->dc2 & (1 << 4)) {
- dev = sysbus_create_simple("pl022", 0x40008000, pic[7]);
+ dev = sysbus_create_simple("pl022", 0x40008000,
+ qdev_get_gpio_in(nvic, 7));
if (board->peripherals & BP_OLED_SSI) {
void *bus;
DeviceState *sddev;
@@ -1326,7 +1347,7 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model,
qdev_set_nic_properties(enet, &nd_table[0]);
qdev_init_nofail(enet);
sysbus_mmio_map(SYS_BUS_DEVICE(enet), 0, 0x40048000);
- sysbus_connect_irq(SYS_BUS_DEVICE(enet), 0, pic[42]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(enet), 0, qdev_get_gpio_in(nvic, 42));
}
if (board->peripherals & BP_GAMEPAD) {
qemu_irq gpad_irq[5];
@@ -1366,25 +1387,41 @@ static void lm3s6965evb_init(MachineState *machine)
stellaris_init(kernel_filename, cpu_model, &stellaris_boards[1]);
}
-static QEMUMachine lm3s811evb_machine = {
- .name = "lm3s811evb",
- .desc = "Stellaris LM3S811EVB",
- .init = lm3s811evb_init,
+static void lm3s811evb_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Stellaris LM3S811EVB";
+ mc->init = lm3s811evb_init;
+}
+
+static const TypeInfo lm3s811evb_type = {
+ .name = MACHINE_TYPE_NAME("lm3s811evb"),
+ .parent = TYPE_MACHINE,
+ .class_init = lm3s811evb_class_init,
};
-static QEMUMachine lm3s6965evb_machine = {
- .name = "lm3s6965evb",
- .desc = "Stellaris LM3S6965EVB",
- .init = lm3s6965evb_init,
+static void lm3s6965evb_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Stellaris LM3S6965EVB";
+ mc->init = lm3s6965evb_init;
+}
+
+static const TypeInfo lm3s6965evb_type = {
+ .name = MACHINE_TYPE_NAME("lm3s6965evb"),
+ .parent = TYPE_MACHINE,
+ .class_init = lm3s6965evb_class_init,
};
static void stellaris_machine_init(void)
{
- qemu_register_machine(&lm3s811evb_machine);
- qemu_register_machine(&lm3s6965evb_machine);
+ type_register_static(&lm3s811evb_type);
+ type_register_static(&lm3s6965evb_type);
}
-machine_init(stellaris_machine_init);
+type_init(stellaris_machine_init)
static void stellaris_i2c_class_init(ObjectClass *klass, void *data)
{
diff --git a/qemu/hw/arm/stm32f205_soc.c b/qemu/hw/arm/stm32f205_soc.c
index 0f3bdc77b..a5ea1e237 100644
--- a/qemu/hw/arm/stm32f205_soc.c
+++ b/qemu/hw/arm/stm32f205_soc.c
@@ -22,6 +22,10 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/arm/arm.h"
#include "exec/address-spaces.h"
#include "hw/arm/stm32f205_soc.h"
@@ -59,9 +63,8 @@ static void stm32f205_soc_initfn(Object *obj)
static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp)
{
STM32F205State *s = STM32F205_SOC(dev_soc);
- DeviceState *syscfgdev, *usartdev, *timerdev;
+ DeviceState *syscfgdev, *usartdev, *timerdev, *nvic;
SysBusDevice *syscfgbusdev, *usartbusdev, *timerbusdev;
- qemu_irq *pic;
Error *err = NULL;
int i;
@@ -71,7 +74,7 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp)
MemoryRegion *flash_alias = g_new(MemoryRegion, 1);
memory_region_init_ram(flash, NULL, "STM32F205.flash", FLASH_SIZE,
- &error_abort);
+ &error_fatal);
memory_region_init_alias(flash_alias, NULL, "STM32F205.flash.alias",
flash, 0, FLASH_SIZE);
@@ -84,12 +87,12 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp)
memory_region_add_subregion(system_memory, 0, flash_alias);
memory_region_init_ram(sram, NULL, "STM32F205.sram", SRAM_SIZE,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(sram);
memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, sram);
- pic = armv7m_init(get_system_memory(), FLASH_SIZE, 96,
- s->kernel_filename, s->cpu_model);
+ nvic = armv7m_init(get_system_memory(), FLASH_SIZE, 96,
+ s->kernel_filename, s->cpu_model);
/* System configuration controller */
syscfgdev = DEVICE(&s->syscfg);
@@ -100,7 +103,7 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp)
}
syscfgbusdev = SYS_BUS_DEVICE(syscfgdev);
sysbus_mmio_map(syscfgbusdev, 0, 0x40013800);
- sysbus_connect_irq(syscfgbusdev, 0, pic[71]);
+ sysbus_connect_irq(syscfgbusdev, 0, qdev_get_gpio_in(nvic, 71));
/* Attach UART (uses USART registers) and USART controllers */
for (i = 0; i < STM_NUM_USARTS; i++) {
@@ -112,7 +115,8 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp)
}
usartbusdev = SYS_BUS_DEVICE(usartdev);
sysbus_mmio_map(usartbusdev, 0, usart_addr[i]);
- sysbus_connect_irq(usartbusdev, 0, pic[usart_irq[i]]);
+ sysbus_connect_irq(usartbusdev, 0,
+ qdev_get_gpio_in(nvic, usart_irq[i]));
}
/* Timer 2 to 5 */
@@ -126,7 +130,8 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp)
}
timerbusdev = SYS_BUS_DEVICE(timerdev);
sysbus_mmio_map(timerbusdev, 0, timer_addr[i]);
- sysbus_connect_irq(timerbusdev, 0, pic[timer_irq[i]]);
+ sysbus_connect_irq(timerbusdev, 0,
+ qdev_get_gpio_in(nvic, timer_irq[i]));
}
}
diff --git a/qemu/hw/arm/strongarm.c b/qemu/hw/arm/strongarm.c
index da9fc1d51..1eeb1ab39 100644
--- a/qemu/hw/arm/strongarm.c
+++ b/qemu/hw/arm/strongarm.c
@@ -27,6 +27,8 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
+#include "cpu.h"
#include "hw/boards.h"
#include "hw/sysbus.h"
#include "strongarm.h"
@@ -34,7 +36,8 @@
#include "hw/arm/arm.h"
#include "sysemu/char.h"
#include "sysemu/sysemu.h"
-#include "hw/ssi.h"
+#include "hw/ssi/ssi.h"
+#include "qemu/cutils.h"
//#define DEBUG
@@ -1023,7 +1026,7 @@ static void strongarm_uart_update_parameters(StrongARMUARTState *s)
ssp.parity = parity;
ssp.data_bits = data_bits;
ssp.stop_bits = stop_bits;
- s->char_transmit_time = (get_ticks_per_sec() / speed) * frame_size;
+ s->char_transmit_time = (NANOSECONDS_PER_SECOND / speed) * frame_size;
if (s->chr) {
qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
}
@@ -1588,7 +1591,7 @@ StrongARMState *sa1110_init(MemoryRegion *sysmem,
StrongARMState *s;
int i;
- s = g_malloc0(sizeof(StrongARMState));
+ s = g_new0(StrongARMState, 1);
if (!rev) {
rev = "sa1110-b5";
diff --git a/qemu/hw/arm/sysbus-fdt.c b/qemu/hw/arm/sysbus-fdt.c
index 9d28797c8..5debb3348 100644
--- a/qemu/hw/arm/sysbus-fdt.c
+++ b/qemu/hw/arm/sysbus-fdt.c
@@ -21,6 +21,13 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include <libfdt.h>
+#include "qemu-common.h"
+#ifdef CONFIG_LINUX
+#include <linux/vfio.h>
+#endif
#include "hw/arm/sysbus-fdt.h"
#include "qemu/error-report.h"
#include "sysemu/device_tree.h"
@@ -28,6 +35,7 @@
#include "sysemu/sysemu.h"
#include "hw/vfio/vfio-platform.h"
#include "hw/vfio/vfio-calxeda-xgmac.h"
+#include "hw/vfio/vfio-amd-xgbe.h"
#include "hw/arm/fdt.h"
/*
@@ -56,6 +64,146 @@ typedef struct NodeCreationPair {
int (*add_fdt_node_fn)(SysBusDevice *sbdev, void *opaque);
} NodeCreationPair;
+/* helpers */
+
+typedef struct HostProperty {
+ const char *name;
+ bool optional;
+} HostProperty;
+
+#ifdef CONFIG_LINUX
+
+/**
+ * copy_properties_from_host
+ *
+ * copies properties listed in an array from host device tree to
+ * guest device tree. If a non optional property is not found, the
+ * function asserts. An optional property is ignored if not found
+ * in the host device tree.
+ * @props: array of HostProperty to copy
+ * @nb_props: number of properties in the array
+ * @host_dt: host device tree blob
+ * @guest_dt: guest device tree blob
+ * @node_path: host dt node path where the property is supposed to be
+ found
+ * @nodename: guest node name the properties should be added to
+ */
+static void copy_properties_from_host(HostProperty *props, int nb_props,
+ void *host_fdt, void *guest_fdt,
+ char *node_path, char *nodename)
+{
+ int i, prop_len;
+ const void *r;
+ Error *err = NULL;
+
+ for (i = 0; i < nb_props; i++) {
+ r = qemu_fdt_getprop(host_fdt, node_path,
+ props[i].name,
+ &prop_len,
+ props[i].optional ? &err : &error_fatal);
+ if (r) {
+ qemu_fdt_setprop(guest_fdt, nodename,
+ props[i].name, r, prop_len);
+ } else {
+ if (prop_len != -FDT_ERR_NOTFOUND) {
+ /* optional property not returned although property exists */
+ error_report_err(err);
+ } else {
+ error_free(err);
+ }
+ }
+ }
+}
+
+/* clock properties whose values are copied/pasted from host */
+static HostProperty clock_copied_properties[] = {
+ {"compatible", false},
+ {"#clock-cells", false},
+ {"clock-frequency", true},
+ {"clock-output-names", true},
+};
+
+/**
+ * fdt_build_clock_node
+ *
+ * Build a guest clock node, used as a dependency from a passthrough'ed
+ * device. Most information are retrieved from the host clock node.
+ * Also check the host clock is a fixed one.
+ *
+ * @host_fdt: host device tree blob from which info are retrieved
+ * @guest_fdt: guest device tree blob where the clock node is added
+ * @host_phandle: phandle of the clock in host device tree
+ * @guest_phandle: phandle to assign to the guest node
+ */
+static void fdt_build_clock_node(void *host_fdt, void *guest_fdt,
+ uint32_t host_phandle,
+ uint32_t guest_phandle)
+{
+ char *node_path = NULL;
+ char *nodename;
+ const void *r;
+ int ret, node_offset, prop_len, path_len = 16;
+
+ node_offset = fdt_node_offset_by_phandle(host_fdt, host_phandle);
+ if (node_offset <= 0) {
+ error_setg(&error_fatal,
+ "not able to locate clock handle %d in host device tree",
+ host_phandle);
+ }
+ node_path = g_malloc(path_len);
+ while ((ret = fdt_get_path(host_fdt, node_offset, node_path, path_len))
+ == -FDT_ERR_NOSPACE) {
+ path_len += 16;
+ node_path = g_realloc(node_path, path_len);
+ }
+ if (ret < 0) {
+ error_setg(&error_fatal,
+ "not able to retrieve node path for clock handle %d",
+ host_phandle);
+ }
+
+ r = qemu_fdt_getprop(host_fdt, node_path, "compatible", &prop_len,
+ &error_fatal);
+ if (strcmp(r, "fixed-clock")) {
+ error_setg(&error_fatal,
+ "clock handle %d is not a fixed clock", host_phandle);
+ }
+
+ nodename = strrchr(node_path, '/');
+ qemu_fdt_add_subnode(guest_fdt, nodename);
+
+ copy_properties_from_host(clock_copied_properties,
+ ARRAY_SIZE(clock_copied_properties),
+ host_fdt, guest_fdt,
+ node_path, nodename);
+
+ qemu_fdt_setprop_cell(guest_fdt, nodename, "phandle", guest_phandle);
+
+ g_free(node_path);
+}
+
+/**
+ * sysfs_to_dt_name: convert the name found in sysfs into the node name
+ * for instance e0900000.xgmac is converted into xgmac@e0900000
+ * @sysfs_name: directory name in sysfs
+ *
+ * returns the device tree name upon success or NULL in case the sysfs name
+ * does not match the expected format
+ */
+static char *sysfs_to_dt_name(const char *sysfs_name)
+{
+ gchar **substrings = g_strsplit(sysfs_name, ".", 2);
+ char *dt_name = NULL;
+
+ if (!substrings || !substrings[0] || !substrings[1]) {
+ goto out;
+ }
+ dt_name = g_strdup_printf("%s@%s", substrings[1], substrings[0]);
+out:
+ g_strfreev(substrings);
+ return dt_name;
+}
+
/* Device Specific Code */
/**
@@ -70,7 +218,7 @@ static int add_calxeda_midway_xgmac_fdt_node(SysBusDevice *sbdev, void *opaque)
PlatformBusDevice *pbus = data->pbus;
void *fdt = data->fdt;
const char *parent_node = data->pbus_node_name;
- int compat_str_len, i, ret = -1;
+ int compat_str_len, i;
char *nodename;
uint32_t *irq_attr, *reg_attr;
uint64_t mmio_base, irq_number;
@@ -93,14 +241,10 @@ static int add_calxeda_midway_xgmac_fdt_node(SysBusDevice *sbdev, void *opaque)
mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i);
reg_attr[2 * i] = cpu_to_be32(mmio_base);
reg_attr[2 * i + 1] = cpu_to_be32(
- memory_region_size(&vdev->regions[i]->mem));
- }
- ret = qemu_fdt_setprop(fdt, nodename, "reg", reg_attr,
- vbasedev->num_regions * 2 * sizeof(uint32_t));
- if (ret) {
- error_report("could not set reg property of node %s", nodename);
- goto fail_reg;
+ memory_region_size(vdev->regions[i]->mem));
}
+ qemu_fdt_setprop(fdt, nodename, "reg", reg_attr,
+ vbasedev->num_regions * 2 * sizeof(uint32_t));
irq_attr = g_new(uint32_t, vbasedev->num_irqs * 3);
for (i = 0; i < vbasedev->num_irqs; i++) {
@@ -110,22 +254,173 @@ static int add_calxeda_midway_xgmac_fdt_node(SysBusDevice *sbdev, void *opaque)
irq_attr[3 * i + 1] = cpu_to_be32(irq_number);
irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_LEVEL_HI);
}
- ret = qemu_fdt_setprop(fdt, nodename, "interrupts",
+ qemu_fdt_setprop(fdt, nodename, "interrupts",
irq_attr, vbasedev->num_irqs * 3 * sizeof(uint32_t));
- if (ret) {
- error_report("could not set interrupts property of node %s",
- nodename);
+ g_free(irq_attr);
+ g_free(reg_attr);
+ g_free(nodename);
+ return 0;
+}
+
+/* AMD xgbe properties whose values are copied/pasted from host */
+static HostProperty amd_xgbe_copied_properties[] = {
+ {"compatible", false},
+ {"dma-coherent", true},
+ {"amd,per-channel-interrupt", true},
+ {"phy-mode", false},
+ {"mac-address", true},
+ {"amd,speed-set", false},
+ {"amd,serdes-blwc", true},
+ {"amd,serdes-cdr-rate", true},
+ {"amd,serdes-pq-skew", true},
+ {"amd,serdes-tx-amp", true},
+ {"amd,serdes-dfe-tap-config", true},
+ {"amd,serdes-dfe-tap-enable", true},
+ {"clock-names", false},
+};
+
+/**
+ * add_amd_xgbe_fdt_node
+ *
+ * Generates the combined xgbe/phy node following kernel >=4.2
+ * binding documentation:
+ * Documentation/devicetree/bindings/net/amd-xgbe.txt:
+ * Also 2 clock nodes are created (dma and ptp)
+ *
+ * Asserts in case of error
+ */
+static int add_amd_xgbe_fdt_node(SysBusDevice *sbdev, void *opaque)
+{
+ PlatformBusFDTData *data = opaque;
+ PlatformBusDevice *pbus = data->pbus;
+ VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev);
+ VFIODevice *vbasedev = &vdev->vbasedev;
+ VFIOINTp *intp;
+ const char *parent_node = data->pbus_node_name;
+ char **node_path, *nodename, *dt_name;
+ void *guest_fdt = data->fdt, *host_fdt;
+ const void *r;
+ int i, prop_len;
+ uint32_t *irq_attr, *reg_attr, *host_clock_phandles;
+ uint64_t mmio_base, irq_number;
+ uint32_t guest_clock_phandles[2];
+
+ host_fdt = load_device_tree_from_sysfs();
+
+ dt_name = sysfs_to_dt_name(vbasedev->name);
+ if (!dt_name) {
+ error_setg(&error_fatal, "%s incorrect sysfs device name %s",
+ __func__, vbasedev->name);
+ }
+ node_path = qemu_fdt_node_path(host_fdt, dt_name, vdev->compat,
+ &error_fatal);
+ if (!node_path || !node_path[0]) {
+ error_setg(&error_fatal, "%s unable to retrieve node path for %s/%s",
+ __func__, dt_name, vdev->compat);
}
+
+ if (node_path[1]) {
+ error_setg(&error_fatal, "%s more than one node matching %s/%s!",
+ __func__, dt_name, vdev->compat);
+ }
+
+ g_free(dt_name);
+
+ if (vbasedev->num_regions != 5) {
+ error_setg(&error_fatal, "%s Does the host dt node combine XGBE/PHY?",
+ __func__);
+ }
+
+ /* generate nodes for DMA_CLK and PTP_CLK */
+ r = qemu_fdt_getprop(host_fdt, node_path[0], "clocks",
+ &prop_len, &error_fatal);
+ if (prop_len != 8) {
+ error_setg(&error_fatal, "%s clocks property should contain 2 handles",
+ __func__);
+ }
+ host_clock_phandles = (uint32_t *)r;
+ guest_clock_phandles[0] = qemu_fdt_alloc_phandle(guest_fdt);
+ guest_clock_phandles[1] = qemu_fdt_alloc_phandle(guest_fdt);
+
+ /**
+ * clock handles fetched from host dt are in be32 layout whereas
+ * rest of the code uses cpu layout. Also guest clock handles are
+ * in cpu layout.
+ */
+ fdt_build_clock_node(host_fdt, guest_fdt,
+ be32_to_cpu(host_clock_phandles[0]),
+ guest_clock_phandles[0]);
+
+ fdt_build_clock_node(host_fdt, guest_fdt,
+ be32_to_cpu(host_clock_phandles[1]),
+ guest_clock_phandles[1]);
+
+ /* combined XGBE/PHY node */
+ mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0);
+ nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node,
+ vbasedev->name, mmio_base);
+ qemu_fdt_add_subnode(guest_fdt, nodename);
+
+ copy_properties_from_host(amd_xgbe_copied_properties,
+ ARRAY_SIZE(amd_xgbe_copied_properties),
+ host_fdt, guest_fdt,
+ node_path[0], nodename);
+
+ qemu_fdt_setprop_cells(guest_fdt, nodename, "clocks",
+ guest_clock_phandles[0],
+ guest_clock_phandles[1]);
+
+ reg_attr = g_new(uint32_t, vbasedev->num_regions * 2);
+ for (i = 0; i < vbasedev->num_regions; i++) {
+ mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i);
+ reg_attr[2 * i] = cpu_to_be32(mmio_base);
+ reg_attr[2 * i + 1] = cpu_to_be32(
+ memory_region_size(vdev->regions[i]->mem));
+ }
+ qemu_fdt_setprop(guest_fdt, nodename, "reg", reg_attr,
+ vbasedev->num_regions * 2 * sizeof(uint32_t));
+
+ irq_attr = g_new(uint32_t, vbasedev->num_irqs * 3);
+ for (i = 0; i < vbasedev->num_irqs; i++) {
+ irq_number = platform_bus_get_irqn(pbus, sbdev , i)
+ + data->irq_start;
+ irq_attr[3 * i] = cpu_to_be32(GIC_FDT_IRQ_TYPE_SPI);
+ irq_attr[3 * i + 1] = cpu_to_be32(irq_number);
+ /*
+ * General device interrupt and PCS auto-negotiation interrupts are
+ * level-sensitive while the 4 per-channel interrupts are edge
+ * sensitive
+ */
+ QLIST_FOREACH(intp, &vdev->intp_list, next) {
+ if (intp->pin == i) {
+ break;
+ }
+ }
+ if (intp->flags & VFIO_IRQ_INFO_AUTOMASKED) {
+ irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_LEVEL_HI);
+ } else {
+ irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_EDGE_LO_HI);
+ }
+ }
+ qemu_fdt_setprop(guest_fdt, nodename, "interrupts",
+ irq_attr, vbasedev->num_irqs * 3 * sizeof(uint32_t));
+
+ g_free(host_fdt);
+ g_strfreev(node_path);
g_free(irq_attr);
-fail_reg:
g_free(reg_attr);
g_free(nodename);
- return ret;
+ return 0;
}
+#endif /* CONFIG_LINUX */
+
/* list of supported dynamic sysbus devices */
static const NodeCreationPair add_fdt_node_functions[] = {
+#ifdef CONFIG_LINUX
{TYPE_VFIO_CALXEDA_XGMAC, add_calxeda_midway_xgmac_fdt_node},
+ {TYPE_VFIO_AMD_XGBE, add_amd_xgbe_fdt_node},
+#endif
{"", NULL}, /* last element */
};
diff --git a/qemu/hw/arm/tosa.c b/qemu/hw/arm/tosa.c
index 73572ebe0..4e9494f94 100644
--- a/qemu/hw/arm/tosa.c
+++ b/qemu/hw/arm/tosa.c
@@ -11,6 +11,8 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/arm/pxa.h"
#include "hw/arm/arm.h"
@@ -19,7 +21,7 @@
#include "hw/pcmcia.h"
#include "hw/boards.h"
#include "hw/i2c/i2c.h"
-#include "hw/ssi.h"
+#include "hw/ssi/ssi.h"
#include "sysemu/block-backend.h"
#include "hw/sysbus.h"
#include "exec/address-spaces.h"
@@ -227,7 +229,7 @@ static void tosa_init(MachineState *machine)
mpu = pxa255_init(address_space_mem, tosa_binfo.ram_size);
- memory_region_init_ram(rom, NULL, "tosa.rom", TOSA_ROM, &error_abort);
+ memory_region_init_ram(rom, NULL, "tosa.rom", TOSA_ROM, &error_fatal);
vmstate_register_ram_global(rom);
memory_region_set_readonly(rom, true);
memory_region_add_subregion(address_space_mem, 0, rom);
@@ -252,18 +254,13 @@ static void tosa_init(MachineState *machine)
sl_bootparam_write(SL_PXA_PARAM_BASE);
}
-static QEMUMachine tosapda_machine = {
- .name = "tosa",
- .desc = "Tosa PDA (PXA255)",
- .init = tosa_init,
-};
-
-static void tosapda_machine_init(void)
+static void tosapda_machine_init(MachineClass *mc)
{
- qemu_register_machine(&tosapda_machine);
+ mc->desc = "Sharp SL-6000 (Tosa) PDA (PXA255)";
+ mc->init = tosa_init;
}
-machine_init(tosapda_machine_init);
+DEFINE_MACHINE("tosa", tosapda_machine_init)
static void tosa_dac_class_init(ObjectClass *klass, void *data)
{
diff --git a/qemu/hw/arm/versatilepb.c b/qemu/hw/arm/versatilepb.c
index 6c69f4eaa..e5a80c2d2 100644
--- a/qemu/hw/arm/versatilepb.c
+++ b/qemu/hw/arm/versatilepb.c
@@ -7,6 +7,10 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/sysbus.h"
#include "hw/arm/arm.h"
#include "hw/devices.h"
@@ -192,7 +196,6 @@ static void versatile_init(MachineState *machine, int board_id)
int n;
int done_smc = 0;
DriveInfo *dinfo;
- Error *err = NULL;
if (!machine->cpu_model) {
machine->cpu_model = "arm926";
@@ -211,18 +214,10 @@ static void versatile_init(MachineState *machine, int board_id)
* realization.
*/
if (object_property_find(cpuobj, "has_el3", NULL)) {
- object_property_set_bool(cpuobj, false, "has_el3", &err);
- if (err) {
- error_report_err(err);
- exit(1);
- }
+ object_property_set_bool(cpuobj, false, "has_el3", &error_fatal);
}
- object_property_set_bool(cpuobj, true, "realized", &err);
- if (err) {
- error_report_err(err);
- exit(1);
- }
+ object_property_set_bool(cpuobj, true, "realized", &error_fatal);
cpu = ARM_CPU(cpuobj);
@@ -391,27 +386,43 @@ static void vab_init(MachineState *machine)
versatile_init(machine, 0x25e);
}
-static QEMUMachine versatilepb_machine = {
- .name = "versatilepb",
- .desc = "ARM Versatile/PB (ARM926EJ-S)",
- .init = vpb_init,
- .block_default_type = IF_SCSI,
+static void versatilepb_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "ARM Versatile/PB (ARM926EJ-S)";
+ mc->init = vpb_init;
+ mc->block_default_type = IF_SCSI;
+}
+
+static const TypeInfo versatilepb_type = {
+ .name = MACHINE_TYPE_NAME("versatilepb"),
+ .parent = TYPE_MACHINE,
+ .class_init = versatilepb_class_init,
};
-static QEMUMachine versatileab_machine = {
- .name = "versatileab",
- .desc = "ARM Versatile/AB (ARM926EJ-S)",
- .init = vab_init,
- .block_default_type = IF_SCSI,
+static void versatileab_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "ARM Versatile/AB (ARM926EJ-S)";
+ mc->init = vab_init;
+ mc->block_default_type = IF_SCSI;
+}
+
+static const TypeInfo versatileab_type = {
+ .name = MACHINE_TYPE_NAME("versatileab"),
+ .parent = TYPE_MACHINE,
+ .class_init = versatileab_class_init,
};
static void versatile_machine_init(void)
{
- qemu_register_machine(&versatilepb_machine);
- qemu_register_machine(&versatileab_machine);
+ type_register_static(&versatilepb_type);
+ type_register_static(&versatileab_type);
}
-machine_init(versatile_machine_init);
+type_init(versatile_machine_init)
static void vpb_sic_class_init(ObjectClass *klass, void *data)
{
diff --git a/qemu/hw/arm/vexpress.c b/qemu/hw/arm/vexpress.c
index da217884e..70b3e701e 100644
--- a/qemu/hw/arm/vexpress.c
+++ b/qemu/hw/arm/vexpress.c
@@ -21,6 +21,10 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/sysbus.h"
#include "hw/arm/arm.h"
#include "hw/arm/primecell.h"
@@ -168,8 +172,8 @@ typedef struct {
} VexpressMachineState;
#define TYPE_VEXPRESS_MACHINE "vexpress"
-#define TYPE_VEXPRESS_A9_MACHINE "vexpress-a9"
-#define TYPE_VEXPRESS_A15_MACHINE "vexpress-a15"
+#define TYPE_VEXPRESS_A9_MACHINE MACHINE_TYPE_NAME("vexpress-a9")
+#define TYPE_VEXPRESS_A15_MACHINE MACHINE_TYPE_NAME("vexpress-a15")
#define VEXPRESS_MACHINE(obj) \
OBJECT_CHECK(VexpressMachineState, (obj), TYPE_VEXPRESS_MACHINE)
#define VEXPRESS_MACHINE_GET_CLASS(obj) \
@@ -211,7 +215,6 @@ static void init_cpus(const char *cpu_model, const char *privdev,
/* Create the actual CPUs */
for (n = 0; n < smp_cpus; n++) {
Object *cpuobj = object_new(object_class_get_name(cpu_oc));
- Error *err = NULL;
if (!secure) {
object_property_set_bool(cpuobj, false, "has_el3", NULL);
@@ -221,11 +224,7 @@ static void init_cpus(const char *cpu_model, const char *privdev,
object_property_set_int(cpuobj, periphbase,
"reset-cbar", &error_abort);
}
- object_property_set_bool(cpuobj, true, "realized", &err);
- if (err) {
- error_report_err(err);
- exit(1);
- }
+ object_property_set_bool(cpuobj, true, "realized", &error_fatal);
}
/* Create the private peripheral devices (including the GIC);
@@ -391,7 +390,7 @@ static void a15_daughterboard_init(const VexpressMachineState *vms,
/* 0x2b0a0000: PL341 dynamic memory controller: not modelled */
/* 0x2e000000: system SRAM */
memory_region_init_ram(sram, NULL, "vexpress.a15sram", 0x10000,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(sram);
memory_region_add_subregion(sysmem, 0x2e000000, sram);
@@ -482,8 +481,10 @@ static void vexpress_modify_dtb(const struct arm_boot_info *info, void *fdt)
uint32_t acells, scells, intc;
const VEDBoardInfo *daughterboard = (const VEDBoardInfo *)info;
- acells = qemu_fdt_getprop_cell(fdt, "/", "#address-cells");
- scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells");
+ acells = qemu_fdt_getprop_cell(fdt, "/", "#address-cells",
+ NULL, &error_fatal);
+ scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells",
+ NULL, &error_fatal);
intc = find_int_controller(fdt);
if (!intc) {
/* Not fatal, we just won't provide virtio. This will
@@ -541,7 +542,7 @@ static void vexpress_common_init(MachineState *machine)
{
VexpressMachineState *vms = VEXPRESS_MACHINE(machine);
VexpressMachineClass *vmc = VEXPRESS_MACHINE_GET_CLASS(machine);
- VEDBoardInfo *daughterboard = vmc->daughterboard;;
+ VEDBoardInfo *daughterboard = vmc->daughterboard;
DeviceState *dev, *sysctl, *pl041;
qemu_irq pic[64];
uint32_t sys_id;
@@ -671,13 +672,13 @@ static void vexpress_common_init(MachineState *machine)
sram_size = 0x2000000;
memory_region_init_ram(sram, NULL, "vexpress.sram", sram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(sram);
memory_region_add_subregion(sysmem, map[VE_SRAM], sram);
vram_size = 0x800000;
memory_region_init_ram(vram, NULL, "vexpress.vram", vram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(vram);
memory_region_add_subregion(sysmem, map[VE_VIDEORAM], vram);
@@ -747,7 +748,6 @@ static void vexpress_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
- mc->name = TYPE_VEXPRESS_MACHINE;
mc->desc = "ARM Versatile Express";
mc->init = vexpress_common_init;
mc->block_default_type = IF_SCSI;
@@ -759,10 +759,9 @@ static void vexpress_a9_class_init(ObjectClass *oc, void *data)
MachineClass *mc = MACHINE_CLASS(oc);
VexpressMachineClass *vmc = VEXPRESS_MACHINE_CLASS(oc);
- mc->name = TYPE_VEXPRESS_A9_MACHINE;
mc->desc = "ARM Versatile Express for Cortex-A9";
- vmc->daughterboard = &a9_daughterboard;;
+ vmc->daughterboard = &a9_daughterboard;
}
static void vexpress_a15_class_init(ObjectClass *oc, void *data)
@@ -770,7 +769,6 @@ static void vexpress_a15_class_init(ObjectClass *oc, void *data)
MachineClass *mc = MACHINE_CLASS(oc);
VexpressMachineClass *vmc = VEXPRESS_MACHINE_CLASS(oc);
- mc->name = TYPE_VEXPRESS_A15_MACHINE;
mc->desc = "ARM Versatile Express for Cortex-A15";
vmc->daughterboard = &a15_daughterboard;
@@ -805,4 +803,4 @@ static void vexpress_machine_init(void)
type_register_static(&vexpress_a15_info);
}
-machine_init(vexpress_machine_init);
+type_init(vexpress_machine_init);
diff --git a/qemu/hw/arm/virt-acpi-build.c b/qemu/hw/arm/virt-acpi-build.c
index f36514031..f51fe396c 100644
--- a/qemu/hw/arm/virt-acpi-build.c
+++ b/qemu/hw/arm/virt-acpi-build.c
@@ -26,6 +26,8 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu-common.h"
#include "hw/arm/virt-acpi-build.h"
#include "qemu/bitmap.h"
@@ -43,20 +45,7 @@
#include "hw/pci/pci.h"
#define ARM_SPI_BASE 32
-
-typedef struct VirtAcpiCpuInfo {
- DECLARE_BITMAP(found_cpus, VIRT_ACPI_CPU_ID_LIMIT);
-} VirtAcpiCpuInfo;
-
-static void virt_acpi_get_cpu_info(VirtAcpiCpuInfo *cpuinfo)
-{
- CPUState *cpu;
-
- memset(cpuinfo->found_cpus, 0, sizeof cpuinfo->found_cpus);
- CPU_FOREACH(cpu) {
- set_bit(cpu->cpu_index, cpuinfo->found_cpus);
- }
-}
+#define ACPI_POWER_BUTTON_DEVICE "PWRB"
static void acpi_dsdt_add_cpus(Aml *scope, int smp_cpus)
{
@@ -71,7 +60,7 @@ static void acpi_dsdt_add_cpus(Aml *scope, int smp_cpus)
}
static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
- int uart_irq)
+ uint32_t uart_irq)
{
Aml *dev = aml_device("COM0");
aml_append(dev, aml_name_decl("_HID", aml_string("ARMH0011")));
@@ -82,7 +71,7 @@ static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
uart_memmap->size, AML_READ_WRITE));
aml_append(crs,
aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
- AML_EXCLUSIVE, uart_irq));
+ AML_EXCLUSIVE, &uart_irq, 1));
aml_append(dev, aml_name_decl("_CRS", crs));
/* The _ADR entry is used to link this device to the UART described
@@ -93,19 +82,16 @@ static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
aml_append(scope, dev);
}
-static void acpi_dsdt_add_rtc(Aml *scope, const MemMapEntry *rtc_memmap,
- int rtc_irq)
+static void acpi_dsdt_add_fw_cfg(Aml *scope, const MemMapEntry *fw_cfg_memmap)
{
- Aml *dev = aml_device("RTC0");
- aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0013")));
- aml_append(dev, aml_name_decl("_UID", aml_int(0)));
+ Aml *dev = aml_device("FWCF");
+ aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0002")));
+ /* device present, functioning, decoding, not shown in UI */
+ aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
Aml *crs = aml_resource_template();
- aml_append(crs, aml_memory32_fixed(rtc_memmap->base,
- rtc_memmap->size, AML_READ_WRITE));
- aml_append(crs,
- aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
- AML_EXCLUSIVE, rtc_irq));
+ aml_append(crs, aml_memory32_fixed(fw_cfg_memmap->base,
+ fw_cfg_memmap->size, AML_READ_WRITE));
aml_append(dev, aml_name_decl("_CRS", crs));
aml_append(scope, dev);
}
@@ -114,7 +100,7 @@ static void acpi_dsdt_add_flash(Aml *scope, const MemMapEntry *flash_memmap)
{
Aml *dev, *crs;
hwaddr base = flash_memmap->base;
- hwaddr size = flash_memmap->size;
+ hwaddr size = flash_memmap->size / 2;
dev = aml_device("FLS0");
aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015")));
@@ -136,14 +122,14 @@ static void acpi_dsdt_add_flash(Aml *scope, const MemMapEntry *flash_memmap)
static void acpi_dsdt_add_virtio(Aml *scope,
const MemMapEntry *virtio_mmio_memmap,
- int mmio_irq, int num)
+ uint32_t mmio_irq, int num)
{
hwaddr base = virtio_mmio_memmap->base;
hwaddr size = virtio_mmio_memmap->size;
- int irq = mmio_irq;
int i;
for (i = 0; i < num; i++) {
+ uint32_t irq = mmio_irq + i;
Aml *dev = aml_device("VR%02u", i);
aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0005")));
aml_append(dev, aml_name_decl("_UID", aml_int(i)));
@@ -152,14 +138,15 @@ static void acpi_dsdt_add_virtio(Aml *scope,
aml_append(crs, aml_memory32_fixed(base, size, AML_READ_WRITE));
aml_append(crs,
aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
- AML_EXCLUSIVE, irq + i));
+ AML_EXCLUSIVE, &irq, 1));
aml_append(dev, aml_name_decl("_CRS", crs));
aml_append(scope, dev);
base += size;
}
}
-static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq)
+static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap,
+ uint32_t irq, bool use_highmem)
{
Aml *method, *crs, *ifctx, *UUID, *ifctx1, *elsectx, *buf;
int i, bus_no;
@@ -179,6 +166,7 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq)
aml_append(dev, aml_name_decl("_ADR", aml_int(0)));
aml_append(dev, aml_name_decl("_UID", aml_string("PCI0")));
aml_append(dev, aml_name_decl("_STR", aml_unicode("PCIe 0 Device")));
+ aml_append(dev, aml_name_decl("_CCA", aml_int(1)));
/* Declare the PCI Routing Table. */
Aml *rt_pkg = aml_package(nr_pcie_buses * PCI_NUM_PINS);
@@ -197,29 +185,30 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq)
/* Create GSI link device */
for (i = 0; i < PCI_NUM_PINS; i++) {
+ uint32_t irqs = irq + i;
Aml *dev_gsi = aml_device("GSI%d", i);
aml_append(dev_gsi, aml_name_decl("_HID", aml_string("PNP0C0F")));
aml_append(dev_gsi, aml_name_decl("_UID", aml_int(0)));
crs = aml_resource_template();
aml_append(crs,
aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
- AML_EXCLUSIVE, irq + i));
+ AML_EXCLUSIVE, &irqs, 1));
aml_append(dev_gsi, aml_name_decl("_PRS", crs));
crs = aml_resource_template();
aml_append(crs,
aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
- AML_EXCLUSIVE, irq + i));
+ AML_EXCLUSIVE, &irqs, 1));
aml_append(dev_gsi, aml_name_decl("_CRS", crs));
- method = aml_method("_SRS", 1);
+ method = aml_method("_SRS", 1, AML_NOTSERIALIZED);
aml_append(dev_gsi, method);
aml_append(dev, dev_gsi);
}
- method = aml_method("_CBA", 0);
+ method = aml_method("_CBA", 0, AML_NOTSERIALIZED);
aml_append(method, aml_return(aml_int(base_ecam)));
aml_append(dev, method);
- method = aml_method("_CRS", 0);
+ method = aml_method("_CRS", 0, AML_NOTSERIALIZED);
Aml *rbuf = aml_resource_template();
aml_append(rbuf,
aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE,
@@ -234,6 +223,17 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq)
AML_ENTIRE_RANGE, 0x0000, 0x0000, size_pio - 1, base_pio,
size_pio));
+ if (use_highmem) {
+ hwaddr base_mmio_high = memmap[VIRT_PCIE_MMIO_HIGH].base;
+ hwaddr size_mmio_high = memmap[VIRT_PCIE_MMIO_HIGH].size;
+
+ aml_append(rbuf,
+ aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
+ AML_NON_CACHEABLE, AML_READ_WRITE, 0x0000,
+ base_mmio_high, base_mmio_high, 0x0000,
+ size_mmio_high));
+ }
+
aml_append(method, aml_name_decl("RBUF", rbuf));
aml_append(method, aml_return(rbuf));
aml_append(dev, method);
@@ -241,7 +241,7 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq)
/* Declare an _OSC (OS Control Handoff) method */
aml_append(dev, aml_name_decl("SUPP", aml_int(0)));
aml_append(dev, aml_name_decl("CTRL", aml_int(0)));
- method = aml_method("_OSC", 4);
+ method = aml_method("_OSC", 4, AML_NOTSERIALIZED);
aml_append(method,
aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1"));
@@ -259,16 +259,16 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq)
aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3"));
aml_append(ifctx, aml_store(aml_name("CDW2"), aml_name("SUPP")));
aml_append(ifctx, aml_store(aml_name("CDW3"), aml_name("CTRL")));
- aml_append(ifctx, aml_store(aml_and(aml_name("CTRL"), aml_int(0x1D)),
+ aml_append(ifctx, aml_store(aml_and(aml_name("CTRL"), aml_int(0x1D), NULL),
aml_name("CTRL")));
ifctx1 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(0x1))));
- aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x08)),
+ aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x08), NULL),
aml_name("CDW1")));
aml_append(ifctx, ifctx1);
ifctx1 = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), aml_name("CTRL"))));
- aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x10)),
+ aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x10), NULL),
aml_name("CDW1")));
aml_append(ifctx, ifctx1);
@@ -277,13 +277,13 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq)
aml_append(method, ifctx);
elsectx = aml_else();
- aml_append(elsectx, aml_store(aml_or(aml_name("CDW1"), aml_int(4)),
+ aml_append(elsectx, aml_store(aml_or(aml_name("CDW1"), aml_int(4), NULL),
aml_name("CDW1")));
aml_append(elsectx, aml_return(aml_arg(3)));
aml_append(method, elsectx);
aml_append(dev, method);
- method = aml_method("_DSM", 4);
+ method = aml_method("_DSM", 4, AML_NOTSERIALIZED);
/* PCI Firmware Specification 3.0
* 4.6.1. _DSM for PCI Express Slot Information
@@ -310,6 +310,46 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq)
aml_append(scope, dev);
}
+static void acpi_dsdt_add_gpio(Aml *scope, const MemMapEntry *gpio_memmap,
+ uint32_t gpio_irq)
+{
+ Aml *dev = aml_device("GPO0");
+ aml_append(dev, aml_name_decl("_HID", aml_string("ARMH0061")));
+ aml_append(dev, aml_name_decl("_ADR", aml_int(0)));
+ aml_append(dev, aml_name_decl("_UID", aml_int(0)));
+
+ Aml *crs = aml_resource_template();
+ aml_append(crs, aml_memory32_fixed(gpio_memmap->base, gpio_memmap->size,
+ AML_READ_WRITE));
+ aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
+ AML_EXCLUSIVE, &gpio_irq, 1));
+ aml_append(dev, aml_name_decl("_CRS", crs));
+
+ Aml *aei = aml_resource_template();
+ /* Pin 3 for power button */
+ const uint32_t pin_list[1] = {3};
+ aml_append(aei, aml_gpio_int(AML_CONSUMER, AML_EDGE, AML_ACTIVE_HIGH,
+ AML_EXCLUSIVE, AML_PULL_UP, 0, pin_list, 1,
+ "GPO0", NULL, 0));
+ aml_append(dev, aml_name_decl("_AEI", aei));
+
+ /* _E03 is handle for power button */
+ Aml *method = aml_method("_E03", 0, AML_NOTSERIALIZED);
+ aml_append(method, aml_notify(aml_name(ACPI_POWER_BUTTON_DEVICE),
+ aml_int(0x80)));
+ aml_append(dev, method);
+ aml_append(scope, dev);
+}
+
+static void acpi_dsdt_add_power_button(Aml *scope)
+{
+ Aml *dev = aml_device(ACPI_POWER_BUTTON_DEVICE);
+ aml_append(dev, aml_name_decl("_HID", aml_string("PNP0C0C")));
+ aml_append(dev, aml_name_decl("_ADR", aml_int(0)));
+ aml_append(dev, aml_name_decl("_UID", aml_int(0)));
+ aml_append(scope, dev);
+}
+
/* RSDP */
static GArray *
build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt)
@@ -334,7 +374,8 @@ build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt)
rsdp->checksum = 0;
/* Checksum to be filled by Guest linker */
bios_linker_loader_add_checksum(linker, ACPI_BUILD_RSDP_FILE,
- rsdp, rsdp, sizeof *rsdp, &rsdp->checksum);
+ rsdp_table, rsdp, sizeof *rsdp,
+ &rsdp->checksum);
return rsdp_table;
}
@@ -368,7 +409,8 @@ build_spcr(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
spcr->pci_device_id = 0xffff; /* PCI Device ID: not a PCI device */
spcr->pci_vendor_id = 0xffff; /* PCI Vendor ID: not a PCI device */
- build_header(linker, table_data, (void *)spcr, "SPCR", sizeof(*spcr), 2);
+ build_header(linker, table_data, (void *)spcr, "SPCR", sizeof(*spcr), 2,
+ NULL, NULL);
}
static void
@@ -387,7 +429,7 @@ build_mcfg(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
mcfg->allocation[0].end_bus_number = (memmap[VIRT_PCIE_ECAM].size
/ PCIE_MMCFG_SIZE_MIN) - 1;
- build_header(linker, table_data, (void *)mcfg, "MCFG", len, 1);
+ build_header(linker, table_data, (void *)mcfg, "MCFG", len, 1, NULL, NULL);
}
/* GTDT */
@@ -403,7 +445,7 @@ build_gtdt(GArray *table_data, GArray *linker)
gtdt->secure_el1_flags = ACPI_EDGE_SENSITIVE;
gtdt->non_secure_el1_interrupt = ARCH_TIMER_NS_EL1_IRQ + 16;
- gtdt->non_secure_el1_flags = ACPI_EDGE_SENSITIVE;
+ gtdt->non_secure_el1_flags = ACPI_EDGE_SENSITIVE | ACPI_GTDT_ALWAYS_ON;
gtdt->virtual_timer_interrupt = ARCH_TIMER_VIRT_IRQ + 16;
gtdt->virtual_timer_flags = ACPI_EDGE_SENSITIVE;
@@ -413,13 +455,12 @@ build_gtdt(GArray *table_data, GArray *linker)
build_header(linker, table_data,
(void *)(table_data->data + gtdt_start), "GTDT",
- table_data->len - gtdt_start, 2);
+ table_data->len - gtdt_start, 2, NULL, NULL);
}
/* MADT */
static void
-build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info,
- VirtAcpiCpuInfo *cpuinfo)
+build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
{
int madt_start = table_data->len;
const MemMapEntry *memmap = guest_info->memmap;
@@ -431,37 +472,49 @@ build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info,
madt = acpi_data_push(table_data, sizeof *madt);
+ gicd = acpi_data_push(table_data, sizeof *gicd);
+ gicd->type = ACPI_APIC_GENERIC_DISTRIBUTOR;
+ gicd->length = sizeof(*gicd);
+ gicd->base_address = memmap[VIRT_GIC_DIST].base;
+
for (i = 0; i < guest_info->smp_cpus; i++) {
AcpiMadtGenericInterrupt *gicc = acpi_data_push(table_data,
sizeof *gicc);
+ ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(i));
+
gicc->type = ACPI_APIC_GENERIC_INTERRUPT;
gicc->length = sizeof(*gicc);
- gicc->base_address = memmap[VIRT_GIC_CPU].base;
+ if (guest_info->gic_version == 2) {
+ gicc->base_address = memmap[VIRT_GIC_CPU].base;
+ }
gicc->cpu_interface_number = i;
- gicc->arm_mpidr = i;
+ gicc->arm_mpidr = armcpu->mp_affinity;
gicc->uid = i;
- if (test_bit(i, cpuinfo->found_cpus)) {
- gicc->flags = cpu_to_le32(ACPI_GICC_ENABLED);
- }
+ gicc->flags = cpu_to_le32(ACPI_GICC_ENABLED);
}
- gicd = acpi_data_push(table_data, sizeof *gicd);
- gicd->type = ACPI_APIC_GENERIC_DISTRIBUTOR;
- gicd->length = sizeof(*gicd);
- gicd->base_address = memmap[VIRT_GIC_DIST].base;
-
- gic_msi = acpi_data_push(table_data, sizeof *gic_msi);
- gic_msi->type = ACPI_APIC_GENERIC_MSI_FRAME;
- gic_msi->length = sizeof(*gic_msi);
- gic_msi->gic_msi_frame_id = 0;
- gic_msi->base_address = cpu_to_le64(memmap[VIRT_GIC_V2M].base);
- gic_msi->flags = cpu_to_le32(1);
- gic_msi->spi_count = cpu_to_le16(NUM_GICV2M_SPIS);
- gic_msi->spi_base = cpu_to_le16(irqmap[VIRT_GIC_V2M] + ARM_SPI_BASE);
+ if (guest_info->gic_version == 3) {
+ AcpiMadtGenericRedistributor *gicr = acpi_data_push(table_data,
+ sizeof *gicr);
+
+ gicr->type = ACPI_APIC_GENERIC_REDISTRIBUTOR;
+ gicr->length = sizeof(*gicr);
+ gicr->base_address = cpu_to_le64(memmap[VIRT_GIC_REDIST].base);
+ gicr->range_length = cpu_to_le32(memmap[VIRT_GIC_REDIST].size);
+ } else {
+ gic_msi = acpi_data_push(table_data, sizeof *gic_msi);
+ gic_msi->type = ACPI_APIC_GENERIC_MSI_FRAME;
+ gic_msi->length = sizeof(*gic_msi);
+ gic_msi->gic_msi_frame_id = 0;
+ gic_msi->base_address = cpu_to_le64(memmap[VIRT_GIC_V2M].base);
+ gic_msi->flags = cpu_to_le32(1);
+ gic_msi->spi_count = cpu_to_le16(NUM_GICV2M_SPIS);
+ gic_msi->spi_base = cpu_to_le16(irqmap[VIRT_GIC_V2M] + ARM_SPI_BASE);
+ }
build_header(linker, table_data,
(void *)(table_data->data + madt_start), "APIC",
- table_data->len - madt_start, 3);
+ table_data->len - madt_start, 3, NULL, NULL);
}
/* FADT */
@@ -486,7 +539,7 @@ build_fadt(GArray *table_data, GArray *linker, unsigned dsdt)
sizeof fadt->dsdt);
build_header(linker, table_data,
- (void *)fadt, "FACP", sizeof(*fadt), 5);
+ (void *)fadt, "FACP", sizeof(*fadt), 5, NULL, NULL);
}
/* DSDT */
@@ -501,16 +554,24 @@ build_dsdt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
/* Reserve space for header */
acpi_data_push(dsdt->buf, sizeof(AcpiTableHeader));
+ /* When booting the VM with UEFI, UEFI takes ownership of the RTC hardware.
+ * While UEFI can use libfdt to disable the RTC device node in the DTB that
+ * it passes to the OS, it cannot modify AML. Therefore, we won't generate
+ * the RTC ACPI device at all when using UEFI.
+ */
scope = aml_scope("\\_SB");
acpi_dsdt_add_cpus(scope, guest_info->smp_cpus);
acpi_dsdt_add_uart(scope, &memmap[VIRT_UART],
(irqmap[VIRT_UART] + ARM_SPI_BASE));
- acpi_dsdt_add_rtc(scope, &memmap[VIRT_RTC],
- (irqmap[VIRT_RTC] + ARM_SPI_BASE));
acpi_dsdt_add_flash(scope, &memmap[VIRT_FLASH]);
+ acpi_dsdt_add_fw_cfg(scope, &memmap[VIRT_FW_CFG]);
acpi_dsdt_add_virtio(scope, &memmap[VIRT_MMIO],
(irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS);
- acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE));
+ acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE),
+ guest_info->use_highmem);
+ acpi_dsdt_add_gpio(scope, &memmap[VIRT_GPIO],
+ (irqmap[VIRT_GPIO] + ARM_SPI_BASE));
+ acpi_dsdt_add_power_button(scope);
aml_append(dsdt, scope);
@@ -518,7 +579,7 @@ build_dsdt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len);
build_header(linker, table_data,
(void *)(table_data->data + table_data->len - dsdt->buf->len),
- "DSDT", dsdt->buf->len, 2);
+ "DSDT", dsdt->buf->len, 2, NULL, NULL);
free_aml_allocator();
}
@@ -538,11 +599,8 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
{
GArray *table_offsets;
unsigned dsdt, rsdt;
- VirtAcpiCpuInfo cpuinfo;
GArray *tables_blob = tables->table_data;
- virt_acpi_get_cpu_info(&cpuinfo);
-
table_offsets = g_array_new(false, true /* clear */,
sizeof(uint32_t));
@@ -569,7 +627,7 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
build_fadt(tables_blob, tables->linker, dsdt);
acpi_add_table(table_offsets, tables_blob);
- build_madt(tables_blob, tables->linker, guest_info, &cpuinfo);
+ build_madt(tables_blob, tables->linker, guest_info);
acpi_add_table(table_offsets, tables_blob);
build_gtdt(tables_blob, tables->linker);
@@ -582,7 +640,7 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
/* RSDT is pointed to by RSDP */
rsdt = tables_blob->len;
- build_rsdt(tables_blob, tables->linker, table_offsets);
+ build_rsdt(tables_blob, tables->linker, table_offsets, NULL, NULL);
/* RSDP is in FSEG memory, so allocate it separately */
build_rsdp(tables->rsdp, tables->linker, rsdt);
@@ -603,7 +661,7 @@ static void acpi_ram_update(MemoryRegion *mr, GArray *data)
memory_region_set_dirty(mr, 0, size);
}
-static void virt_acpi_build_update(void *build_opaque, uint32_t offset)
+static void virt_acpi_build_update(void *build_opaque)
{
AcpiBuildState *build_state = build_opaque;
AcpiBuildTables tables;
diff --git a/qemu/hw/arm/virt.c b/qemu/hw/arm/virt.c
index 484689264..56d35c771 100644
--- a/qemu/hw/arm/virt.c
+++ b/qemu/hw/arm/virt.c
@@ -28,6 +28,8 @@
* This is essentially the same approach kvmtool uses.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/sysbus.h"
#include "hw/arm/arm.h"
#include "hw/arm/primecell.h"
@@ -48,6 +50,11 @@
#include "hw/arm/sysbus-fdt.h"
#include "hw/platform-bus.h"
#include "hw/arm/fdt.h"
+#include "hw/intc/arm_gic_common.h"
+#include "kvm_arm.h"
+#include "hw/smbios/smbios.h"
+#include "qapi/visitor.h"
+#include "standard-headers/linux/input.h"
/* Number of external interrupt lines to configure the GIC with */
#define NUM_IRQS 256
@@ -67,6 +74,7 @@ typedef struct VirtBoardInfo {
uint32_t clock_phandle;
uint32_t gic_phandle;
uint32_t v2m_phandle;
+ bool using_psci;
} VirtBoardInfo;
typedef struct {
@@ -77,9 +85,11 @@ typedef struct {
typedef struct {
MachineState parent;
bool secure;
+ bool highmem;
+ int32_t gic_version;
} VirtMachineState;
-#define TYPE_VIRT_MACHINE "virt"
+#define TYPE_VIRT_MACHINE MACHINE_TYPE_NAME("virt")
#define VIRT_MACHINE(obj) \
OBJECT_CHECK(VirtMachineState, (obj), TYPE_VIRT_MACHINE)
#define VIRT_MACHINE_GET_CLASS(obj) \
@@ -87,6 +97,23 @@ typedef struct {
#define VIRT_MACHINE_CLASS(klass) \
OBJECT_CLASS_CHECK(VirtMachineClass, klass, TYPE_VIRT_MACHINE)
+/* RAM limit in GB. Since VIRT_MEM starts at the 1GB mark, this means
+ * RAM can go up to the 256GB mark, leaving 256GB of the physical
+ * address space unallocated and free for future use between 256G and 512G.
+ * If we need to provide more RAM to VMs in the future then we need to:
+ * * allocate a second bank of RAM starting at 2TB and working up
+ * * fix the DT and ACPI table generation code in QEMU to correctly
+ * report two split lumps of RAM to the guest
+ * * fix KVM in the host kernel to allow guests with >40 bit address spaces
+ * (We don't want to fill all the way up to 512GB with RAM because
+ * we might want it for non-RAM purposes later. Conversely it seems
+ * reasonable to assume that anybody configuring a VM with a quarter
+ * of a terabyte of RAM will be doing it on a host with more than a
+ * terabyte of physical address space.)
+ */
+#define RAMLIMIT_GB 255
+#define RAMLIMIT_BYTES (RAMLIMIT_GB * 1024ULL * 1024 * 1024)
+
/* Addresses and sizes of our components.
* 0..128MB is space for a flash device so we can run bootrom code such as UEFI.
* 128MB..256MB is used for miscellaneous device I/O.
@@ -107,22 +134,33 @@ static const MemMapEntry a15memmap[] = {
[VIRT_GIC_DIST] = { 0x08000000, 0x00010000 },
[VIRT_GIC_CPU] = { 0x08010000, 0x00010000 },
[VIRT_GIC_V2M] = { 0x08020000, 0x00001000 },
+ /* The space in between here is reserved for GICv3 CPU/vCPU/HYP */
+ [VIRT_GIC_ITS] = { 0x08080000, 0x00020000 },
+ /* This redistributor space allows up to 2*64kB*123 CPUs */
+ [VIRT_GIC_REDIST] = { 0x080A0000, 0x00F60000 },
[VIRT_UART] = { 0x09000000, 0x00001000 },
[VIRT_RTC] = { 0x09010000, 0x00001000 },
- [VIRT_FW_CFG] = { 0x09020000, 0x0000000a },
+ [VIRT_FW_CFG] = { 0x09020000, 0x00000018 },
+ [VIRT_GPIO] = { 0x09030000, 0x00001000 },
+ [VIRT_SECURE_UART] = { 0x09040000, 0x00001000 },
[VIRT_MMIO] = { 0x0a000000, 0x00000200 },
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
[VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 },
+ [VIRT_SECURE_MEM] = { 0x0e000000, 0x01000000 },
[VIRT_PCIE_MMIO] = { 0x10000000, 0x2eff0000 },
[VIRT_PCIE_PIO] = { 0x3eff0000, 0x00010000 },
[VIRT_PCIE_ECAM] = { 0x3f000000, 0x01000000 },
- [VIRT_MEM] = { 0x40000000, 30ULL * 1024 * 1024 * 1024 },
+ [VIRT_MEM] = { 0x40000000, RAMLIMIT_BYTES },
+ /* Second PCIe window, 512GB wide at the 512GB boundary */
+ [VIRT_PCIE_MMIO_HIGH] = { 0x8000000000ULL, 0x8000000000ULL },
};
static const int a15irqmap[] = {
[VIRT_UART] = 1,
[VIRT_RTC] = 2,
[VIRT_PCIE] = 3, /* ... to 6 */
+ [VIRT_GPIO] = 7,
+ [VIRT_SECURE_UART] = 8,
[VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
[VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */
[VIRT_PLATFORM_BUS] = 112, /* ...to 112 + PLATFORM_BUS_NUM_IRQS -1 */
@@ -212,6 +250,10 @@ static void fdt_add_psci_node(const VirtBoardInfo *vbi)
void *fdt = vbi->fdt;
ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(0));
+ if (!vbi->using_psci) {
+ return;
+ }
+
qemu_fdt_add_subnode(fdt, "/psci");
if (armcpu->psci_version == 2) {
const char comp[] = "arm,psci-0.2\0arm,psci";
@@ -249,7 +291,7 @@ static void fdt_add_psci_node(const VirtBoardInfo *vbi)
qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn);
}
-static void fdt_add_timer_nodes(const VirtBoardInfo *vbi)
+static void fdt_add_timer_nodes(const VirtBoardInfo *vbi, int gictype)
{
/* Note that on A15 h/w these interrupts are level-triggered,
* but for the GIC implementation provided by both QEMU and KVM
@@ -258,8 +300,11 @@ static void fdt_add_timer_nodes(const VirtBoardInfo *vbi)
ARMCPU *armcpu;
uint32_t irqflags = GIC_FDT_IRQ_FLAGS_EDGE_LO_HI;
- irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START,
- GIC_FDT_IRQ_PPI_CPU_WIDTH, (1 << vbi->smp_cpus) - 1);
+ if (gictype == 2) {
+ irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START,
+ GIC_FDT_IRQ_PPI_CPU_WIDTH,
+ (1 << vbi->smp_cpus) - 1);
+ }
qemu_fdt_add_subnode(vbi->fdt, "/timer");
@@ -272,6 +317,7 @@ static void fdt_add_timer_nodes(const VirtBoardInfo *vbi)
qemu_fdt_setprop_string(vbi->fdt, "/timer", "compatible",
"arm,armv7-timer");
}
+ qemu_fdt_setprop(vbi->fdt, "/timer", "always-on", NULL, 0);
qemu_fdt_setprop_cells(vbi->fdt, "/timer", "interrupts",
GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_S_EL1_IRQ, irqflags,
GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_NS_EL1_IRQ, irqflags,
@@ -282,9 +328,32 @@ static void fdt_add_timer_nodes(const VirtBoardInfo *vbi)
static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi)
{
int cpu;
+ int addr_cells = 1;
+
+ /*
+ * From Documentation/devicetree/bindings/arm/cpus.txt
+ * On ARM v8 64-bit systems value should be set to 2,
+ * that corresponds to the MPIDR_EL1 register size.
+ * If MPIDR_EL1[63:32] value is equal to 0 on all CPUs
+ * in the system, #address-cells can be set to 1, since
+ * MPIDR_EL1[63:32] bits are not used for CPUs
+ * identification.
+ *
+ * Here we actually don't know whether our system is 32- or 64-bit one.
+ * The simplest way to go is to examine affinity IDs of all our CPUs. If
+ * at least one of them has Aff3 populated, we set #address-cells to 2.
+ */
+ for (cpu = 0; cpu < vbi->smp_cpus; cpu++) {
+ ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(cpu));
+
+ if (armcpu->mp_affinity & ARM_AFF3_MASK) {
+ addr_cells = 2;
+ break;
+ }
+ }
qemu_fdt_add_subnode(vbi->fdt, "/cpus");
- qemu_fdt_setprop_cell(vbi->fdt, "/cpus", "#address-cells", 0x1);
+ qemu_fdt_setprop_cell(vbi->fdt, "/cpus", "#address-cells", addr_cells);
qemu_fdt_setprop_cell(vbi->fdt, "/cpus", "#size-cells", 0x0);
for (cpu = vbi->smp_cpus - 1; cpu >= 0; cpu--) {
@@ -296,12 +365,19 @@ static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi)
qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible",
armcpu->dtb_compatible);
- if (vbi->smp_cpus > 1) {
+ if (vbi->using_psci && vbi->smp_cpus > 1) {
qemu_fdt_setprop_string(vbi->fdt, nodename,
"enable-method", "psci");
}
- qemu_fdt_setprop_cell(vbi->fdt, nodename, "reg", armcpu->mp_affinity);
+ if (addr_cells == 2) {
+ qemu_fdt_setprop_u64(vbi->fdt, nodename, "reg",
+ armcpu->mp_affinity);
+ } else {
+ qemu_fdt_setprop_cell(vbi->fdt, nodename, "reg",
+ armcpu->mp_affinity);
+ }
+
g_free(nodename);
}
}
@@ -319,25 +395,36 @@ static void fdt_add_v2m_gic_node(VirtBoardInfo *vbi)
qemu_fdt_setprop_cell(vbi->fdt, "/intc/v2m", "phandle", vbi->v2m_phandle);
}
-static void fdt_add_gic_node(VirtBoardInfo *vbi)
+static void fdt_add_gic_node(VirtBoardInfo *vbi, int type)
{
vbi->gic_phandle = qemu_fdt_alloc_phandle(vbi->fdt);
qemu_fdt_setprop_cell(vbi->fdt, "/", "interrupt-parent", vbi->gic_phandle);
qemu_fdt_add_subnode(vbi->fdt, "/intc");
- /* 'cortex-a15-gic' means 'GIC v2' */
- qemu_fdt_setprop_string(vbi->fdt, "/intc", "compatible",
- "arm,cortex-a15-gic");
qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#interrupt-cells", 3);
qemu_fdt_setprop(vbi->fdt, "/intc", "interrupt-controller", NULL, 0);
- qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc", "reg",
- 2, vbi->memmap[VIRT_GIC_DIST].base,
- 2, vbi->memmap[VIRT_GIC_DIST].size,
- 2, vbi->memmap[VIRT_GIC_CPU].base,
- 2, vbi->memmap[VIRT_GIC_CPU].size);
qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#address-cells", 0x2);
qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#size-cells", 0x2);
qemu_fdt_setprop(vbi->fdt, "/intc", "ranges", NULL, 0);
+ if (type == 3) {
+ qemu_fdt_setprop_string(vbi->fdt, "/intc", "compatible",
+ "arm,gic-v3");
+ qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc", "reg",
+ 2, vbi->memmap[VIRT_GIC_DIST].base,
+ 2, vbi->memmap[VIRT_GIC_DIST].size,
+ 2, vbi->memmap[VIRT_GIC_REDIST].base,
+ 2, vbi->memmap[VIRT_GIC_REDIST].size);
+ } else {
+ /* 'cortex-a15-gic' means 'GIC v2' */
+ qemu_fdt_setprop_string(vbi->fdt, "/intc", "compatible",
+ "arm,cortex-a15-gic");
+ qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc", "reg",
+ 2, vbi->memmap[VIRT_GIC_DIST].base,
+ 2, vbi->memmap[VIRT_GIC_DIST].size,
+ 2, vbi->memmap[VIRT_GIC_CPU].base,
+ 2, vbi->memmap[VIRT_GIC_CPU].size);
+ }
+
qemu_fdt_setprop_cell(vbi->fdt, "/intc", "phandle", vbi->gic_phandle);
}
@@ -360,29 +447,34 @@ static void create_v2m(VirtBoardInfo *vbi, qemu_irq *pic)
fdt_add_v2m_gic_node(vbi);
}
-static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic)
+static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic, int type, bool secure)
{
- /* We create a standalone GIC v2 */
+ /* We create a standalone GIC */
DeviceState *gicdev;
SysBusDevice *gicbusdev;
- const char *gictype = "arm_gic";
+ const char *gictype;
int i;
- if (kvm_irqchip_in_kernel()) {
- gictype = "kvm-arm-gic";
- }
+ gictype = (type == 3) ? gicv3_class_name() : gic_class_name();
gicdev = qdev_create(NULL, gictype);
- qdev_prop_set_uint32(gicdev, "revision", 2);
+ qdev_prop_set_uint32(gicdev, "revision", type);
qdev_prop_set_uint32(gicdev, "num-cpu", smp_cpus);
/* Note that the num-irq property counts both internal and external
* interrupts; there are always 32 of the former (mandated by GIC spec).
*/
qdev_prop_set_uint32(gicdev, "num-irq", NUM_IRQS + 32);
+ if (!kvm_irqchip_in_kernel()) {
+ qdev_prop_set_bit(gicdev, "has-security-extensions", secure);
+ }
qdev_init_nofail(gicdev);
gicbusdev = SYS_BUS_DEVICE(gicdev);
sysbus_mmio_map(gicbusdev, 0, vbi->memmap[VIRT_GIC_DIST].base);
- sysbus_mmio_map(gicbusdev, 1, vbi->memmap[VIRT_GIC_CPU].base);
+ if (type == 3) {
+ sysbus_mmio_map(gicbusdev, 1, vbi->memmap[VIRT_GIC_REDIST].base);
+ } else {
+ sysbus_mmio_map(gicbusdev, 1, vbi->memmap[VIRT_GIC_CPU].base);
+ }
/* Wire the outputs from each CPU's generic timer to the
* appropriate GIC PPI inputs, and the GIC's IRQ output to
@@ -390,15 +482,23 @@ static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic)
*/
for (i = 0; i < smp_cpus; i++) {
DeviceState *cpudev = DEVICE(qemu_get_cpu(i));
- int ppibase = NUM_IRQS + i * 32;
- /* physical timer; we wire it up to the non-secure timer's ID,
- * since a real A15 always has TrustZone but QEMU doesn't.
+ int ppibase = NUM_IRQS + i * GIC_INTERNAL + GIC_NR_SGIS;
+ int irq;
+ /* Mapping from the output timer irq lines from the CPU to the
+ * GIC PPI inputs we use for the virt board.
*/
- qdev_connect_gpio_out(cpudev, 0,
- qdev_get_gpio_in(gicdev, ppibase + 30));
- /* virtual timer */
- qdev_connect_gpio_out(cpudev, 1,
- qdev_get_gpio_in(gicdev, ppibase + 27));
+ const int timer_irq[] = {
+ [GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ,
+ [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ,
+ [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ,
+ [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ,
+ };
+
+ for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) {
+ qdev_connect_gpio_out(cpudev, irq,
+ qdev_get_gpio_in(gicdev,
+ ppibase + timer_irq[irq]));
+ }
sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
sysbus_connect_irq(gicbusdev, i + smp_cpus,
@@ -409,21 +509,29 @@ static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic)
pic[i] = qdev_get_gpio_in(gicdev, i);
}
- fdt_add_gic_node(vbi);
+ fdt_add_gic_node(vbi, type);
- create_v2m(vbi, pic);
+ if (type == 2) {
+ create_v2m(vbi, pic);
+ }
}
-static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic)
+static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic, int uart,
+ MemoryRegion *mem)
{
char *nodename;
- hwaddr base = vbi->memmap[VIRT_UART].base;
- hwaddr size = vbi->memmap[VIRT_UART].size;
- int irq = vbi->irqmap[VIRT_UART];
+ hwaddr base = vbi->memmap[uart].base;
+ hwaddr size = vbi->memmap[uart].size;
+ int irq = vbi->irqmap[uart];
const char compat[] = "arm,pl011\0arm,primecell";
const char clocknames[] = "uartclk\0apb_pclk";
+ DeviceState *dev = qdev_create(NULL, "pl011");
+ SysBusDevice *s = SYS_BUS_DEVICE(dev);
- sysbus_create_simple("pl011", base, pic[irq]);
+ qdev_init_nofail(dev);
+ memory_region_add_subregion(mem, base,
+ sysbus_mmio_get_region(s, 0));
+ sysbus_connect_irq(s, 0, pic[irq]);
nodename = g_strdup_printf("/pl011@%" PRIx64, base);
qemu_fdt_add_subnode(vbi->fdt, nodename);
@@ -440,7 +548,14 @@ static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic)
qemu_fdt_setprop(vbi->fdt, nodename, "clock-names",
clocknames, sizeof(clocknames));
- qemu_fdt_setprop_string(vbi->fdt, "/chosen", "stdout-path", nodename);
+ if (uart == VIRT_UART) {
+ qemu_fdt_setprop_string(vbi->fdt, "/chosen", "stdout-path", nodename);
+ } else {
+ /* Mark as not usable by the normal world */
+ qemu_fdt_setprop_string(vbi->fdt, nodename, "status", "disabled");
+ qemu_fdt_setprop_string(vbi->fdt, nodename, "secure-status", "okay");
+ }
+
g_free(nodename);
}
@@ -467,6 +582,64 @@ static void create_rtc(const VirtBoardInfo *vbi, qemu_irq *pic)
g_free(nodename);
}
+static DeviceState *gpio_key_dev;
+static void virt_powerdown_req(Notifier *n, void *opaque)
+{
+ /* use gpio Pin 3 for power button event */
+ qemu_set_irq(qdev_get_gpio_in(gpio_key_dev, 0), 1);
+}
+
+static Notifier virt_system_powerdown_notifier = {
+ .notify = virt_powerdown_req
+};
+
+static void create_gpio(const VirtBoardInfo *vbi, qemu_irq *pic)
+{
+ char *nodename;
+ DeviceState *pl061_dev;
+ hwaddr base = vbi->memmap[VIRT_GPIO].base;
+ hwaddr size = vbi->memmap[VIRT_GPIO].size;
+ int irq = vbi->irqmap[VIRT_GPIO];
+ const char compat[] = "arm,pl061\0arm,primecell";
+
+ pl061_dev = sysbus_create_simple("pl061", base, pic[irq]);
+
+ uint32_t phandle = qemu_fdt_alloc_phandle(vbi->fdt);
+ nodename = g_strdup_printf("/pl061@%" PRIx64, base);
+ qemu_fdt_add_subnode(vbi->fdt, nodename);
+ qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg",
+ 2, base, 2, size);
+ qemu_fdt_setprop(vbi->fdt, nodename, "compatible", compat, sizeof(compat));
+ qemu_fdt_setprop_cell(vbi->fdt, nodename, "#gpio-cells", 2);
+ qemu_fdt_setprop(vbi->fdt, nodename, "gpio-controller", NULL, 0);
+ qemu_fdt_setprop_cells(vbi->fdt, nodename, "interrupts",
+ GIC_FDT_IRQ_TYPE_SPI, irq,
+ GIC_FDT_IRQ_FLAGS_LEVEL_HI);
+ qemu_fdt_setprop_cell(vbi->fdt, nodename, "clocks", vbi->clock_phandle);
+ qemu_fdt_setprop_string(vbi->fdt, nodename, "clock-names", "apb_pclk");
+ qemu_fdt_setprop_cell(vbi->fdt, nodename, "phandle", phandle);
+
+ gpio_key_dev = sysbus_create_simple("gpio-key", -1,
+ qdev_get_gpio_in(pl061_dev, 3));
+ qemu_fdt_add_subnode(vbi->fdt, "/gpio-keys");
+ qemu_fdt_setprop_string(vbi->fdt, "/gpio-keys", "compatible", "gpio-keys");
+ qemu_fdt_setprop_cell(vbi->fdt, "/gpio-keys", "#size-cells", 0);
+ qemu_fdt_setprop_cell(vbi->fdt, "/gpio-keys", "#address-cells", 1);
+
+ qemu_fdt_add_subnode(vbi->fdt, "/gpio-keys/poweroff");
+ qemu_fdt_setprop_string(vbi->fdt, "/gpio-keys/poweroff",
+ "label", "GPIO Key Poweroff");
+ qemu_fdt_setprop_cell(vbi->fdt, "/gpio-keys/poweroff", "linux,code",
+ KEY_POWER);
+ qemu_fdt_setprop_cells(vbi->fdt, "/gpio-keys/poweroff",
+ "gpios", phandle, 3, 0);
+
+ /* connect powerdown request */
+ qemu_register_powerdown_notifier(&virt_system_powerdown_notifier);
+
+ g_free(nodename);
+}
+
static void create_virtio_devices(const VirtBoardInfo *vbi, qemu_irq *pic)
{
int i;
@@ -532,13 +705,15 @@ static void create_virtio_devices(const VirtBoardInfo *vbi, qemu_irq *pic)
}
static void create_one_flash(const char *name, hwaddr flashbase,
- hwaddr flashsize)
+ hwaddr flashsize, const char *file,
+ MemoryRegion *sysmem)
{
/* Create and map a single flash device. We use the same
* parameters as the flash devices on the Versatile Express board.
*/
DriveInfo *dinfo = drive_get_next(IF_PFLASH);
DeviceState *dev = qdev_create(NULL, "cfi.pflash01");
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
const uint64_t sectorlength = 256 * 1024;
if (dinfo) {
@@ -558,19 +733,10 @@ static void create_one_flash(const char *name, hwaddr flashbase,
qdev_prop_set_string(dev, "name", name);
qdev_init_nofail(dev);
- sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, flashbase);
-}
-
-static void create_flash(const VirtBoardInfo *vbi)
-{
- /* Create two flash devices to fill the VIRT_FLASH space in the memmap.
- * Any file passed via -bios goes in the first of these.
- */
- hwaddr flashsize = vbi->memmap[VIRT_FLASH].size / 2;
- hwaddr flashbase = vbi->memmap[VIRT_FLASH].base;
- char *nodename;
+ memory_region_add_subregion(sysmem, flashbase,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0));
- if (bios_name) {
+ if (file) {
char *fn;
int image_size;
@@ -580,39 +746,82 @@ static void create_flash(const VirtBoardInfo *vbi)
"but you cannot use both options at once");
exit(1);
}
- fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, file);
if (!fn) {
- error_report("Could not find ROM image '%s'", bios_name);
+ error_report("Could not find ROM image '%s'", file);
exit(1);
}
- image_size = load_image_targphys(fn, flashbase, flashsize);
+ image_size = load_image_mr(fn, sysbus_mmio_get_region(sbd, 0));
g_free(fn);
if (image_size < 0) {
- error_report("Could not load ROM image '%s'", bios_name);
+ error_report("Could not load ROM image '%s'", file);
exit(1);
}
}
+}
+
+static void create_flash(const VirtBoardInfo *vbi,
+ MemoryRegion *sysmem,
+ MemoryRegion *secure_sysmem)
+{
+ /* Create two flash devices to fill the VIRT_FLASH space in the memmap.
+ * Any file passed via -bios goes in the first of these.
+ * sysmem is the system memory space. secure_sysmem is the secure view
+ * of the system, and the first flash device should be made visible only
+ * there. The second flash device is visible to both secure and nonsecure.
+ * If sysmem == secure_sysmem this means there is no separate Secure
+ * address space and both flash devices are generally visible.
+ */
+ hwaddr flashsize = vbi->memmap[VIRT_FLASH].size / 2;
+ hwaddr flashbase = vbi->memmap[VIRT_FLASH].base;
+ char *nodename;
- create_one_flash("virt.flash0", flashbase, flashsize);
- create_one_flash("virt.flash1", flashbase + flashsize, flashsize);
+ create_one_flash("virt.flash0", flashbase, flashsize,
+ bios_name, secure_sysmem);
+ create_one_flash("virt.flash1", flashbase + flashsize, flashsize,
+ NULL, sysmem);
- nodename = g_strdup_printf("/flash@%" PRIx64, flashbase);
- qemu_fdt_add_subnode(vbi->fdt, nodename);
- qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible", "cfi-flash");
- qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg",
- 2, flashbase, 2, flashsize,
- 2, flashbase + flashsize, 2, flashsize);
- qemu_fdt_setprop_cell(vbi->fdt, nodename, "bank-width", 4);
- g_free(nodename);
+ if (sysmem == secure_sysmem) {
+ /* Report both flash devices as a single node in the DT */
+ nodename = g_strdup_printf("/flash@%" PRIx64, flashbase);
+ qemu_fdt_add_subnode(vbi->fdt, nodename);
+ qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible", "cfi-flash");
+ qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg",
+ 2, flashbase, 2, flashsize,
+ 2, flashbase + flashsize, 2, flashsize);
+ qemu_fdt_setprop_cell(vbi->fdt, nodename, "bank-width", 4);
+ g_free(nodename);
+ } else {
+ /* Report the devices as separate nodes so we can mark one as
+ * only visible to the secure world.
+ */
+ nodename = g_strdup_printf("/secflash@%" PRIx64, flashbase);
+ qemu_fdt_add_subnode(vbi->fdt, nodename);
+ qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible", "cfi-flash");
+ qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg",
+ 2, flashbase, 2, flashsize);
+ qemu_fdt_setprop_cell(vbi->fdt, nodename, "bank-width", 4);
+ qemu_fdt_setprop_string(vbi->fdt, nodename, "status", "disabled");
+ qemu_fdt_setprop_string(vbi->fdt, nodename, "secure-status", "okay");
+ g_free(nodename);
+
+ nodename = g_strdup_printf("/flash@%" PRIx64, flashbase);
+ qemu_fdt_add_subnode(vbi->fdt, nodename);
+ qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible", "cfi-flash");
+ qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg",
+ 2, flashbase + flashsize, 2, flashsize);
+ qemu_fdt_setprop_cell(vbi->fdt, nodename, "bank-width", 4);
+ g_free(nodename);
+ }
}
-static void create_fw_cfg(const VirtBoardInfo *vbi)
+static void create_fw_cfg(const VirtBoardInfo *vbi, AddressSpace *as)
{
hwaddr base = vbi->memmap[VIRT_FW_CFG].base;
hwaddr size = vbi->memmap[VIRT_FW_CFG].size;
char *nodename;
- fw_cfg_init_mem_wide(base + 8, base, 8);
+ fw_cfg_init_mem_wide(base + 8, base, 8, base + 16, as);
nodename = g_strdup_printf("/fw-cfg@%" PRIx64, base);
qemu_fdt_add_subnode(vbi->fdt, nodename);
@@ -658,10 +867,13 @@ static void create_pcie_irq_map(const VirtBoardInfo *vbi, uint32_t gic_phandle,
0x7 /* PCI irq */);
}
-static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic)
+static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic,
+ bool use_highmem)
{
hwaddr base_mmio = vbi->memmap[VIRT_PCIE_MMIO].base;
hwaddr size_mmio = vbi->memmap[VIRT_PCIE_MMIO].size;
+ hwaddr base_mmio_high = vbi->memmap[VIRT_PCIE_MMIO_HIGH].base;
+ hwaddr size_mmio_high = vbi->memmap[VIRT_PCIE_MMIO_HIGH].size;
hwaddr base_pio = vbi->memmap[VIRT_PCIE_PIO].base;
hwaddr size_pio = vbi->memmap[VIRT_PCIE_PIO].size;
hwaddr base_ecam = vbi->memmap[VIRT_PCIE_ECAM].base;
@@ -676,6 +888,7 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic)
DeviceState *dev;
char *nodename;
int i;
+ PCIHostState *pci;
dev = qdev_create(NULL, TYPE_GPEX_HOST);
qdev_init_nofail(dev);
@@ -698,6 +911,16 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic)
mmio_reg, base_mmio, size_mmio);
memory_region_add_subregion(get_system_memory(), base_mmio, mmio_alias);
+ if (use_highmem) {
+ /* Map high MMIO space */
+ MemoryRegion *high_mmio_alias = g_new0(MemoryRegion, 1);
+
+ memory_region_init_alias(high_mmio_alias, OBJECT(dev), "pcie-mmio-high",
+ mmio_reg, base_mmio_high, size_mmio_high);
+ memory_region_add_subregion(get_system_memory(), base_mmio_high,
+ high_mmio_alias);
+ }
+
/* Map IO port space */
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, base_pio);
@@ -705,6 +928,19 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic)
sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pic[irq + i]);
}
+ pci = PCI_HOST_BRIDGE(dev);
+ if (pci->bus) {
+ for (i = 0; i < nb_nics; i++) {
+ NICInfo *nd = &nd_table[i];
+
+ if (!nd->model) {
+ nd->model = g_strdup("virtio");
+ }
+
+ pci_nic_init_nofail(nd, pci->bus, nd->model, NULL);
+ }
+ }
+
nodename = g_strdup_printf("/pcie@%" PRIx64, base);
qemu_fdt_add_subnode(vbi->fdt, nodename);
qemu_fdt_setprop_string(vbi->fdt, nodename,
@@ -715,15 +951,30 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic)
qemu_fdt_setprop_cells(vbi->fdt, nodename, "bus-range", 0,
nr_pcie_buses - 1);
- qemu_fdt_setprop_cells(vbi->fdt, nodename, "msi-parent", vbi->v2m_phandle);
+ if (vbi->v2m_phandle) {
+ qemu_fdt_setprop_cells(vbi->fdt, nodename, "msi-parent",
+ vbi->v2m_phandle);
+ }
qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg",
2, base_ecam, 2, size_ecam);
- qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "ranges",
- 1, FDT_PCI_RANGE_IOPORT, 2, 0,
- 2, base_pio, 2, size_pio,
- 1, FDT_PCI_RANGE_MMIO, 2, base_mmio,
- 2, base_mmio, 2, size_mmio);
+
+ if (use_highmem) {
+ qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "ranges",
+ 1, FDT_PCI_RANGE_IOPORT, 2, 0,
+ 2, base_pio, 2, size_pio,
+ 1, FDT_PCI_RANGE_MMIO, 2, base_mmio,
+ 2, base_mmio, 2, size_mmio,
+ 1, FDT_PCI_RANGE_MMIO_64BIT,
+ 2, base_mmio_high,
+ 2, base_mmio_high, 2, size_mmio_high);
+ } else {
+ qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "ranges",
+ 1, FDT_PCI_RANGE_IOPORT, 2, 0,
+ 2, base_pio, 2, size_pio,
+ 1, FDT_PCI_RANGE_MMIO, 2, base_mmio,
+ 2, base_mmio, 2, size_mmio);
+ }
qemu_fdt_setprop_cell(vbi->fdt, nodename, "#interrupt-cells", 1);
create_pcie_irq_map(vbi, vbi->gic_phandle, irq, nodename);
@@ -772,6 +1023,27 @@ static void create_platform_bus(VirtBoardInfo *vbi, qemu_irq *pic)
sysbus_mmio_get_region(s, 0));
}
+static void create_secure_ram(VirtBoardInfo *vbi, MemoryRegion *secure_sysmem)
+{
+ MemoryRegion *secram = g_new(MemoryRegion, 1);
+ char *nodename;
+ hwaddr base = vbi->memmap[VIRT_SECURE_MEM].base;
+ hwaddr size = vbi->memmap[VIRT_SECURE_MEM].size;
+
+ memory_region_init_ram(secram, NULL, "virt.secure-ram", size, &error_fatal);
+ vmstate_register_ram_global(secram);
+ memory_region_add_subregion(secure_sysmem, base, secram);
+
+ nodename = g_strdup_printf("/secram@%" PRIx64, base);
+ qemu_fdt_add_subnode(vbi->fdt, nodename);
+ qemu_fdt_setprop_string(vbi->fdt, nodename, "device_type", "memory");
+ qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", 2, base, 2, size);
+ qemu_fdt_setprop_string(vbi->fdt, nodename, "status", "disabled");
+ qemu_fdt_setprop_string(vbi->fdt, nodename, "secure-status", "okay");
+
+ g_free(nodename);
+}
+
static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size)
{
const VirtBoardInfo *board = (const VirtBoardInfo *)binfo;
@@ -780,12 +1052,42 @@ static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size)
return board->fdt;
}
+static void virt_build_smbios(VirtGuestInfo *guest_info)
+{
+ FWCfgState *fw_cfg = guest_info->fw_cfg;
+ uint8_t *smbios_tables, *smbios_anchor;
+ size_t smbios_tables_len, smbios_anchor_len;
+ const char *product = "QEMU Virtual Machine";
+
+ if (!fw_cfg) {
+ return;
+ }
+
+ if (kvm_enabled()) {
+ product = "KVM Virtual Machine";
+ }
+
+ smbios_set_defaults("QEMU", product,
+ "1.0", false, true, SMBIOS_ENTRY_POINT_30);
+
+ smbios_get_tables(NULL, 0, &smbios_tables, &smbios_tables_len,
+ &smbios_anchor, &smbios_anchor_len);
+
+ if (smbios_anchor) {
+ fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-tables",
+ smbios_tables, smbios_tables_len);
+ fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-anchor",
+ smbios_anchor, smbios_anchor_len);
+ }
+}
+
static
void virt_guest_info_machine_done(Notifier *notifier, void *data)
{
VirtGuestInfoState *guest_info_state = container_of(notifier,
VirtGuestInfoState, machine_done);
virt_acpi_setup(&guest_info_state->info);
+ virt_build_smbios(&guest_info_state->info);
}
static void machvirt_init(MachineState *machine)
@@ -793,18 +1095,33 @@ static void machvirt_init(MachineState *machine)
VirtMachineState *vms = VIRT_MACHINE(machine);
qemu_irq pic[NUM_IRQS];
MemoryRegion *sysmem = get_system_memory();
- int n;
+ MemoryRegion *secure_sysmem = NULL;
+ int gic_version = vms->gic_version;
+ int n, virt_max_cpus;
MemoryRegion *ram = g_new(MemoryRegion, 1);
const char *cpu_model = machine->cpu_model;
VirtBoardInfo *vbi;
VirtGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state);
VirtGuestInfo *guest_info = &guest_info_state->info;
char **cpustr;
+ bool firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0);
if (!cpu_model) {
cpu_model = "cortex-a15";
}
+ /* We can probe only here because during property set
+ * KVM is not available yet
+ */
+ if (!gic_version) {
+ gic_version = kvm_arm_vgic_probe();
+ if (!gic_version) {
+ error_report("Unable to determine GIC version supported by host");
+ error_printf("KVM acceleration is probably not supported\n");
+ exit(1);
+ }
+ }
+
/* Separate the actual CPU model name from any appended features */
cpustr = g_strsplit(cpu_model, ",", 2);
@@ -815,13 +1132,55 @@ static void machvirt_init(MachineState *machine)
exit(1);
}
+ /* If we have an EL3 boot ROM then the assumption is that it will
+ * implement PSCI itself, so disable QEMU's internal implementation
+ * so it doesn't get in the way. Instead of starting secondary
+ * CPUs in PSCI powerdown state we will start them all running and
+ * let the boot ROM sort them out.
+ * The usual case is that we do use QEMU's PSCI implementation.
+ */
+ vbi->using_psci = !(vms->secure && firmware_loaded);
+
+ /* The maximum number of CPUs depends on the GIC version, or on how
+ * many redistributors we can fit into the memory map.
+ */
+ if (gic_version == 3) {
+ virt_max_cpus = vbi->memmap[VIRT_GIC_REDIST].size / 0x20000;
+ } else {
+ virt_max_cpus = GIC_NCPU;
+ }
+
+ if (max_cpus > virt_max_cpus) {
+ error_report("Number of SMP CPUs requested (%d) exceeds max CPUs "
+ "supported by machine 'mach-virt' (%d)",
+ max_cpus, virt_max_cpus);
+ exit(1);
+ }
+
vbi->smp_cpus = smp_cpus;
if (machine->ram_size > vbi->memmap[VIRT_MEM].size) {
- error_report("mach-virt: cannot model more than 30GB RAM");
+ error_report("mach-virt: cannot model more than %dGB RAM", RAMLIMIT_GB);
exit(1);
}
+ if (vms->secure) {
+ if (kvm_enabled()) {
+ error_report("mach-virt: KVM does not support Security extensions");
+ exit(1);
+ }
+
+ /* The Secure view of the world is the same as the NonSecure,
+ * but with a few extra devices. Create it as a container region
+ * containing the system memory at low priority; any secure-only
+ * devices go in at higher priority and take precedence.
+ */
+ secure_sysmem = g_new(MemoryRegion, 1);
+ memory_region_init(secure_sysmem, OBJECT(machine), "secure-memory",
+ UINT64_MAX);
+ memory_region_add_subregion_overlap(secure_sysmem, 0, sysmem, -1);
+ }
+
create_fdt(vbi);
for (n = 0; n < smp_cpus; n++) {
@@ -832,7 +1191,7 @@ static void machvirt_init(MachineState *machine)
char *cpuopts = g_strdup(cpustr[1]);
if (!oc) {
- fprintf(stderr, "Unable to find CPU definition\n");
+ error_report("Unable to find CPU definition");
exit(1);
}
cpuobj = object_new(object_class_get_name(oc));
@@ -849,12 +1208,15 @@ static void machvirt_init(MachineState *machine)
object_property_set_bool(cpuobj, false, "has_el3", NULL);
}
- object_property_set_int(cpuobj, QEMU_PSCI_CONDUIT_HVC, "psci-conduit",
- NULL);
+ if (vbi->using_psci) {
+ object_property_set_int(cpuobj, QEMU_PSCI_CONDUIT_HVC,
+ "psci-conduit", NULL);
- /* Secondary CPUs start in PSCI powered-down state */
- if (n > 0) {
- object_property_set_bool(cpuobj, true, "start-powered-off", NULL);
+ /* Secondary CPUs start in PSCI powered-down state */
+ if (n > 0) {
+ object_property_set_bool(cpuobj, true,
+ "start-powered-off", NULL);
+ }
}
if (object_property_find(cpuobj, "reset-cbar", NULL)) {
@@ -862,10 +1224,17 @@ static void machvirt_init(MachineState *machine)
"reset-cbar", &error_abort);
}
+ object_property_set_link(cpuobj, OBJECT(sysmem), "memory",
+ &error_abort);
+ if (vms->secure) {
+ object_property_set_link(cpuobj, OBJECT(secure_sysmem),
+ "secure-memory", &error_abort);
+ }
+
object_property_set_bool(cpuobj, true, "realized", NULL);
}
g_strfreev(cpustr);
- fdt_add_timer_nodes(vbi);
+ fdt_add_timer_nodes(vbi, gic_version);
fdt_add_cpu_nodes(vbi);
fdt_add_psci_node(vbi);
@@ -873,15 +1242,22 @@ static void machvirt_init(MachineState *machine)
machine->ram_size);
memory_region_add_subregion(sysmem, vbi->memmap[VIRT_MEM].base, ram);
- create_flash(vbi);
+ create_flash(vbi, sysmem, secure_sysmem ? secure_sysmem : sysmem);
+
+ create_gic(vbi, pic, gic_version, vms->secure);
- create_gic(vbi, pic);
+ create_uart(vbi, pic, VIRT_UART, sysmem);
- create_uart(vbi, pic);
+ if (vms->secure) {
+ create_secure_ram(vbi, secure_sysmem);
+ create_uart(vbi, pic, VIRT_SECURE_UART, secure_sysmem);
+ }
create_rtc(vbi, pic);
- create_pcie(vbi, pic);
+ create_pcie(vbi, pic, vms->highmem);
+
+ create_gpio(vbi, pic);
/* Create mmio transports, so the user can create virtio backends
* (which will be automatically plugged in to the transports). If
@@ -889,13 +1265,15 @@ static void machvirt_init(MachineState *machine)
*/
create_virtio_devices(vbi, pic);
- create_fw_cfg(vbi);
+ create_fw_cfg(vbi, &address_space_memory);
rom_set_fw(fw_cfg_find());
guest_info->smp_cpus = smp_cpus;
guest_info->fw_cfg = fw_cfg_find();
guest_info->memmap = vbi->memmap;
guest_info->irqmap = vbi->irqmap;
+ guest_info->use_highmem = vms->highmem;
+ guest_info->gic_version = gic_version;
guest_info_state->machine_done.notify = virt_guest_info_machine_done;
qemu_add_machine_init_done_notifier(&guest_info_state->machine_done);
@@ -907,7 +1285,7 @@ static void machvirt_init(MachineState *machine)
vbi->bootinfo.board_id = -1;
vbi->bootinfo.loader_start = vbi->memmap[VIRT_MEM].base;
vbi->bootinfo.get_dtb = machvirt_dtb;
- vbi->bootinfo.firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0);
+ vbi->bootinfo.firmware_loaded = firmware_loaded;
arm_load_kernel(ARM_CPU(first_cpu), &vbi->bootinfo);
/*
@@ -933,45 +1311,125 @@ static void virt_set_secure(Object *obj, bool value, Error **errp)
vms->secure = value;
}
-static void virt_instance_init(Object *obj)
+static bool virt_get_highmem(Object *obj, Error **errp)
+{
+ VirtMachineState *vms = VIRT_MACHINE(obj);
+
+ return vms->highmem;
+}
+
+static void virt_set_highmem(Object *obj, bool value, Error **errp)
+{
+ VirtMachineState *vms = VIRT_MACHINE(obj);
+
+ vms->highmem = value;
+}
+
+static char *virt_get_gic_version(Object *obj, Error **errp)
+{
+ VirtMachineState *vms = VIRT_MACHINE(obj);
+ const char *val = vms->gic_version == 3 ? "3" : "2";
+
+ return g_strdup(val);
+}
+
+static void virt_set_gic_version(Object *obj, const char *value, Error **errp)
+{
+ VirtMachineState *vms = VIRT_MACHINE(obj);
+
+ if (!strcmp(value, "3")) {
+ vms->gic_version = 3;
+ } else if (!strcmp(value, "2")) {
+ vms->gic_version = 2;
+ } else if (!strcmp(value, "host")) {
+ vms->gic_version = 0; /* Will probe later */
+ } else {
+ error_setg(errp, "Invalid gic-version value");
+ error_append_hint(errp, "Valid values are 3, 2, host.\n");
+ }
+}
+
+static void virt_machine_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->init = machvirt_init;
+ /* Start max_cpus at the maximum QEMU supports. We'll further restrict
+ * it later in machvirt_init, where we have more information about the
+ * configuration of the particular instance.
+ */
+ mc->max_cpus = MAX_CPUMASK_BITS;
+ mc->has_dynamic_sysbus = true;
+ mc->block_default_type = IF_VIRTIO;
+ mc->no_cdrom = 1;
+ mc->pci_allow_0_address = true;
+}
+
+static const TypeInfo virt_machine_info = {
+ .name = TYPE_VIRT_MACHINE,
+ .parent = TYPE_MACHINE,
+ .abstract = true,
+ .instance_size = sizeof(VirtMachineState),
+ .class_size = sizeof(VirtMachineClass),
+ .class_init = virt_machine_class_init,
+};
+
+static void virt_2_6_instance_init(Object *obj)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
- /* EL3 is enabled by default on virt */
- vms->secure = true;
+ /* EL3 is disabled by default on virt: this makes us consistent
+ * between KVM and TCG for this board, and it also allows us to
+ * boot UEFI blobs which assume no TrustZone support.
+ */
+ vms->secure = false;
object_property_add_bool(obj, "secure", virt_get_secure,
virt_set_secure, NULL);
object_property_set_description(obj, "secure",
"Set on/off to enable/disable the ARM "
"Security Extensions (TrustZone)",
NULL);
+
+ /* High memory is enabled by default */
+ vms->highmem = true;
+ object_property_add_bool(obj, "highmem", virt_get_highmem,
+ virt_set_highmem, NULL);
+ object_property_set_description(obj, "highmem",
+ "Set on/off to enable/disable using "
+ "physical address space above 32 bits",
+ NULL);
+ /* Default GIC type is v2 */
+ vms->gic_version = 2;
+ object_property_add_str(obj, "gic-version", virt_get_gic_version,
+ virt_set_gic_version, NULL);
+ object_property_set_description(obj, "gic-version",
+ "Set GIC version. "
+ "Valid values are 2, 3 and host", NULL);
}
-static void virt_class_init(ObjectClass *oc, void *data)
+static void virt_2_6_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
+ static GlobalProperty compat_props[] = {
+ { /* end of list */ }
+ };
- mc->name = TYPE_VIRT_MACHINE;
- mc->desc = "ARM Virtual Machine",
- mc->init = machvirt_init;
- mc->max_cpus = 8;
- mc->has_dynamic_sysbus = true;
- mc->block_default_type = IF_VIRTIO;
- mc->no_cdrom = 1;
+ mc->desc = "QEMU 2.6 ARM Virtual Machine";
+ mc->alias = "virt";
+ mc->compat_props = compat_props;
}
static const TypeInfo machvirt_info = {
- .name = TYPE_VIRT_MACHINE,
- .parent = TYPE_MACHINE,
- .instance_size = sizeof(VirtMachineState),
- .instance_init = virt_instance_init,
- .class_size = sizeof(VirtMachineClass),
- .class_init = virt_class_init,
+ .name = MACHINE_TYPE_NAME("virt-2.6"),
+ .parent = TYPE_VIRT_MACHINE,
+ .instance_init = virt_2_6_instance_init,
+ .class_init = virt_2_6_class_init,
};
static void machvirt_machine_init(void)
{
+ type_register_static(&virt_machine_info);
type_register_static(&machvirt_info);
}
-machine_init(machvirt_machine_init);
+type_init(machvirt_machine_init);
diff --git a/qemu/hw/arm/xilinx_zynq.c b/qemu/hw/arm/xilinx_zynq.c
index a4e7b5c63..98b17c9ae 100644
--- a/qemu/hw/arm/xilinx_zynq.c
+++ b/qemu/hw/arm/xilinx_zynq.c
@@ -15,6 +15,10 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/sysbus.h"
#include "hw/arm/arm.h"
#include "net/net.h"
@@ -24,8 +28,10 @@
#include "hw/block/flash.h"
#include "sysemu/block-backend.h"
#include "hw/loader.h"
-#include "hw/ssi.h"
+#include "hw/misc/zynq-xadc.h"
+#include "hw/ssi/ssi.h"
#include "qemu/error-report.h"
+#include "hw/sd/sd.h"
#define NUM_SPI_FLASHES 4
#define NUM_QSPI_FLASHES 2
@@ -43,6 +49,45 @@ static const int dma_irqs[8] = {
46, 47, 48, 49, 72, 73, 74, 75
};
+#define BOARD_SETUP_ADDR 0x100
+
+#define SLCR_LOCK_OFFSET 0x004
+#define SLCR_UNLOCK_OFFSET 0x008
+#define SLCR_ARM_PLL_OFFSET 0x100
+
+#define SLCR_XILINX_UNLOCK_KEY 0xdf0d
+#define SLCR_XILINX_LOCK_KEY 0x767b
+
+#define ARMV7_IMM16(x) (extract32((x), 0, 12) | \
+ extract32((x), 12, 4) << 16)
+
+/* Write immediate val to address r0 + addr. r0 should contain base offset
+ * of the SLCR block. Clobbers r1.
+ */
+
+#define SLCR_WRITE(addr, val) \
+ 0xe3001000 + ARMV7_IMM16(extract32((val), 0, 16)), /* movw r1 ... */ \
+ 0xe3401000 + ARMV7_IMM16(extract32((val), 16, 16)), /* movt r1 ... */ \
+ 0xe5801000 + (addr)
+
+static void zynq_write_board_setup(ARMCPU *cpu,
+ const struct arm_boot_info *info)
+{
+ int n;
+ uint32_t board_setup_blob[] = {
+ 0xe3a004f8, /* mov r0, #0xf8000000 */
+ SLCR_WRITE(SLCR_UNLOCK_OFFSET, SLCR_XILINX_UNLOCK_KEY),
+ SLCR_WRITE(SLCR_ARM_PLL_OFFSET, 0x00014008),
+ SLCR_WRITE(SLCR_LOCK_OFFSET, SLCR_XILINX_LOCK_KEY),
+ 0xe12fff1e, /* bx lr */
+ };
+ for (n = 0; n < ARRAY_SIZE(board_setup_blob); n++) {
+ board_setup_blob[n] = tswap32(board_setup_blob[n]);
+ }
+ rom_add_blob_fixed("board-setup", board_setup_blob,
+ sizeof(board_setup_blob), BOARD_SETUP_ADDR);
+}
+
static struct arm_boot_info zynq_binfo = {};
static void gem_init(NICInfo *nd, uint32_t base, qemu_irq irq)
@@ -113,10 +158,11 @@ static void zynq_init(MachineState *machine)
MemoryRegion *address_space_mem = get_system_memory();
MemoryRegion *ext_ram = g_new(MemoryRegion, 1);
MemoryRegion *ocm_ram = g_new(MemoryRegion, 1);
- DeviceState *dev;
+ DeviceState *dev, *carddev;
SysBusDevice *busdev;
+ DriveInfo *di;
+ BlockBackend *blk;
qemu_irq pic[64];
- Error *err = NULL;
int n;
if (!cpu_model) {
@@ -131,29 +177,14 @@ static void zynq_init(MachineState *machine)
* realization.
*/
if (object_property_find(OBJECT(cpu), "has_el3", NULL)) {
- object_property_set_bool(OBJECT(cpu), false, "has_el3", &err);
- if (err) {
- error_report_err(err);
- exit(1);
- }
- }
-
- object_property_set_int(OBJECT(cpu), ZYNQ_BOARD_MIDR, "midr", &err);
- if (err) {
- error_report_err(err);
- exit(1);
+ object_property_set_bool(OBJECT(cpu), false, "has_el3", &error_fatal);
}
- object_property_set_int(OBJECT(cpu), MPCORE_PERIPHBASE, "reset-cbar", &err);
- if (err) {
- error_report_err(err);
- exit(1);
- }
- object_property_set_bool(OBJECT(cpu), true, "realized", &err);
- if (err) {
- error_report_err(err);
- exit(1);
- }
+ object_property_set_int(OBJECT(cpu), ZYNQ_BOARD_MIDR, "midr",
+ &error_fatal);
+ object_property_set_int(OBJECT(cpu), MPCORE_PERIPHBASE, "reset-cbar",
+ &error_fatal);
+ object_property_set_bool(OBJECT(cpu), true, "realized", &error_fatal);
/* max 2GB ram */
if (ram_size > 0x80000000) {
@@ -167,7 +198,7 @@ static void zynq_init(MachineState *machine)
/* 256K of on-chip memory */
memory_region_init_ram(ocm_ram, NULL, "zynq.ocm_ram", 256 << 10,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(ocm_ram);
memory_region_add_subregion(address_space_mem, 0xFFFC0000, ocm_ram);
@@ -220,11 +251,28 @@ static void zynq_init(MachineState *machine)
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xE0100000);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[56-IRQ_OFFSET]);
+ di = drive_get_next(IF_SD);
+ blk = di ? blk_by_legacy_dinfo(di) : NULL;
+ carddev = qdev_create(qdev_get_child_bus(dev, "sd-bus"), TYPE_SD_CARD);
+ qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
+ object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal);
+
dev = qdev_create(NULL, "generic-sdhci");
qdev_init_nofail(dev);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xE0101000);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[79-IRQ_OFFSET]);
+ di = drive_get_next(IF_SD);
+ blk = di ? blk_by_legacy_dinfo(di) : NULL;
+ carddev = qdev_create(qdev_get_child_bus(dev, "sd-bus"), TYPE_SD_CARD);
+ qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
+ object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal);
+
+ dev = qdev_create(NULL, TYPE_ZYNQ_XADC);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xF8007100);
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[39-IRQ_OFFSET]);
+
dev = qdev_create(NULL, "pl330");
qdev_prop_set_uint8(dev, "num_chnls", 8);
qdev_prop_set_uint8(dev, "num_periph_req", 4);
@@ -252,21 +300,19 @@ static void zynq_init(MachineState *machine)
zynq_binfo.nb_cpus = 1;
zynq_binfo.board_id = 0xd32;
zynq_binfo.loader_start = 0;
+ zynq_binfo.board_setup_addr = BOARD_SETUP_ADDR;
+ zynq_binfo.write_board_setup = zynq_write_board_setup;
+
arm_load_kernel(ARM_CPU(first_cpu), &zynq_binfo);
}
-static QEMUMachine zynq_machine = {
- .name = "xilinx-zynq-a9",
- .desc = "Xilinx Zynq Platform Baseboard for Cortex-A9",
- .init = zynq_init,
- .block_default_type = IF_SCSI,
- .max_cpus = 1,
- .no_sdcard = 1,
-};
-
-static void zynq_machine_init(void)
+static void zynq_machine_init(MachineClass *mc)
{
- qemu_register_machine(&zynq_machine);
+ mc->desc = "Xilinx Zynq Platform Baseboard for Cortex-A9";
+ mc->init = zynq_init;
+ mc->block_default_type = IF_SCSI;
+ mc->max_cpus = 1;
+ mc->no_sdcard = 1;
}
-machine_init(zynq_machine_init);
+DEFINE_MACHINE("xilinx-zynq-a9", zynq_machine_init)
diff --git a/qemu/hw/arm/xlnx-ep108.c b/qemu/hw/arm/xlnx-ep108.c
index f94da86cb..5f480182b 100644
--- a/qemu/hw/arm/xlnx-ep108.c
+++ b/qemu/hw/arm/xlnx-ep108.c
@@ -15,6 +15,10 @@
* for more details.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/arm/xlnx-zynqmp.h"
#include "hw/boards.h"
#include "qemu/error-report.h"
@@ -25,42 +29,76 @@ typedef struct XlnxEP108 {
MemoryRegion ddr_ram;
} XlnxEP108;
-/* Max 2GB RAM */
-#define EP108_MAX_RAM_SIZE 0x80000000ull
-
static struct arm_boot_info xlnx_ep108_binfo;
static void xlnx_ep108_init(MachineState *machine)
{
XlnxEP108 *s = g_new0(XlnxEP108, 1);
- Error *err = NULL;
+ int i;
+ uint64_t ram_size = machine->ram_size;
+
+ /* Create the memory region to pass to the SoC */
+ if (ram_size > XLNX_ZYNQMP_MAX_RAM_SIZE) {
+ error_report("ERROR: RAM size 0x%" PRIx64 " above max supported of "
+ "0x%llx", ram_size,
+ XLNX_ZYNQMP_MAX_RAM_SIZE);
+ exit(1);
+ }
+
+ if (ram_size < 0x08000000) {
+ qemu_log("WARNING: RAM size 0x%" PRIx64 " is small for EP108",
+ ram_size);
+ }
+
+ memory_region_allocate_system_memory(&s->ddr_ram, NULL, "ddr-ram",
+ ram_size);
object_initialize(&s->soc, sizeof(s->soc), TYPE_XLNX_ZYNQMP);
object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
&error_abort);
- object_property_set_bool(OBJECT(&s->soc), true, "realized", &err);
- if (err) {
- error_report("%s", error_get_pretty(err));
- exit(1);
- }
+ object_property_set_link(OBJECT(&s->soc), OBJECT(&s->ddr_ram),
+ "ddr-ram", &error_abort);
- if (machine->ram_size > EP108_MAX_RAM_SIZE) {
- error_report("WARNING: RAM size " RAM_ADDR_FMT " above max supported, "
- "reduced to %llx", machine->ram_size, EP108_MAX_RAM_SIZE);
- machine->ram_size = EP108_MAX_RAM_SIZE;
- }
+ object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_fatal);
+
+ /* Create and plug in the SD cards */
+ for (i = 0; i < XLNX_ZYNQMP_NUM_SDHCI; i++) {
+ BusState *bus;
+ DriveInfo *di = drive_get_next(IF_SD);
+ BlockBackend *blk = di ? blk_by_legacy_dinfo(di) : NULL;
+ DeviceState *carddev;
+ char *bus_name;
- if (machine->ram_size <= 0x08000000) {
- qemu_log("WARNING: RAM size " RAM_ADDR_FMT " is small for EP108",
- machine->ram_size);
+ bus_name = g_strdup_printf("sd-bus%d", i);
+ bus = qdev_get_child_bus(DEVICE(&s->soc), bus_name);
+ g_free(bus_name);
+ if (!bus) {
+ error_report("No SD bus found for SD card %d", i);
+ exit(1);
+ }
+ carddev = qdev_create(bus, TYPE_SD_CARD);
+ qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
+ object_property_set_bool(OBJECT(carddev), true, "realized",
+ &error_fatal);
}
- memory_region_allocate_system_memory(&s->ddr_ram, NULL, "ddr-ram",
- machine->ram_size);
- memory_region_add_subregion(get_system_memory(), 0, &s->ddr_ram);
+ for (i = 0; i < XLNX_ZYNQMP_NUM_SPIS; i++) {
+ SSIBus *spi_bus;
+ DeviceState *flash_dev;
+ qemu_irq cs_line;
+ gchar *bus_name = g_strdup_printf("spi%d", i);
+
+ spi_bus = (SSIBus *)qdev_get_child_bus(DEVICE(&s->soc), bus_name);
+ g_free(bus_name);
- xlnx_ep108_binfo.ram_size = machine->ram_size;
+ flash_dev = ssi_create_slave(spi_bus, "sst25wf080");
+ cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0);
+
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->soc.spi[i]), 1, cs_line);
+ }
+
+ xlnx_ep108_binfo.ram_size = ram_size;
xlnx_ep108_binfo.kernel_filename = machine->kernel_filename;
xlnx_ep108_binfo.kernel_cmdline = machine->kernel_cmdline;
xlnx_ep108_binfo.initrd_filename = machine->initrd_filename;
@@ -68,15 +106,10 @@ static void xlnx_ep108_init(MachineState *machine)
arm_load_kernel(s->soc.boot_cpu_ptr, &xlnx_ep108_binfo);
}
-static QEMUMachine xlnx_ep108_machine = {
- .name = "xlnx-ep108",
- .desc = "Xilinx ZynqMP EP108 board",
- .init = xlnx_ep108_init,
-};
-
-static void xlnx_ep108_machine_init(void)
+static void xlnx_ep108_machine_init(MachineClass *mc)
{
- qemu_register_machine(&xlnx_ep108_machine);
+ mc->desc = "Xilinx ZynqMP EP108 board";
+ mc->init = xlnx_ep108_init;
}
-machine_init(xlnx_ep108_machine_init);
+DEFINE_MACHINE("xlnx-ep108", xlnx_ep108_machine_init)
diff --git a/qemu/hw/arm/xlnx-zynqmp.c b/qemu/hw/arm/xlnx-zynqmp.c
index 62ef4ceb3..4d504da64 100644
--- a/qemu/hw/arm/xlnx-zynqmp.c
+++ b/qemu/hw/arm/xlnx-zynqmp.c
@@ -15,6 +15,10 @@
* for more details.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/arm/xlnx-zynqmp.h"
#include "hw/intc/arm_gic_common.h"
#include "exec/address-spaces.h"
@@ -28,6 +32,10 @@
#define GIC_DIST_ADDR 0xf9010000
#define GIC_CPU_ADDR 0xf9020000
+#define SATA_INTR 133
+#define SATA_ADDR 0xFD0C0000
+#define SATA_NUM_PORTS 2
+
static const uint64_t gem_addr[XLNX_ZYNQMP_NUM_GEMS] = {
0xFF0B0000, 0xFF0C0000, 0xFF0D0000, 0xFF0E0000,
};
@@ -44,6 +52,22 @@ static const int uart_intr[XLNX_ZYNQMP_NUM_UARTS] = {
21, 22,
};
+static const uint64_t sdhci_addr[XLNX_ZYNQMP_NUM_SDHCI] = {
+ 0xFF160000, 0xFF170000,
+};
+
+static const int sdhci_intr[XLNX_ZYNQMP_NUM_SDHCI] = {
+ 48, 49,
+};
+
+static const uint64_t spi_addr[XLNX_ZYNQMP_NUM_SPIS] = {
+ 0xFF040000, 0xFF050000,
+};
+
+static const int spi_intr[XLNX_ZYNQMP_NUM_SPIS] = {
+ 19, 20,
+};
+
typedef struct XlnxZynqMPGICRegion {
int region_index;
uint32_t address;
@@ -78,6 +102,11 @@ static void xlnx_zynqmp_init(Object *obj)
&error_abort);
}
+ object_property_add_link(obj, "ddr-ram", TYPE_MEMORY_REGION,
+ (Object **)&s->ddr_ram,
+ qdev_prop_allow_set_link_before_realize,
+ OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort);
+
object_initialize(&s->gic, sizeof(s->gic), TYPE_ARM_GIC);
qdev_set_parent_bus(DEVICE(&s->gic), sysbus_get_default());
@@ -90,6 +119,22 @@ static void xlnx_zynqmp_init(Object *obj)
object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_CADENCE_UART);
qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default());
}
+
+ object_initialize(&s->sata, sizeof(s->sata), TYPE_SYSBUS_AHCI);
+ qdev_set_parent_bus(DEVICE(&s->sata), sysbus_get_default());
+
+ for (i = 0; i < XLNX_ZYNQMP_NUM_SDHCI; i++) {
+ object_initialize(&s->sdhci[i], sizeof(s->sdhci[i]),
+ TYPE_SYSBUS_SDHCI);
+ qdev_set_parent_bus(DEVICE(&s->sdhci[i]),
+ sysbus_get_default());
+ }
+
+ for (i = 0; i < XLNX_ZYNQMP_NUM_SPIS; i++) {
+ object_initialize(&s->spi[i], sizeof(s->spi[i]),
+ TYPE_XILINX_SPIPS);
+ qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default());
+ }
}
static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
@@ -97,16 +142,63 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
XlnxZynqMPState *s = XLNX_ZYNQMP(dev);
MemoryRegion *system_memory = get_system_memory();
uint8_t i;
+ uint64_t ram_size;
const char *boot_cpu = s->boot_cpu ? s->boot_cpu : "apu-cpu[0]";
+ ram_addr_t ddr_low_size, ddr_high_size;
qemu_irq gic_spi[GIC_NUM_SPI_INTR];
Error *err = NULL;
+ ram_size = memory_region_size(s->ddr_ram);
+
+ /* Create the DDR Memory Regions. User friendly checks should happen at
+ * the board level
+ */
+ if (ram_size > XLNX_ZYNQMP_MAX_LOW_RAM_SIZE) {
+ /* The RAM size is above the maximum available for the low DDR.
+ * Create the high DDR memory region as well.
+ */
+ assert(ram_size <= XLNX_ZYNQMP_MAX_RAM_SIZE);
+ ddr_low_size = XLNX_ZYNQMP_MAX_LOW_RAM_SIZE;
+ ddr_high_size = ram_size - XLNX_ZYNQMP_MAX_LOW_RAM_SIZE;
+
+ memory_region_init_alias(&s->ddr_ram_high, NULL,
+ "ddr-ram-high", s->ddr_ram,
+ ddr_low_size, ddr_high_size);
+ memory_region_add_subregion(get_system_memory(),
+ XLNX_ZYNQMP_HIGH_RAM_START,
+ &s->ddr_ram_high);
+ } else {
+ /* RAM must be non-zero */
+ assert(ram_size);
+ ddr_low_size = ram_size;
+ }
+
+ memory_region_init_alias(&s->ddr_ram_low, NULL,
+ "ddr-ram-low", s->ddr_ram,
+ 0, ddr_low_size);
+ memory_region_add_subregion(get_system_memory(), 0, &s->ddr_ram_low);
+
+ /* Create the four OCM banks */
+ for (i = 0; i < XLNX_ZYNQMP_NUM_OCM_BANKS; i++) {
+ char *ocm_name = g_strdup_printf("zynqmp.ocm_ram_bank_%d", i);
+
+ memory_region_init_ram(&s->ocm_ram[i], NULL, ocm_name,
+ XLNX_ZYNQMP_OCM_RAM_SIZE, &error_fatal);
+ vmstate_register_ram_global(&s->ocm_ram[i]);
+ memory_region_add_subregion(get_system_memory(),
+ XLNX_ZYNQMP_OCM_RAM_0_ADDRESS +
+ i * XLNX_ZYNQMP_OCM_RAM_SIZE,
+ &s->ocm_ram[i]);
+
+ g_free(ocm_name);
+ }
+
qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq", GIC_NUM_SPI_INTR + 32);
qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 2);
qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", XLNX_ZYNQMP_NUM_APU_CPUS);
object_property_set_bool(OBJECT(&s->gic), true, "realized", &err);
if (err) {
- error_propagate((errp), (err));
+ error_propagate(errp, err);
return;
}
assert(ARRAY_SIZE(xlnx_zynqmp_gic_regions) == XLNX_ZYNQMP_GIC_REGIONS);
@@ -147,16 +239,11 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
g_free(name);
object_property_set_int(OBJECT(&s->apu_cpu[i]), GIC_BASE_ADDR,
- "reset-cbar", &err);
- if (err) {
- error_propagate((errp), (err));
- return;
- }
-
+ "reset-cbar", &error_abort);
object_property_set_bool(OBJECT(&s->apu_cpu[i]), true, "realized",
&err);
if (err) {
- error_propagate((errp), (err));
+ error_propagate(errp, err);
return;
}
@@ -185,22 +272,17 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
g_free(name);
object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, "reset-hivecs",
- &err);
- if (err != NULL) {
- error_propagate(errp, err);
- return;
- }
-
+ &error_abort);
object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, "realized",
&err);
if (err) {
- error_propagate((errp), (err));
+ error_propagate(errp, err);
return;
}
}
if (!s->boot_cpu_ptr) {
- error_setg(errp, "ZynqMP Boot cpu %s not found\n", boot_cpu);
+ error_setg(errp, "ZynqMP Boot cpu %s not found", boot_cpu);
return;
}
@@ -217,7 +299,7 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
}
object_property_set_bool(OBJECT(&s->gem[i]), true, "realized", &err);
if (err) {
- error_propagate((errp), (err));
+ error_propagate(errp, err);
return;
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gem[i]), 0, gem_addr[i]);
@@ -228,13 +310,62 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
for (i = 0; i < XLNX_ZYNQMP_NUM_UARTS; i++) {
object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err);
if (err) {
- error_propagate((errp), (err));
+ error_propagate(errp, err);
return;
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, uart_addr[i]);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0,
gic_spi[uart_intr[i]]);
}
+
+ object_property_set_int(OBJECT(&s->sata), SATA_NUM_PORTS, "num-ports",
+ &error_abort);
+ object_property_set_bool(OBJECT(&s->sata), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->sata), 0, SATA_ADDR);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->sata), 0, gic_spi[SATA_INTR]);
+
+ for (i = 0; i < XLNX_ZYNQMP_NUM_SDHCI; i++) {
+ char *bus_name;
+
+ object_property_set_bool(OBJECT(&s->sdhci[i]), true,
+ "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdhci[i]), 0,
+ sdhci_addr[i]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci[i]), 0,
+ gic_spi[sdhci_intr[i]]);
+ /* Alias controller SD bus to the SoC itself */
+ bus_name = g_strdup_printf("sd-bus%d", i);
+ object_property_add_alias(OBJECT(s), bus_name,
+ OBJECT(&s->sdhci[i]), "sd-bus",
+ &error_abort);
+ g_free(bus_name);
+ }
+
+ for (i = 0; i < XLNX_ZYNQMP_NUM_SPIS; i++) {
+ gchar *bus_name;
+
+ object_property_set_bool(OBJECT(&s->spi[i]), true, "realized", &err);
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, spi_addr[i]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0,
+ gic_spi[spi_intr[i]]);
+
+ /* Alias controller SPI bus to the SoC itself */
+ bus_name = g_strdup_printf("spi%d", i);
+ object_property_add_alias(OBJECT(s), bus_name,
+ OBJECT(&s->spi[i]), "spi0",
+ &error_abort);
+ g_free(bus_name);
+ }
}
static Property xlnx_zynqmp_props[] = {
@@ -248,6 +379,12 @@ static void xlnx_zynqmp_class_init(ObjectClass *oc, void *data)
dc->props = xlnx_zynqmp_props;
dc->realize = xlnx_zynqmp_realize;
+
+ /*
+ * Reason: creates an ARM CPU, thus use after free(), see
+ * arm_cpu_class_init()
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo xlnx_zynqmp_type_info = {
diff --git a/qemu/hw/arm/z2.c b/qemu/hw/arm/z2.c
index 17355479a..aea895a50 100644
--- a/qemu/hw/arm/z2.c
+++ b/qemu/hw/arm/z2.c
@@ -11,12 +11,13 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/arm/pxa.h"
#include "hw/arm/arm.h"
#include "hw/devices.h"
#include "hw/i2c/i2c.h"
-#include "hw/ssi.h"
+#include "hw/ssi/ssi.h"
#include "hw/boards.h"
#include "sysemu/sysemu.h"
#include "hw/block/flash.h"
@@ -372,15 +373,10 @@ static void z2_init(MachineState *machine)
arm_load_kernel(mpu->cpu, &z2_binfo);
}
-static QEMUMachine z2_machine = {
- .name = "z2",
- .desc = "Zipit Z2 (PXA27x)",
- .init = z2_init,
-};
-
-static void z2_machine_init(void)
+static void z2_machine_init(MachineClass *mc)
{
- qemu_register_machine(&z2_machine);
+ mc->desc = "Zipit Z2 (PXA27x)";
+ mc->init = z2_init;
}
-machine_init(z2_machine_init);
+DEFINE_MACHINE("z2", z2_machine_init)
diff --git a/qemu/hw/audio/ac97.c b/qemu/hw/audio/ac97.c
index b17383555..cbd959e0b 100644
--- a/qemu/hw/audio/ac97.c
+++ b/qemu/hw/audio/ac97.c
@@ -17,6 +17,7 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/audio/audio.h"
#include "audio/audio.h"
diff --git a/qemu/hw/audio/adlib.c b/qemu/hw/audio/adlib.c
index 656eb3773..7836446fc 100644
--- a/qemu/hw/audio/adlib.c
+++ b/qemu/hw/audio/adlib.c
@@ -22,6 +22,8 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/audio/audio.h"
#include "audio/audio.h"
@@ -57,11 +59,6 @@ void YMF262UpdateOneQEMU (int which, INT16 *dst, int length);
#define SHIFT 1
#endif
-#define IO_READ_PROTO(name) \
- uint32_t name (void *opaque, uint32_t nport)
-#define IO_WRITE_PROTO(name) \
- void name (void *opaque, uint32_t nport, uint32_t val)
-
#define TYPE_ADLIB "adlib"
#define ADLIB(obj) OBJECT_CHECK(AdlibState, (obj), TYPE_ADLIB)
@@ -124,7 +121,7 @@ static void adlib_kill_timers (AdlibState *s)
}
}
-static IO_WRITE_PROTO (adlib_write)
+static void adlib_write(void *opaque, uint32_t nport, uint32_t val)
{
AdlibState *s = opaque;
int a = nport & 3;
@@ -141,7 +138,7 @@ static IO_WRITE_PROTO (adlib_write)
#endif
}
-static IO_READ_PROTO (adlib_read)
+static uint32_t adlib_read(void *opaque, uint32_t nport)
{
AdlibState *s = opaque;
uint8_t data;
@@ -173,7 +170,7 @@ static void timer_handler (int c, double interval_Sec)
s->ticking[n] = 1;
#ifdef DEBUG
- interval = get_ticks_per_sec () * interval_Sec;
+ interval = NANOSECONDS_PER_SECOND * interval_Sec;
exp = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + interval;
s->exp[n] = exp;
#endif
diff --git a/qemu/hw/audio/cs4231.c b/qemu/hw/audio/cs4231.c
index 6325a8cea..caf97c169 100644
--- a/qemu/hw/audio/cs4231.c
+++ b/qemu/hw/audio/cs4231.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "trace.h"
diff --git a/qemu/hw/audio/cs4231a.c b/qemu/hw/audio/cs4231a.c
index f96f561c7..3ecd0582b 100644
--- a/qemu/hw/audio/cs4231a.c
+++ b/qemu/hw/audio/cs4231a.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/audio/audio.h"
#include "audio/audio.h"
@@ -69,6 +70,7 @@ typedef struct CSState {
uint32_t irq;
uint32_t dma;
uint32_t port;
+ IsaDma *isa_dma;
int shift;
int dma_running;
int audio_free;
@@ -264,6 +266,7 @@ static void cs_reset_voices (CSState *s, uint32_t val)
{
int xtal;
struct audsettings as;
+ IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma);
#ifdef DEBUG_XLAW
if (val == 0 || val == 32)
@@ -327,7 +330,7 @@ static void cs_reset_voices (CSState *s, uint32_t val)
if (s->dregs[Interface_Configuration] & PEN) {
if (!s->dma_running) {
- DMA_hold_DREQ (s->dma);
+ k->hold_DREQ(s->isa_dma, s->dma);
AUD_set_active_out (s->voice, 1);
s->transferred = 0;
}
@@ -335,7 +338,7 @@ static void cs_reset_voices (CSState *s, uint32_t val)
}
else {
if (s->dma_running) {
- DMA_release_DREQ (s->dma);
+ k->release_DREQ(s->isa_dma, s->dma);
AUD_set_active_out (s->voice, 0);
}
s->dma_running = 0;
@@ -344,7 +347,7 @@ static void cs_reset_voices (CSState *s, uint32_t val)
error:
if (s->dma_running) {
- DMA_release_DREQ (s->dma);
+ k->release_DREQ(s->isa_dma, s->dma);
AUD_set_active_out (s->voice, 0);
}
}
@@ -452,7 +455,8 @@ static void cs_write (void *opaque, hwaddr addr,
}
else {
if (s->dma_running) {
- DMA_release_DREQ (s->dma);
+ IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma);
+ k->release_DREQ(s->isa_dma, s->dma);
AUD_set_active_out (s->voice, 0);
s->dma_running = 0;
}
@@ -517,6 +521,7 @@ static int cs_write_audio (CSState *s, int nchan, int dma_pos,
{
int temp, net;
uint8_t tmpbuf[4096];
+ IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma);
temp = len;
net = 0;
@@ -531,7 +536,7 @@ static int cs_write_audio (CSState *s, int nchan, int dma_pos,
to_copy = sizeof (tmpbuf);
}
- copied = DMA_read_memory (nchan, tmpbuf, dma_pos, to_copy);
+ copied = k->read_memory(s->isa_dma, nchan, tmpbuf, dma_pos, to_copy);
if (s->tab) {
int i;
int16_t linbuf[4096];
@@ -599,7 +604,8 @@ static int cs4231a_pre_load (void *opaque)
CSState *s = opaque;
if (s->dma_running) {
- DMA_release_DREQ (s->dma);
+ IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma);
+ k->release_DREQ(s->isa_dma, s->dma);
AUD_set_active_out (s->voice, 0);
}
s->dma_running = 0;
@@ -655,13 +661,15 @@ static void cs4231a_realizefn (DeviceState *dev, Error **errp)
{
ISADevice *d = ISA_DEVICE (dev);
CSState *s = CS4231A (dev);
+ IsaDmaClass *k;
isa_init_irq (d, &s->pic, s->irq);
+ s->isa_dma = isa_get_dma(isa_bus_from_device(d), s->dma);
+ k = ISADMA_GET_CLASS(s->isa_dma);
+ k->register_channel(s->isa_dma, s->dma, cs_dma_read, s);
isa_register_ioport (d, &s->ioports, s->port);
- DMA_register_channel (s->dma, cs_dma_read, s);
-
AUD_register_card ("cs4231a", &s->card);
}
diff --git a/qemu/hw/audio/es1370.c b/qemu/hw/audio/es1370.c
index 8e7bcf503..8449b5f43 100644
--- a/qemu/hw/audio/es1370.c
+++ b/qemu/hw/audio/es1370.c
@@ -26,6 +26,7 @@
/* #define VERBOSE_ES1370 */
#define SILENT_ES1370
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/audio/audio.h"
#include "audio/audio.h"
@@ -157,11 +158,6 @@ static const unsigned dac1_samplerate[] = { 5512, 11025, 22050, 44100 };
#define DAC2_CHANNEL 1
#define ADC_CHANNEL 2
-#define IO_READ_PROTO(n) \
-static uint32_t n (void *opaque, uint32_t addr)
-#define IO_WRITE_PROTO(n) \
-static void n (void *opaque, uint32_t addr, uint32_t val)
-
static void es1370_dac1_callback (void *opaque, int free);
static void es1370_dac2_callback (void *opaque, int free);
static void es1370_adc_callback (void *opaque, int avail);
@@ -293,6 +289,10 @@ struct chan_bits {
uint32_t *old_freq, uint32_t *new_freq);
};
+#define TYPE_ES1370 "ES1370"
+#define ES1370(obj) \
+ OBJECT_CHECK(ES1370State, (obj), TYPE_ES1370)
+
static void es1370_dac1_calc_freq (ES1370State *s, uint32_t ctl,
uint32_t *old_freq, uint32_t *new_freq);
static void es1370_dac2_and_adc_calc_freq (ES1370State *s, uint32_t ctl,
@@ -474,7 +474,7 @@ static inline uint32_t es1370_fixup (ES1370State *s, uint32_t addr)
return addr;
}
-IO_WRITE_PROTO (es1370_writeb)
+static void es1370_writeb(void *opaque, uint32_t addr, uint32_t val)
{
ES1370State *s = opaque;
uint32_t shift, mask;
@@ -512,7 +512,7 @@ IO_WRITE_PROTO (es1370_writeb)
}
}
-IO_WRITE_PROTO (es1370_writew)
+static void es1370_writew(void *opaque, uint32_t addr, uint32_t val)
{
ES1370State *s = opaque;
addr = es1370_fixup (s, addr);
@@ -549,7 +549,7 @@ IO_WRITE_PROTO (es1370_writew)
}
}
-IO_WRITE_PROTO (es1370_writel)
+static void es1370_writel(void *opaque, uint32_t addr, uint32_t val)
{
ES1370State *s = opaque;
struct chan *d = &s->chan[0];
@@ -615,7 +615,7 @@ IO_WRITE_PROTO (es1370_writel)
}
}
-IO_READ_PROTO (es1370_readb)
+static uint32_t es1370_readb(void *opaque, uint32_t addr)
{
ES1370State *s = opaque;
uint32_t val;
@@ -650,7 +650,7 @@ IO_READ_PROTO (es1370_readb)
return val;
}
-IO_READ_PROTO (es1370_readw)
+static uint32_t es1370_readw(void *opaque, uint32_t addr)
{
ES1370State *s = opaque;
struct chan *d = &s->chan[0];
@@ -692,7 +692,7 @@ IO_READ_PROTO (es1370_readw)
return val;
}
-IO_READ_PROTO (es1370_readl)
+static uint32_t es1370_readl(void *opaque, uint32_t addr)
{
ES1370State *s = opaque;
uint32_t val;
@@ -1018,7 +1018,7 @@ static void es1370_on_reset (void *opaque)
static void es1370_realize(PCIDevice *dev, Error **errp)
{
- ES1370State *s = DO_UPCAST (ES1370State, dev, dev);
+ ES1370State *s = ES1370(dev);
uint8_t *c = s->dev.config;
c[PCI_STATUS + 1] = PCI_STATUS_DEVSEL_SLOW >> 8;
@@ -1043,7 +1043,7 @@ static void es1370_realize(PCIDevice *dev, Error **errp)
static int es1370_init (PCIBus *bus)
{
- pci_create_simple (bus, -1, "ES1370");
+ pci_create_simple (bus, -1, TYPE_ES1370);
return 0;
}
@@ -1064,7 +1064,7 @@ static void es1370_class_init (ObjectClass *klass, void *data)
}
static const TypeInfo es1370_info = {
- .name = "ES1370",
+ .name = TYPE_ES1370,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof (ES1370State),
.class_init = es1370_class_init,
diff --git a/qemu/hw/audio/fmopl.c b/qemu/hw/audio/fmopl.c
index adcef2d3b..731110fe8 100644
--- a/qemu/hw/audio/fmopl.c
+++ b/qemu/hw/audio/fmopl.c
@@ -32,10 +32,7 @@
#define HAS_YM3812 1
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
+#include "qemu/osdep.h"
#include <math.h>
//#include "driver.h" /* use M.A.M.E. */
#include "fmopl.h"
@@ -1177,7 +1174,7 @@ void OPLResetChip(FM_OPL *OPL)
OPLWriteReg(OPL,0x03,0); /* Timer2 */
OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */
for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0);
- /* reset OPerator paramater */
+ /* reset operator parameter */
for( c = 0 ; c < OPL->max_ch ; c++ )
{
OPL_CH *CH = &OPL->P_CH[c];
diff --git a/qemu/hw/audio/gus.c b/qemu/hw/audio/gus.c
index 86223a954..9dd6947be 100644
--- a/qemu/hw/audio/gus.c
+++ b/qemu/hw/audio/gus.c
@@ -21,6 +21,8 @@
* 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/hw.h"
#include "hw/audio/audio.h"
#include "audio/audio.h"
@@ -41,11 +43,6 @@
#define GUS_ENDIANNESS 0
#endif
-#define IO_READ_PROTO(name) \
- static uint32_t name (void *opaque, uint32_t nport)
-#define IO_WRITE_PROTO(name) \
- static void name (void *opaque, uint32_t nport, uint32_t val)
-
#define TYPE_GUS "gus"
#define GUS(obj) OBJECT_CHECK (GUSState, (obj), TYPE_GUS)
@@ -62,16 +59,17 @@ typedef struct GUSState {
SWVoiceOut *voice;
int64_t last_ticks;
qemu_irq pic;
+ IsaDma *isa_dma;
} GUSState;
-IO_READ_PROTO (gus_readb)
+static uint32_t gus_readb(void *opaque, uint32_t nport)
{
GUSState *s = opaque;
return gus_read (&s->emu, nport, 1);
}
-IO_WRITE_PROTO (gus_writeb)
+static void gus_writeb(void *opaque, uint32_t nport, uint32_t val)
{
GUSState *s = opaque;
@@ -172,34 +170,36 @@ void GUS_irqclear (GUSEmuState *emu, int hwirq)
#endif
}
-void GUS_dmarequest (GUSEmuState *der)
+void GUS_dmarequest (GUSEmuState *emu)
{
- /* GUSState *s = (GUSState *) der; */
+ GUSState *s = emu->opaque;
+ IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma);
ldebug ("dma request %d\n", der->gusdma);
- DMA_hold_DREQ (der->gusdma);
+ k->hold_DREQ(s->isa_dma, s->emu.gusdma);
}
static int GUS_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len)
{
GUSState *s = opaque;
+ IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma);
char tmpbuf[4096];
int pos = dma_pos, mode, left = dma_len - dma_pos;
ldebug ("read DMA %#x %d\n", dma_pos, dma_len);
- mode = DMA_get_channel_mode (s->emu.gusdma);
+ mode = k->has_autoinitialization(s->isa_dma, s->emu.gusdma);
while (left) {
int to_copy = audio_MIN ((size_t) left, sizeof (tmpbuf));
int copied;
ldebug ("left=%d to_copy=%d pos=%d\n", left, to_copy, pos);
- copied = DMA_read_memory (nchan, tmpbuf, pos, to_copy);
+ copied = k->read_memory(s->isa_dma, nchan, tmpbuf, pos, to_copy);
gus_dma_transferdata (&s->emu, tmpbuf, copied, left == copied);
left -= copied;
pos += copied;
}
if (((mode >> 4) & 1) == 0) {
- DMA_release_DREQ (s->emu.gusdma);
+ k->release_DREQ(s->isa_dma, s->emu.gusdma);
}
return dma_len;
}
@@ -236,6 +236,7 @@ static void gus_realizefn (DeviceState *dev, Error **errp)
{
ISADevice *d = ISA_DEVICE(dev);
GUSState *s = GUS (dev);
+ IsaDmaClass *k;
struct audsettings as;
AUD_register_card ("gus", &s->card);
@@ -268,7 +269,9 @@ static void gus_realizefn (DeviceState *dev, Error **errp)
isa_register_portio_list (d, (s->port + 0x100) & 0xf00,
gus_portio_list2, s, "gus");
- DMA_register_channel (s->emu.gusdma, GUS_read_DMA, s);
+ s->isa_dma = isa_get_dma(isa_bus_from_device(d), s->emu.gusdma);
+ k = ISADMA_GET_CLASS(s->isa_dma);
+ k->register_channel(s->isa_dma, s->emu.gusdma, GUS_read_DMA, s);
s->emu.himemaddr = s->himem;
s->emu.gusdatapos = s->emu.himemaddr + 1024 * 1024 + 32;
s->emu.opaque = s;
diff --git a/qemu/hw/audio/gusemu.h b/qemu/hw/audio/gusemu.h
index 331bb6fec..b7f075126 100644
--- a/qemu/hw/audio/gusemu.h
+++ b/qemu/hw/audio/gusemu.h
@@ -34,7 +34,6 @@
typedef signed char GUSchar;
typedef signed short GUSsample;
#else
- #include <stdint.h>
typedef int8_t GUSchar;
typedef uint8_t GUSbyte;
typedef uint16_t GUSword;
diff --git a/qemu/hw/audio/gusemu_hal.c b/qemu/hw/audio/gusemu_hal.c
index 609669073..973d6b9f4 100644
--- a/qemu/hw/audio/gusemu_hal.c
+++ b/qemu/hw/audio/gusemu_hal.c
@@ -26,6 +26,7 @@
* TODO: check mixer: see 7.20 of sdk for panning pos (applies to all gus models?)?
*/
+#include "qemu/osdep.h"
#include "gustate.h"
#include "gusemu.h"
diff --git a/qemu/hw/audio/gusemu_mixer.c b/qemu/hw/audio/gusemu_mixer.c
index 6d8d9ced1..701e8fb0e 100644
--- a/qemu/hw/audio/gusemu_mixer.c
+++ b/qemu/hw/audio/gusemu_mixer.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "gusemu.h"
#include "gustate.h"
diff --git a/qemu/hw/audio/hda-codec.c b/qemu/hw/audio/hda-codec.c
index 3c03ff566..52d4640e6 100644
--- a/qemu/hw/audio/hda-codec.c
+++ b/qemu/hw/audio/hda-codec.c
@@ -17,6 +17,7 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "intel-hda.h"
diff --git a/qemu/hw/audio/intel-hda.c b/qemu/hw/audio/intel-hda.c
index 433463e9c..d372d4ab9 100644
--- a/qemu/hw/audio/intel-hda.c
+++ b/qemu/hw/audio/intel-hda.c
@@ -17,6 +17,7 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "hw/pci/msi.h"
diff --git a/qemu/hw/audio/lm4549.c b/qemu/hw/audio/lm4549.c
index 380ef603b..a46f2301a 100644
--- a/qemu/hw/audio/lm4549.c
+++ b/qemu/hw/audio/lm4549.c
@@ -13,6 +13,7 @@
* It supports only one playback voice and no record voice.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "audio/audio.h"
#include "lm4549.h"
@@ -33,7 +34,6 @@ do { printf("lm4549: " fmt , ## __VA_ARGS__); } while (0)
#endif
#if defined(LM4549_DUMP_DAC_INPUT)
-#include <stdio.h>
static FILE *fp_dac_input;
#endif
diff --git a/qemu/hw/audio/marvell_88w8618.c b/qemu/hw/audio/marvell_88w8618.c
index 86992677e..a6ca1806b 100644
--- a/qemu/hw/audio/marvell_88w8618.c
+++ b/qemu/hw/audio/marvell_88w8618.c
@@ -9,6 +9,7 @@
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/hw.h"
#include "hw/i2c/i2c.h"
diff --git a/qemu/hw/audio/milkymist-ac97.c b/qemu/hw/audio/milkymist-ac97.c
index 28f55e853..6a3b53674 100644
--- a/qemu/hw/audio/milkymist-ac97.c
+++ b/qemu/hw/audio/milkymist-ac97.c
@@ -21,6 +21,7 @@
* http://www.milkymist.org/socdoc/ac97.pdf
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "trace.h"
diff --git a/qemu/hw/audio/pcspk.c b/qemu/hw/audio/pcspk.c
index 5266fb545..f9afc8eda 100644
--- a/qemu/hw/audio/pcspk.c
+++ b/qemu/hw/audio/pcspk.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/isa/isa.h"
diff --git a/qemu/hw/audio/pl041.c b/qemu/hw/audio/pl041.c
index 19982f247..4717bc9b9 100644
--- a/qemu/hw/audio/pl041.c
+++ b/qemu/hw/audio/pl041.c
@@ -20,6 +20,7 @@
*
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "pl041.h"
diff --git a/qemu/hw/audio/sb16.c b/qemu/hw/audio/sb16.c
index b052de5f7..3a4a57ac3 100644
--- a/qemu/hw/audio/sb16.c
+++ b/qemu/hw/audio/sb16.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/audio/audio.h"
#include "audio/audio.h"
@@ -40,11 +41,6 @@
#define ldebug(...)
#endif
-#define IO_READ_PROTO(name) \
- uint32_t name (void *opaque, uint32_t nport)
-#define IO_WRITE_PROTO(name) \
- void name (void *opaque, uint32_t nport, uint32_t val)
-
static const char e3[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992.";
#define TYPE_SB16 "sb16"
@@ -60,6 +56,8 @@ typedef struct SB16State {
uint32_t hdma;
uint32_t port;
uint32_t ver;
+ IsaDma *isa_dma;
+ IsaDma *isa_hdma;
int in_index;
int out_data_len;
@@ -170,16 +168,18 @@ static void speaker (SB16State *s, int on)
static void control (SB16State *s, int hold)
{
int dma = s->use_hdma ? s->hdma : s->dma;
+ IsaDma *isa_dma = s->use_hdma ? s->isa_hdma : s->isa_dma;
+ IsaDmaClass *k = ISADMA_GET_CLASS(isa_dma);
s->dma_running = hold;
ldebug ("hold %d high %d dma %d\n", hold, s->use_hdma, dma);
if (hold) {
- DMA_hold_DREQ (dma);
+ k->hold_DREQ(isa_dma, dma);
AUD_set_active_out (s->voice, 1);
}
else {
- DMA_release_DREQ (dma);
+ k->release_DREQ(isa_dma, dma);
AUD_set_active_out (s->voice, 0);
}
}
@@ -762,8 +762,8 @@ static void complete (SB16State *s)
freq = s->freq > 0 ? s->freq : 11025;
samples = dsp_get_lohi (s) + 1;
bytes = samples << s->fmt_stereo << (s->fmt_bits == 16);
- ticks = muldiv64 (bytes, get_ticks_per_sec (), freq);
- if (ticks < get_ticks_per_sec () / 1024) {
+ ticks = muldiv64(bytes, NANOSECONDS_PER_SECOND, freq);
+ if (ticks < NANOSECONDS_PER_SECOND / 1024) {
qemu_irq_raise (s->pic);
}
else {
@@ -881,7 +881,7 @@ static void reset (SB16State *s)
legacy_reset (s);
}
-static IO_WRITE_PROTO (dsp_write)
+static void dsp_write(void *opaque, uint32_t nport, uint32_t val)
{
SB16State *s = opaque;
int iport;
@@ -959,7 +959,7 @@ static IO_WRITE_PROTO (dsp_write)
}
}
-static IO_READ_PROTO (dsp_read)
+static uint32_t dsp_read(void *opaque, uint32_t nport)
{
SB16State *s = opaque;
int iport, retval, ack = 0;
@@ -1058,14 +1058,14 @@ static void reset_mixer (SB16State *s)
}
}
-static IO_WRITE_PROTO (mixer_write_indexb)
+static void mixer_write_indexb(void *opaque, uint32_t nport, uint32_t val)
{
SB16State *s = opaque;
(void) nport;
s->mixer_nreg = val;
}
-static IO_WRITE_PROTO (mixer_write_datab)
+static void mixer_write_datab(void *opaque, uint32_t nport, uint32_t val)
{
SB16State *s = opaque;
@@ -1121,7 +1121,7 @@ static IO_WRITE_PROTO (mixer_write_datab)
s->mixer_regs[s->mixer_nreg] = val;
}
-static IO_READ_PROTO (mixer_read)
+static uint32_t mixer_read(void *opaque, uint32_t nport)
{
SB16State *s = opaque;
@@ -1141,6 +1141,8 @@ static IO_READ_PROTO (mixer_read)
static int write_audio (SB16State *s, int nchan, int dma_pos,
int dma_len, int len)
{
+ IsaDma *isa_dma = nchan == s->dma ? s->isa_dma : s->isa_hdma;
+ IsaDmaClass *k = ISADMA_GET_CLASS(isa_dma);
int temp, net;
uint8_t tmpbuf[4096];
@@ -1157,7 +1159,7 @@ static int write_audio (SB16State *s, int nchan, int dma_pos,
to_copy = sizeof (tmpbuf);
}
- copied = DMA_read_memory (nchan, tmpbuf, dma_pos, to_copy);
+ copied = k->read_memory(isa_dma, nchan, tmpbuf, dma_pos, to_copy);
copied = AUD_write (s->voice, tmpbuf, copied);
temp -= copied;
@@ -1359,6 +1361,7 @@ static void sb16_realizefn (DeviceState *dev, Error **errp)
{
ISADevice *isadev = ISA_DEVICE (dev);
SB16State *s = SB16 (dev);
+ IsaDmaClass *k;
isa_init_irq (isadev, &s->pic, s->irq);
@@ -1377,8 +1380,14 @@ static void sb16_realizefn (DeviceState *dev, Error **errp)
isa_register_portio_list (isadev, s->port, sb16_ioport_list, s, "sb16");
- DMA_register_channel (s->hdma, SB_read_DMA, s);
- DMA_register_channel (s->dma, SB_read_DMA, s);
+ s->isa_hdma = isa_get_dma(isa_bus_from_device(isadev), s->hdma);
+ k = ISADMA_GET_CLASS(s->isa_hdma);
+ k->register_channel(s->isa_hdma, s->hdma, SB_read_DMA, s);
+
+ s->isa_dma = isa_get_dma(isa_bus_from_device(isadev), s->dma);
+ k = ISADMA_GET_CLASS(s->isa_dma);
+ k->register_channel(s->isa_dma, s->dma, SB_read_DMA, s);
+
s->can_write = 1;
AUD_register_card ("sb16", &s->card);
diff --git a/qemu/hw/audio/wm8750.c b/qemu/hw/audio/wm8750.c
index b50b33140..0c6500e96 100644
--- a/qemu/hw/audio/wm8750.c
+++ b/qemu/hw/audio/wm8750.c
@@ -7,6 +7,7 @@
* This file is licensed under GNU GPL.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i2c/i2c.h"
#include "audio/audio.h"
diff --git a/qemu/hw/block/block.c b/qemu/hw/block/block.c
index f7243e5b9..97a59d4fa 100644
--- a/qemu/hw/block/block.c
+++ b/qemu/hw/block/block.c
@@ -7,9 +7,11 @@
* later. See the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
#include "sysemu/blockdev.h"
#include "sysemu/block-backend.h"
#include "hw/block/block.h"
+#include "qapi/error.h"
#include "qemu/error-report.h"
void blkconf_serial(BlockConf *conf, char **serial)
diff --git a/qemu/hw/block/cdrom.c b/qemu/hw/block/cdrom.c
index 4e1019c89..da937fe33 100644
--- a/qemu/hw/block/cdrom.c
+++ b/qemu/hw/block/cdrom.c
@@ -25,6 +25,7 @@
/* ??? Most of the ATAPI emulation is still in ide.c. It should be moved
here. */
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "hw/scsi/scsi.h"
diff --git a/qemu/hw/block/dataplane/virtio-blk.c b/qemu/hw/block/dataplane/virtio-blk.c
index 6106e4615..3cb97c9a2 100644
--- a/qemu/hw/block/dataplane/virtio-blk.c
+++ b/qemu/hw/block/dataplane/virtio-blk.c
@@ -12,13 +12,13 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "trace.h"
#include "qemu/iov.h"
#include "qemu/thread.h"
#include "qemu/error-report.h"
#include "hw/virtio/virtio-access.h"
-#include "hw/virtio/dataplane/vring.h"
-#include "hw/virtio/dataplane/vring-accessors.h"
#include "sysemu/block-backend.h"
#include "hw/virtio/virtio-blk.h"
#include "virtio-blk.h"
@@ -27,114 +27,93 @@
#include "qom/object_interfaces.h"
struct VirtIOBlockDataPlane {
- bool started;
bool starting;
bool stopping;
- bool disabled;
VirtIOBlkConf *conf;
VirtIODevice *vdev;
- Vring vring; /* virtqueue vring */
+ VirtQueue *vq; /* virtqueue vring */
EventNotifier *guest_notifier; /* irq */
QEMUBH *bh; /* bh for guest notification */
+ Notifier insert_notifier, remove_notifier;
+
/* Note that these EventNotifiers are assigned by value. This is
* fine as long as you do not call event_notifier_cleanup on them
* (because you don't own the file descriptor or handle; you just
* use it).
*/
IOThread *iothread;
- IOThread internal_iothread_obj;
AioContext *ctx;
- EventNotifier host_notifier; /* doorbell */
/* Operation blocker on BDS */
Error *blocker;
- void (*saved_complete_request)(struct VirtIOBlockReq *req,
- unsigned char status);
};
/* Raise an interrupt to signal guest, if necessary */
-static void notify_guest(VirtIOBlockDataPlane *s)
+void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s)
{
- if (!vring_should_notify(s->vdev, &s->vring)) {
- return;
- }
-
- event_notifier_set(s->guest_notifier);
+ qemu_bh_schedule(s->bh);
}
static void notify_guest_bh(void *opaque)
{
VirtIOBlockDataPlane *s = opaque;
- notify_guest(s);
+ if (!virtio_should_notify(s->vdev, s->vq)) {
+ return;
+ }
+
+ event_notifier_set(s->guest_notifier);
}
-static void complete_request_vring(VirtIOBlockReq *req, unsigned char status)
+static void data_plane_set_up_op_blockers(VirtIOBlockDataPlane *s)
{
- VirtIOBlockDataPlane *s = req->dev->dataplane;
- stb_p(&req->in->status, status);
-
- vring_push(s->vdev, &req->dev->dataplane->vring, &req->elem, req->in_len);
+ assert(!s->blocker);
+ error_setg(&s->blocker, "block device is in use by data plane");
+ blk_op_block_all(s->conf->conf.blk, s->blocker);
+ blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_RESIZE, s->blocker);
+ blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_DRIVE_DEL, s->blocker);
+ blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_BACKUP_SOURCE, s->blocker);
+ blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_CHANGE, s->blocker);
+ blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_COMMIT_SOURCE, s->blocker);
+ blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_COMMIT_TARGET, s->blocker);
+ blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_EJECT, s->blocker);
+ blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT,
+ s->blocker);
+ blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT,
+ s->blocker);
+ blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE,
+ s->blocker);
+ blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_MIRROR_SOURCE, s->blocker);
+ blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_STREAM, s->blocker);
+ blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_REPLACE, s->blocker);
+}
- /* Suppress notification to guest by BH and its scheduled
- * flag because requests are completed as a batch after io
- * plug & unplug is introduced, and the BH can still be
- * executed in dataplane aio context even after it is
- * stopped, so needn't worry about notification loss with BH.
- */
- qemu_bh_schedule(s->bh);
+static void data_plane_remove_op_blockers(VirtIOBlockDataPlane *s)
+{
+ if (s->blocker) {
+ blk_op_unblock_all(s->conf->conf.blk, s->blocker);
+ error_free(s->blocker);
+ s->blocker = NULL;
+ }
}
-static void handle_notify(EventNotifier *e)
+static void data_plane_blk_insert_notifier(Notifier *n, void *data)
{
- VirtIOBlockDataPlane *s = container_of(e, VirtIOBlockDataPlane,
- host_notifier);
- VirtIOBlock *vblk = VIRTIO_BLK(s->vdev);
+ VirtIOBlockDataPlane *s = container_of(n, VirtIOBlockDataPlane,
+ insert_notifier);
+ assert(s->conf->conf.blk == data);
+ data_plane_set_up_op_blockers(s);
+}
- event_notifier_test_and_clear(&s->host_notifier);
- blk_io_plug(s->conf->conf.blk);
- for (;;) {
- MultiReqBuffer mrb = {};
- int ret;
-
- /* Disable guest->host notifies to avoid unnecessary vmexits */
- vring_disable_notification(s->vdev, &s->vring);
-
- for (;;) {
- VirtIOBlockReq *req = virtio_blk_alloc_request(vblk);
-
- ret = vring_pop(s->vdev, &s->vring, &req->elem);
- if (ret < 0) {
- virtio_blk_free_request(req);
- break; /* no more requests */
- }
-
- trace_virtio_blk_data_plane_process_request(s, req->elem.out_num,
- req->elem.in_num,
- req->elem.index);
-
- virtio_blk_handle_request(req, &mrb);
- }
-
- if (mrb.num_reqs) {
- virtio_blk_submit_multireq(s->conf->conf.blk, &mrb);
- }
-
- if (likely(ret == -EAGAIN)) { /* vring emptied */
- /* Re-enable guest->host notifies and stop processing the vring.
- * But if the guest has snuck in more descriptors, keep processing.
- */
- if (vring_enable_notification(s->vdev, &s->vring)) {
- break;
- }
- } else { /* fatal error */
- break;
- }
- }
- blk_io_unplug(s->conf->conf.blk);
+static void data_plane_blk_remove_notifier(Notifier *n, void *data)
+{
+ VirtIOBlockDataPlane *s = container_of(n, VirtIOBlockDataPlane,
+ remove_notifier);
+ assert(s->conf->conf.blk == data);
+ data_plane_remove_op_blockers(s);
}
/* Context: QEMU global mutex held */
@@ -143,20 +122,19 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
Error **errp)
{
VirtIOBlockDataPlane *s;
- Error *local_err = NULL;
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
*dataplane = NULL;
- if (!conf->data_plane && !conf->iothread) {
+ if (!conf->iothread) {
return;
}
/* Don't try if transport does not support notifiers. */
if (!k->set_guest_notifiers || !k->set_host_notifier) {
error_setg(errp,
- "device is incompatible with x-data-plane "
+ "device is incompatible with dataplane "
"(transport does not support notifiers)");
return;
}
@@ -164,11 +142,8 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
/* If dataplane is (re-)enabled while the guest is running there could be
* block jobs that can conflict.
*/
- if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE,
- &local_err)) {
- error_setg(errp, "cannot start dataplane thread: %s",
- error_get_pretty(local_err));
- error_free(local_err);
+ if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) {
+ error_prepend(errp, "cannot start dataplane thread: ");
return;
}
@@ -179,36 +154,16 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
if (conf->iothread) {
s->iothread = conf->iothread;
object_ref(OBJECT(s->iothread));
- } else {
- /* Create per-device IOThread if none specified. This is for
- * x-data-plane option compatibility. If x-data-plane is removed we
- * can drop this.
- */
- object_initialize(&s->internal_iothread_obj,
- sizeof(s->internal_iothread_obj),
- TYPE_IOTHREAD);
- user_creatable_complete(OBJECT(&s->internal_iothread_obj), &error_abort);
- s->iothread = &s->internal_iothread_obj;
}
s->ctx = iothread_get_aio_context(s->iothread);
s->bh = aio_bh_new(s->ctx, notify_guest_bh, s);
- error_setg(&s->blocker, "block device is in use by data plane");
- blk_op_block_all(conf->conf.blk, s->blocker);
- blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_RESIZE, s->blocker);
- blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_DRIVE_DEL, s->blocker);
- blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_BACKUP_SOURCE, s->blocker);
- blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_CHANGE, s->blocker);
- blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_COMMIT_SOURCE, s->blocker);
- blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_COMMIT_TARGET, s->blocker);
- blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_EJECT, s->blocker);
- blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, s->blocker);
- blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, s->blocker);
- blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE,
- s->blocker);
- blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_MIRROR, s->blocker);
- blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_STREAM, s->blocker);
- blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_REPLACE, s->blocker);
+ s->insert_notifier.notify = data_plane_blk_insert_notifier;
+ s->remove_notifier.notify = data_plane_blk_remove_notifier;
+ blk_add_insert_bs_notifier(conf->conf.blk, &s->insert_notifier);
+ blk_add_remove_bs_notifier(conf->conf.blk, &s->remove_notifier);
+
+ data_plane_set_up_op_blockers(s);
*dataplane = s;
}
@@ -221,36 +176,39 @@ void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s)
}
virtio_blk_data_plane_stop(s);
- blk_op_unblock_all(s->conf->conf.blk, s->blocker);
- error_free(s->blocker);
+ data_plane_remove_op_blockers(s);
+ notifier_remove(&s->insert_notifier);
+ notifier_remove(&s->remove_notifier);
qemu_bh_delete(s->bh);
object_unref(OBJECT(s->iothread));
g_free(s);
}
+static void virtio_blk_data_plane_handle_output(VirtIODevice *vdev,
+ VirtQueue *vq)
+{
+ VirtIOBlock *s = (VirtIOBlock *)vdev;
+
+ assert(s->dataplane);
+ assert(s->dataplane_started);
+
+ virtio_blk_handle_vq(s, vq);
+}
+
/* Context: QEMU global mutex held */
void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
{
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev)));
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
VirtIOBlock *vblk = VIRTIO_BLK(s->vdev);
- VirtQueue *vq;
int r;
- if (s->started || s->disabled) {
- return;
- }
-
- if (s->starting) {
+ if (vblk->dataplane_started || s->starting) {
return;
}
s->starting = true;
-
- vq = virtio_get_queue(s->vdev, 0);
- if (!vring_setup(&s->vring, s->vdev, 0)) {
- goto fail_vring;
- }
+ s->vq = virtio_get_queue(s->vdev, 0);
/* Set up guest notifier (irq) */
r = k->set_guest_notifiers(qbus->parent, 1, true);
@@ -259,7 +217,7 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
"ensure -enable-kvm is set\n", r);
goto fail_guest_notifiers;
}
- s->guest_notifier = virtio_queue_get_guest_notifier(vq);
+ s->guest_notifier = virtio_queue_get_guest_notifier(s->vq);
/* Set up virtqueue notify */
r = k->set_host_notifier(qbus->parent, 0, true);
@@ -267,33 +225,29 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
fprintf(stderr, "virtio-blk failed to set host notifier (%d)\n", r);
goto fail_host_notifier;
}
- s->host_notifier = *virtio_queue_get_host_notifier(vq);
-
- s->saved_complete_request = vblk->complete_request;
- vblk->complete_request = complete_request_vring;
s->starting = false;
- s->started = true;
+ vblk->dataplane_started = true;
trace_virtio_blk_data_plane_start(s);
blk_set_aio_context(s->conf->conf.blk, s->ctx);
/* Kick right away to begin processing requests already in vring */
- event_notifier_set(virtio_queue_get_host_notifier(vq));
+ event_notifier_set(virtio_queue_get_host_notifier(s->vq));
/* Get this show started by hooking up our callbacks */
aio_context_acquire(s->ctx);
- aio_set_event_notifier(s->ctx, &s->host_notifier, handle_notify);
+ virtio_queue_aio_set_host_notifier_handler(s->vq, s->ctx,
+ virtio_blk_data_plane_handle_output);
aio_context_release(s->ctx);
return;
fail_host_notifier:
k->set_guest_notifiers(qbus->parent, 1, false);
fail_guest_notifiers:
- vring_teardown(&s->vring, s->vdev, 0);
- s->disabled = true;
- fail_vring:
+ vblk->dataplane_disabled = true;
s->starting = false;
+ vblk->dataplane_started = true;
}
/* Context: QEMU global mutex held */
@@ -303,39 +257,34 @@ void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s)
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
VirtIOBlock *vblk = VIRTIO_BLK(s->vdev);
-
- /* Better luck next time. */
- if (s->disabled) {
- s->disabled = false;
+ if (!vblk->dataplane_started || s->stopping) {
return;
}
- if (!s->started || s->stopping) {
+
+ /* Better luck next time. */
+ if (vblk->dataplane_disabled) {
+ vblk->dataplane_disabled = false;
+ vblk->dataplane_started = false;
return;
}
s->stopping = true;
- vblk->complete_request = s->saved_complete_request;
trace_virtio_blk_data_plane_stop(s);
aio_context_acquire(s->ctx);
/* Stop notifications for new requests from guest */
- aio_set_event_notifier(s->ctx, &s->host_notifier, NULL);
+ virtio_queue_aio_set_host_notifier_handler(s->vq, s->ctx, NULL);
/* Drain and switch bs back to the QEMU main loop */
blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context());
aio_context_release(s->ctx);
- /* Sync vring state back to virtqueue so that non-dataplane request
- * processing can continue when we disable the host notifier below.
- */
- vring_teardown(&s->vring, s->vdev, 0);
-
k->set_host_notifier(qbus->parent, 0, false);
/* Clean up guest notifier (irq) */
k->set_guest_notifiers(qbus->parent, 1, false);
- s->started = false;
+ vblk->dataplane_started = false;
s->stopping = false;
}
diff --git a/qemu/hw/block/dataplane/virtio-blk.h b/qemu/hw/block/dataplane/virtio-blk.h
index c88d40e72..0714c11a2 100644
--- a/qemu/hw/block/dataplane/virtio-blk.h
+++ b/qemu/hw/block/dataplane/virtio-blk.h
@@ -26,5 +26,6 @@ void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s);
void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s);
void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s);
void virtio_blk_data_plane_drain(VirtIOBlockDataPlane *s);
+void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s);
#endif /* HW_DATAPLANE_VIRTIO_BLK_H */
diff --git a/qemu/hw/block/ecc.c b/qemu/hw/block/ecc.c
index 10bb23308..48311d260 100644
--- a/qemu/hw/block/ecc.c
+++ b/qemu/hw/block/ecc.c
@@ -11,6 +11,7 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/block/flash.h"
diff --git a/qemu/hw/block/fdc.c b/qemu/hw/block/fdc.c
index 5e1b67ee4..372227569 100644
--- a/qemu/hw/block/fdc.c
+++ b/qemu/hw/block/fdc.c
@@ -27,8 +27,10 @@
* way. There are changes in DOR register and DMA is not available.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/block/fdc.h"
+#include "qapi/error.h"
#include "qemu/error-report.h"
#include "qemu/timer.h"
#include "hw/isa/isa.h"
@@ -40,14 +42,15 @@
/********************************************************/
/* debug Floppy devices */
-//#define DEBUG_FLOPPY
-#ifdef DEBUG_FLOPPY
+#define DEBUG_FLOPPY 0
+
#define FLOPPY_DPRINTF(fmt, ...) \
- do { printf("FLOPPY: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define FLOPPY_DPRINTF(fmt, ...)
-#endif
+ do { \
+ if (DEBUG_FLOPPY) { \
+ fprintf(stderr, "FLOPPY: " fmt , ## __VA_ARGS__); \
+ } \
+ } while (0)
/********************************************************/
/* Floppy drive emulation */
@@ -59,104 +62,82 @@ typedef enum FDriveRate {
FDRIVE_RATE_1M = 0x03, /* 1 Mbps */
} FDriveRate;
+typedef enum FDriveSize {
+ FDRIVE_SIZE_UNKNOWN,
+ FDRIVE_SIZE_350,
+ FDRIVE_SIZE_525,
+} FDriveSize;
+
typedef struct FDFormat {
- FDriveType drive;
+ FloppyDriveType drive;
uint8_t last_sect;
uint8_t max_track;
uint8_t max_head;
FDriveRate rate;
} FDFormat;
+/* In many cases, the total sector size of a format is enough to uniquely
+ * identify it. However, there are some total sector collisions between
+ * formats of different physical size, and these are noted below by
+ * highlighting the total sector size for entries with collisions. */
static const FDFormat fd_formats[] = {
/* First entry is default format */
/* 1.44 MB 3"1/2 floppy disks */
- { FDRIVE_DRV_144, 18, 80, 1, FDRIVE_RATE_500K, },
- { FDRIVE_DRV_144, 20, 80, 1, FDRIVE_RATE_500K, },
- { FDRIVE_DRV_144, 21, 80, 1, FDRIVE_RATE_500K, },
- { FDRIVE_DRV_144, 21, 82, 1, FDRIVE_RATE_500K, },
- { FDRIVE_DRV_144, 21, 83, 1, FDRIVE_RATE_500K, },
- { FDRIVE_DRV_144, 22, 80, 1, FDRIVE_RATE_500K, },
- { FDRIVE_DRV_144, 23, 80, 1, FDRIVE_RATE_500K, },
- { FDRIVE_DRV_144, 24, 80, 1, FDRIVE_RATE_500K, },
+ { FLOPPY_DRIVE_TYPE_144, 18, 80, 1, FDRIVE_RATE_500K, }, /* 3.5" 2880 */
+ { FLOPPY_DRIVE_TYPE_144, 20, 80, 1, FDRIVE_RATE_500K, }, /* 3.5" 3200 */
+ { FLOPPY_DRIVE_TYPE_144, 21, 80, 1, FDRIVE_RATE_500K, },
+ { FLOPPY_DRIVE_TYPE_144, 21, 82, 1, FDRIVE_RATE_500K, },
+ { FLOPPY_DRIVE_TYPE_144, 21, 83, 1, FDRIVE_RATE_500K, },
+ { FLOPPY_DRIVE_TYPE_144, 22, 80, 1, FDRIVE_RATE_500K, },
+ { FLOPPY_DRIVE_TYPE_144, 23, 80, 1, FDRIVE_RATE_500K, },
+ { FLOPPY_DRIVE_TYPE_144, 24, 80, 1, FDRIVE_RATE_500K, },
/* 2.88 MB 3"1/2 floppy disks */
- { FDRIVE_DRV_288, 36, 80, 1, FDRIVE_RATE_1M, },
- { FDRIVE_DRV_288, 39, 80, 1, FDRIVE_RATE_1M, },
- { FDRIVE_DRV_288, 40, 80, 1, FDRIVE_RATE_1M, },
- { FDRIVE_DRV_288, 44, 80, 1, FDRIVE_RATE_1M, },
- { FDRIVE_DRV_288, 48, 80, 1, FDRIVE_RATE_1M, },
+ { FLOPPY_DRIVE_TYPE_288, 36, 80, 1, FDRIVE_RATE_1M, },
+ { FLOPPY_DRIVE_TYPE_288, 39, 80, 1, FDRIVE_RATE_1M, },
+ { FLOPPY_DRIVE_TYPE_288, 40, 80, 1, FDRIVE_RATE_1M, },
+ { FLOPPY_DRIVE_TYPE_288, 44, 80, 1, FDRIVE_RATE_1M, },
+ { FLOPPY_DRIVE_TYPE_288, 48, 80, 1, FDRIVE_RATE_1M, },
/* 720 kB 3"1/2 floppy disks */
- { FDRIVE_DRV_144, 9, 80, 1, FDRIVE_RATE_250K, },
- { FDRIVE_DRV_144, 10, 80, 1, FDRIVE_RATE_250K, },
- { FDRIVE_DRV_144, 10, 82, 1, FDRIVE_RATE_250K, },
- { FDRIVE_DRV_144, 10, 83, 1, FDRIVE_RATE_250K, },
- { FDRIVE_DRV_144, 13, 80, 1, FDRIVE_RATE_250K, },
- { FDRIVE_DRV_144, 14, 80, 1, FDRIVE_RATE_250K, },
+ { FLOPPY_DRIVE_TYPE_144, 9, 80, 1, FDRIVE_RATE_250K, }, /* 3.5" 1440 */
+ { FLOPPY_DRIVE_TYPE_144, 10, 80, 1, FDRIVE_RATE_250K, },
+ { FLOPPY_DRIVE_TYPE_144, 10, 82, 1, FDRIVE_RATE_250K, },
+ { FLOPPY_DRIVE_TYPE_144, 10, 83, 1, FDRIVE_RATE_250K, },
+ { FLOPPY_DRIVE_TYPE_144, 13, 80, 1, FDRIVE_RATE_250K, },
+ { FLOPPY_DRIVE_TYPE_144, 14, 80, 1, FDRIVE_RATE_250K, },
/* 1.2 MB 5"1/4 floppy disks */
- { FDRIVE_DRV_120, 15, 80, 1, FDRIVE_RATE_500K, },
- { FDRIVE_DRV_120, 18, 80, 1, FDRIVE_RATE_500K, },
- { FDRIVE_DRV_120, 18, 82, 1, FDRIVE_RATE_500K, },
- { FDRIVE_DRV_120, 18, 83, 1, FDRIVE_RATE_500K, },
- { FDRIVE_DRV_120, 20, 80, 1, FDRIVE_RATE_500K, },
+ { FLOPPY_DRIVE_TYPE_120, 15, 80, 1, FDRIVE_RATE_500K, },
+ { FLOPPY_DRIVE_TYPE_120, 18, 80, 1, FDRIVE_RATE_500K, }, /* 5.25" 2880 */
+ { FLOPPY_DRIVE_TYPE_120, 18, 82, 1, FDRIVE_RATE_500K, },
+ { FLOPPY_DRIVE_TYPE_120, 18, 83, 1, FDRIVE_RATE_500K, },
+ { FLOPPY_DRIVE_TYPE_120, 20, 80, 1, FDRIVE_RATE_500K, }, /* 5.25" 3200 */
/* 720 kB 5"1/4 floppy disks */
- { FDRIVE_DRV_120, 9, 80, 1, FDRIVE_RATE_250K, },
- { FDRIVE_DRV_120, 11, 80, 1, FDRIVE_RATE_250K, },
+ { FLOPPY_DRIVE_TYPE_120, 9, 80, 1, FDRIVE_RATE_250K, }, /* 5.25" 1440 */
+ { FLOPPY_DRIVE_TYPE_120, 11, 80, 1, FDRIVE_RATE_250K, },
/* 360 kB 5"1/4 floppy disks */
- { FDRIVE_DRV_120, 9, 40, 1, FDRIVE_RATE_300K, },
- { FDRIVE_DRV_120, 9, 40, 0, FDRIVE_RATE_300K, },
- { FDRIVE_DRV_120, 10, 41, 1, FDRIVE_RATE_300K, },
- { FDRIVE_DRV_120, 10, 42, 1, FDRIVE_RATE_300K, },
+ { FLOPPY_DRIVE_TYPE_120, 9, 40, 1, FDRIVE_RATE_300K, }, /* 5.25" 720 */
+ { FLOPPY_DRIVE_TYPE_120, 9, 40, 0, FDRIVE_RATE_300K, },
+ { FLOPPY_DRIVE_TYPE_120, 10, 41, 1, FDRIVE_RATE_300K, },
+ { FLOPPY_DRIVE_TYPE_120, 10, 42, 1, FDRIVE_RATE_300K, },
/* 320 kB 5"1/4 floppy disks */
- { FDRIVE_DRV_120, 8, 40, 1, FDRIVE_RATE_250K, },
- { FDRIVE_DRV_120, 8, 40, 0, FDRIVE_RATE_250K, },
+ { FLOPPY_DRIVE_TYPE_120, 8, 40, 1, FDRIVE_RATE_250K, },
+ { FLOPPY_DRIVE_TYPE_120, 8, 40, 0, FDRIVE_RATE_250K, },
/* 360 kB must match 5"1/4 better than 3"1/2... */
- { FDRIVE_DRV_144, 9, 80, 0, FDRIVE_RATE_250K, },
+ { FLOPPY_DRIVE_TYPE_144, 9, 80, 0, FDRIVE_RATE_250K, }, /* 3.5" 720 */
/* end */
- { FDRIVE_DRV_NONE, -1, -1, 0, 0, },
+ { FLOPPY_DRIVE_TYPE_NONE, -1, -1, 0, 0, },
};
-static void pick_geometry(BlockBackend *blk, int *nb_heads,
- int *max_track, int *last_sect,
- FDriveType drive_in, FDriveType *drive,
- FDriveRate *rate)
+static FDriveSize drive_size(FloppyDriveType drive)
{
- const FDFormat *parse;
- uint64_t nb_sectors, size;
- int i, first_match, match;
-
- blk_get_geometry(blk, &nb_sectors);
- match = -1;
- first_match = -1;
- for (i = 0; ; i++) {
- parse = &fd_formats[i];
- if (parse->drive == FDRIVE_DRV_NONE) {
- break;
- }
- if (drive_in == parse->drive ||
- drive_in == FDRIVE_DRV_NONE) {
- size = (parse->max_head + 1) * parse->max_track *
- parse->last_sect;
- if (nb_sectors == size) {
- match = i;
- break;
- }
- if (first_match == -1) {
- first_match = i;
- }
- }
- }
- if (match == -1) {
- if (first_match == -1) {
- match = 1;
- } else {
- match = first_match;
- }
- parse = &fd_formats[match];
+ switch (drive) {
+ case FLOPPY_DRIVE_TYPE_120:
+ return FDRIVE_SIZE_525;
+ case FLOPPY_DRIVE_TYPE_144:
+ case FLOPPY_DRIVE_TYPE_288:
+ return FDRIVE_SIZE_350;
+ default:
+ return FDRIVE_SIZE_UNKNOWN;
}
- *nb_heads = parse->max_head + 1;
- *max_track = parse->max_track;
- *last_sect = parse->last_sect;
- *drive = parse->drive;
- *rate = parse->rate;
}
#define GET_CUR_DRV(fdctrl) ((fdctrl)->cur_drv)
@@ -178,13 +159,14 @@ typedef struct FDrive {
FDCtrl *fdctrl;
BlockBackend *blk;
/* Drive status */
- FDriveType drive;
+ FloppyDriveType drive; /* CMOS drive type */
uint8_t perpendicular; /* 2.88 MB access mode */
/* Position */
uint8_t head;
uint8_t track;
uint8_t sect;
/* Media */
+ FloppyDriveType disk; /* Current disk type */
FDiskFlags flags;
uint8_t last_sect; /* Nb sector per track */
uint8_t max_track; /* Nb of tracks */
@@ -192,16 +174,38 @@ typedef struct FDrive {
uint8_t ro; /* Is read-only */
uint8_t media_changed; /* Is media changed */
uint8_t media_rate; /* Data rate of medium */
+
+ bool media_validated; /* Have we validated the media? */
} FDrive;
+
+static FloppyDriveType get_fallback_drive_type(FDrive *drv);
+
+/* Hack: FD_SEEK is expected to work on empty drives. However, QEMU
+ * currently goes through some pains to keep seeks within the bounds
+ * established by last_sect and max_track. Correcting this is difficult,
+ * as refactoring FDC code tends to expose nasty bugs in the Linux kernel.
+ *
+ * For now: allow empty drives to have large bounds so we can seek around,
+ * with the understanding that when a diskette is inserted, the bounds will
+ * properly tighten to match the geometry of that inserted medium.
+ */
+static void fd_empty_seek_hack(FDrive *drv)
+{
+ drv->last_sect = 0xFF;
+ drv->max_track = 0xFF;
+}
+
static void fd_init(FDrive *drv)
{
/* Drive */
- drv->drive = FDRIVE_DRV_NONE;
drv->perpendicular = 0;
/* Disk */
+ drv->disk = FLOPPY_DRIVE_TYPE_NONE;
drv->last_sect = 0;
drv->max_track = 0;
+ drv->ro = true;
+ drv->media_changed = 1;
}
#define NUM_SIDES(drv) ((drv)->flags & FDISK_DBL_SIDES ? 2 : 1)
@@ -284,39 +288,149 @@ static void fd_recalibrate(FDrive *drv)
fd_seek(drv, 0, 0, 1, 1);
}
+/**
+ * Determine geometry based on inserted diskette.
+ * Will not operate on an empty drive.
+ *
+ * @return: 0 on success, -1 if the drive is empty.
+ */
+static int pick_geometry(FDrive *drv)
+{
+ BlockBackend *blk = drv->blk;
+ const FDFormat *parse;
+ uint64_t nb_sectors, size;
+ int i;
+ int match, size_match, type_match;
+ bool magic = drv->drive == FLOPPY_DRIVE_TYPE_AUTO;
+
+ /* We can only pick a geometry if we have a diskette. */
+ if (!drv->blk || !blk_is_inserted(drv->blk) ||
+ drv->drive == FLOPPY_DRIVE_TYPE_NONE)
+ {
+ return -1;
+ }
+
+ /* We need to determine the likely geometry of the inserted medium.
+ * In order of preference, we look for:
+ * (1) The same drive type and number of sectors,
+ * (2) The same diskette size and number of sectors,
+ * (3) The same drive type.
+ *
+ * In all cases, matches that occur higher in the drive table will take
+ * precedence over matches that occur later in the table.
+ */
+ blk_get_geometry(blk, &nb_sectors);
+ match = size_match = type_match = -1;
+ for (i = 0; ; i++) {
+ parse = &fd_formats[i];
+ if (parse->drive == FLOPPY_DRIVE_TYPE_NONE) {
+ break;
+ }
+ size = (parse->max_head + 1) * parse->max_track * parse->last_sect;
+ if (nb_sectors == size) {
+ if (magic || parse->drive == drv->drive) {
+ /* (1) perfect match -- nb_sectors and drive type */
+ goto out;
+ } else if (drive_size(parse->drive) == drive_size(drv->drive)) {
+ /* (2) size match -- nb_sectors and physical medium size */
+ match = (match == -1) ? i : match;
+ } else {
+ /* This is suspicious -- Did the user misconfigure? */
+ size_match = (size_match == -1) ? i : size_match;
+ }
+ } else if (type_match == -1) {
+ if ((parse->drive == drv->drive) ||
+ (magic && (parse->drive == get_fallback_drive_type(drv)))) {
+ /* (3) type match -- nb_sectors mismatch, but matches the type
+ * specified explicitly by the user, or matches the fallback
+ * default type when using the drive autodetect mechanism */
+ type_match = i;
+ }
+ }
+ }
+
+ /* No exact match found */
+ if (match == -1) {
+ if (size_match != -1) {
+ parse = &fd_formats[size_match];
+ FLOPPY_DPRINTF("User requested floppy drive type '%s', "
+ "but inserted medium appears to be a "
+ "%"PRId64" sector '%s' type\n",
+ FloppyDriveType_lookup[drv->drive],
+ nb_sectors,
+ FloppyDriveType_lookup[parse->drive]);
+ }
+ match = type_match;
+ }
+
+ /* No match of any kind found -- fd_format is misconfigured, abort. */
+ if (match == -1) {
+ error_setg(&error_abort, "No candidate geometries present in table "
+ " for floppy drive type '%s'",
+ FloppyDriveType_lookup[drv->drive]);
+ }
+
+ parse = &(fd_formats[match]);
+
+ out:
+ if (parse->max_head == 0) {
+ drv->flags &= ~FDISK_DBL_SIDES;
+ } else {
+ drv->flags |= FDISK_DBL_SIDES;
+ }
+ drv->max_track = parse->max_track;
+ drv->last_sect = parse->last_sect;
+ drv->disk = parse->drive;
+ drv->media_rate = parse->rate;
+ return 0;
+}
+
+static void pick_drive_type(FDrive *drv)
+{
+ if (drv->drive != FLOPPY_DRIVE_TYPE_AUTO) {
+ return;
+ }
+
+ if (pick_geometry(drv) == 0) {
+ drv->drive = drv->disk;
+ } else {
+ drv->drive = get_fallback_drive_type(drv);
+ }
+
+ g_assert(drv->drive != FLOPPY_DRIVE_TYPE_AUTO);
+}
+
/* Revalidate a disk drive after a disk change */
static void fd_revalidate(FDrive *drv)
{
- int nb_heads, max_track, last_sect, ro;
- FDriveType drive;
- FDriveRate rate;
+ int rc;
FLOPPY_DPRINTF("revalidate\n");
if (drv->blk != NULL) {
- ro = blk_is_read_only(drv->blk);
- pick_geometry(drv->blk, &nb_heads, &max_track,
- &last_sect, drv->drive, &drive, &rate);
+ drv->ro = blk_is_read_only(drv->blk);
if (!blk_is_inserted(drv->blk)) {
FLOPPY_DPRINTF("No disk in drive\n");
- } else {
- FLOPPY_DPRINTF("Floppy disk (%d h %d t %d s) %s\n", nb_heads,
- max_track, last_sect, ro ? "ro" : "rw");
- }
- if (nb_heads == 1) {
- drv->flags &= ~FDISK_DBL_SIDES;
- } else {
- drv->flags |= FDISK_DBL_SIDES;
+ drv->disk = FLOPPY_DRIVE_TYPE_NONE;
+ fd_empty_seek_hack(drv);
+ } else if (!drv->media_validated) {
+ rc = pick_geometry(drv);
+ if (rc) {
+ FLOPPY_DPRINTF("Could not validate floppy drive media");
+ } else {
+ drv->media_validated = true;
+ FLOPPY_DPRINTF("Floppy disk (%d h %d t %d s) %s\n",
+ (drv->flags & FDISK_DBL_SIDES) ? 2 : 1,
+ drv->max_track, drv->last_sect,
+ drv->ro ? "ro" : "rw");
+ }
}
- drv->max_track = max_track;
- drv->last_sect = last_sect;
- drv->ro = ro;
- drv->drive = drive;
- drv->media_rate = rate;
} else {
FLOPPY_DPRINTF("No drive connected\n");
drv->last_sect = 0;
drv->max_track = 0;
drv->flags &= ~FDISK_DBL_SIDES;
+ drv->drive = FLOPPY_DRIVE_TYPE_NONE;
+ drv->disk = FLOPPY_DRIVE_TYPE_NONE;
}
}
@@ -532,6 +646,7 @@ struct FDCtrl {
QEMUTimer *result_timer;
int dma_chann;
uint8_t phase;
+ IsaDma *dma;
/* Controller's identification */
uint8_t version;
/* HW */
@@ -566,11 +681,17 @@ struct FDCtrl {
FDrive drives[MAX_FD];
int reset_sensei;
uint32_t check_media_rate;
+ FloppyDriveType fallback; /* type=auto failure fallback */
/* Timers state */
uint8_t timer0;
uint8_t timer1;
};
+static FloppyDriveType get_fallback_drive_type(FDrive *drv)
+{
+ return drv->fdctrl->fallback;
+}
+
#define TYPE_SYSBUS_FDC "base-sysbus-fdc"
#define SYSBUS_FDC(obj) OBJECT_CHECK(FDCtrlSysBus, (obj), TYPE_SYSBUS_FDC)
@@ -1311,7 +1432,8 @@ static void fdctrl_stop_transfer(FDCtrl *fdctrl, uint8_t status0,
fdctrl->fifo[6] = FD_SECTOR_SC;
fdctrl->data_dir = FD_DIR_READ;
if (!(fdctrl->msr & FD_MSR_NONDMA)) {
- DMA_release_DREQ(fdctrl->dma_chann);
+ IsaDmaClass *k = ISADMA_GET_CLASS(fdctrl->dma);
+ k->release_DREQ(fdctrl->dma, fdctrl->dma_chann);
}
fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO;
fdctrl->msr &= ~FD_MSR_NONDMA;
@@ -1397,27 +1519,43 @@ static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction)
}
fdctrl->eot = fdctrl->fifo[6];
if (fdctrl->dor & FD_DOR_DMAEN) {
- int dma_mode;
+ IsaDmaTransferMode dma_mode;
+ IsaDmaClass *k = ISADMA_GET_CLASS(fdctrl->dma);
+ bool dma_mode_ok;
/* DMA transfer are enabled. Check if DMA channel is well programmed */
- dma_mode = DMA_get_channel_mode(fdctrl->dma_chann);
- dma_mode = (dma_mode >> 2) & 3;
+ dma_mode = k->get_transfer_mode(fdctrl->dma, fdctrl->dma_chann);
FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n",
dma_mode, direction,
(128 << fdctrl->fifo[5]) *
(cur_drv->last_sect - ks + 1), fdctrl->data_len);
- if (((direction == FD_DIR_SCANE || direction == FD_DIR_SCANL ||
- direction == FD_DIR_SCANH) && dma_mode == 0) ||
- (direction == FD_DIR_WRITE && dma_mode == 2) ||
- (direction == FD_DIR_READ && dma_mode == 1) ||
- (direction == FD_DIR_VERIFY)) {
+ switch (direction) {
+ case FD_DIR_SCANE:
+ case FD_DIR_SCANL:
+ case FD_DIR_SCANH:
+ dma_mode_ok = (dma_mode == ISADMA_TRANSFER_VERIFY);
+ break;
+ case FD_DIR_WRITE:
+ dma_mode_ok = (dma_mode == ISADMA_TRANSFER_WRITE);
+ break;
+ case FD_DIR_READ:
+ dma_mode_ok = (dma_mode == ISADMA_TRANSFER_READ);
+ break;
+ case FD_DIR_VERIFY:
+ dma_mode_ok = true;
+ break;
+ default:
+ dma_mode_ok = false;
+ break;
+ }
+ if (dma_mode_ok) {
/* No access is allowed until DMA transfer has completed */
fdctrl->msr &= ~FD_MSR_RQM;
if (direction != FD_DIR_VERIFY) {
/* Now, we just have to wait for the DMA controller to
* recall us...
*/
- DMA_hold_DREQ(fdctrl->dma_chann);
- DMA_schedule(fdctrl->dma_chann);
+ k->hold_DREQ(fdctrl->dma, fdctrl->dma_chann);
+ k->schedule(fdctrl->dma);
} else {
/* Start transfer */
fdctrl_transfer_handler(fdctrl, fdctrl->dma_chann, 0,
@@ -1456,12 +1594,14 @@ static int fdctrl_transfer_handler (void *opaque, int nchan,
FDrive *cur_drv;
int len, start_pos, rel_pos;
uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00;
+ IsaDmaClass *k;
fdctrl = opaque;
if (fdctrl->msr & FD_MSR_RQM) {
FLOPPY_DPRINTF("Not in DMA transfer mode !\n");
return 0;
}
+ k = ISADMA_GET_CLASS(fdctrl->dma);
cur_drv = get_cur_drv(fdctrl);
if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL ||
fdctrl->data_dir == FD_DIR_SCANH)
@@ -1500,8 +1640,8 @@ static int fdctrl_transfer_handler (void *opaque, int nchan,
switch (fdctrl->data_dir) {
case FD_DIR_READ:
/* READ commands */
- DMA_write_memory (nchan, fdctrl->fifo + rel_pos,
- fdctrl->data_pos, len);
+ k->write_memory(fdctrl->dma, nchan, fdctrl->fifo + rel_pos,
+ fdctrl->data_pos, len);
break;
case FD_DIR_WRITE:
/* WRITE commands */
@@ -1515,8 +1655,8 @@ static int fdctrl_transfer_handler (void *opaque, int nchan,
goto transfer_error;
}
- DMA_read_memory (nchan, fdctrl->fifo + rel_pos,
- fdctrl->data_pos, len);
+ k->read_memory(fdctrl->dma, nchan, fdctrl->fifo + rel_pos,
+ fdctrl->data_pos, len);
if (blk_write(cur_drv->blk, fd_sector(cur_drv),
fdctrl->fifo, 1) < 0) {
FLOPPY_DPRINTF("error writing sector %d\n",
@@ -1533,7 +1673,8 @@ static int fdctrl_transfer_handler (void *opaque, int nchan,
{
uint8_t tmpbuf[FD_SECTOR_LEN];
int ret;
- DMA_read_memory (nchan, tmpbuf, fdctrl->data_pos, len);
+ k->read_memory(fdctrl->dma, nchan, tmpbuf, fdctrl->data_pos,
+ len);
ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len);
if (ret == 0) {
status2 = FD_SR2_SEH;
@@ -1798,8 +1939,8 @@ static void fdctrl_handle_readid(FDCtrl *fdctrl, int direction)
FDrive *cur_drv = get_cur_drv(fdctrl);
cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
- timer_mod(fdctrl->result_timer,
- qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (get_ticks_per_sec() / 50));
+ timer_mod(fdctrl->result_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ (NANOSECONDS_PER_SECOND / 50));
}
static void fdctrl_handle_format_track(FDCtrl *fdctrl, int direction)
@@ -2185,6 +2326,7 @@ static void fdctrl_change_cb(void *opaque, bool load)
FDrive *drive = opaque;
drive->media_changed = 1;
+ drive->media_validated = false;
fd_revalidate(drive);
}
@@ -2214,10 +2356,11 @@ static void fdctrl_connect_drives(FDCtrl *fdctrl, Error **errp)
}
fd_init(drive);
- fdctrl_change_cb(drive, 0);
if (drive->blk) {
blk_set_dev_ops(drive->blk, &fdctrl_block_ops, drive);
+ pick_drive_type(drive);
}
+ fd_revalidate(drive);
}
}
@@ -2233,10 +2376,12 @@ ISADevice *fdctrl_init_isa(ISABus *bus, DriveInfo **fds)
dev = DEVICE(isadev);
if (fds[0]) {
- qdev_prop_set_drive_nofail(dev, "driveA", blk_by_legacy_dinfo(fds[0]));
+ qdev_prop_set_drive(dev, "driveA", blk_by_legacy_dinfo(fds[0]),
+ &error_fatal);
}
if (fds[1]) {
- qdev_prop_set_drive_nofail(dev, "driveB", blk_by_legacy_dinfo(fds[1]));
+ qdev_prop_set_drive(dev, "driveB", blk_by_legacy_dinfo(fds[1]),
+ &error_fatal);
}
qdev_init_nofail(dev);
@@ -2256,10 +2401,12 @@ void fdctrl_init_sysbus(qemu_irq irq, int dma_chann,
fdctrl = &sys->state;
fdctrl->dma_chann = dma_chann; /* FIXME */
if (fds[0]) {
- qdev_prop_set_drive_nofail(dev, "driveA", blk_by_legacy_dinfo(fds[0]));
+ qdev_prop_set_drive(dev, "driveA", blk_by_legacy_dinfo(fds[0]),
+ &error_fatal);
}
if (fds[1]) {
- qdev_prop_set_drive_nofail(dev, "driveB", blk_by_legacy_dinfo(fds[1]));
+ qdev_prop_set_drive(dev, "driveB", blk_by_legacy_dinfo(fds[1]),
+ &error_fatal);
}
qdev_init_nofail(dev);
sbd = SYS_BUS_DEVICE(dev);
@@ -2275,7 +2422,8 @@ void sun4m_fdctrl_init(qemu_irq irq, hwaddr io_base,
dev = qdev_create(NULL, "SUNW,fdtwo");
if (fds[0]) {
- qdev_prop_set_drive_nofail(dev, "drive", blk_by_legacy_dinfo(fds[0]));
+ qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(fds[0]),
+ &error_fatal);
}
qdev_init_nofail(dev);
sys = SYSBUS_FDC(dev);
@@ -2289,6 +2437,10 @@ static void fdctrl_realize_common(FDCtrl *fdctrl, Error **errp)
int i, j;
static int command_tables_inited = 0;
+ if (fdctrl->fallback == FLOPPY_DRIVE_TYPE_AUTO) {
+ error_setg(errp, "Cannot choose a fallback FDrive type of 'auto'");
+ }
+
/* Fill 'command_to_handler' lookup table */
if (!command_tables_inited) {
command_tables_inited = 1;
@@ -2312,7 +2464,11 @@ static void fdctrl_realize_common(FDCtrl *fdctrl, Error **errp)
fdctrl->num_floppies = MAX_FD;
if (fdctrl->dma_chann != -1) {
- DMA_register_channel(fdctrl->dma_chann, &fdctrl_transfer_handler, fdctrl);
+ IsaDmaClass *k;
+ assert(fdctrl->dma);
+ k = ISADMA_GET_CLASS(fdctrl->dma);
+ k->register_channel(fdctrl->dma, fdctrl->dma_chann,
+ &fdctrl_transfer_handler, fdctrl);
}
fdctrl_connect_drives(fdctrl, errp);
}
@@ -2335,6 +2491,10 @@ static void isabus_fdc_realize(DeviceState *dev, Error **errp)
isa_init_irq(isadev, &fdctrl->irq, isa->irq);
fdctrl->dma_chann = isa->dma;
+ if (fdctrl->dma_chann != -1) {
+ fdctrl->dma = isa_get_dma(isa_bus_from_device(isadev), isa->dma);
+ assert(fdctrl->dma);
+ }
qdev_set_legacy_instance_id(dev, isa->iobase, 2);
fdctrl_realize_common(fdctrl, &err);
@@ -2363,6 +2523,8 @@ static void sun4m_fdc_initfn(Object *obj)
FDCtrlSysBus *sys = SYSBUS_FDC(obj);
FDCtrl *fdctrl = &sys->state;
+ fdctrl->dma_chann = -1;
+
memory_region_init_io(&fdctrl->iomem, obj, &fdctrl_mem_strict_ops,
fdctrl, "fdctrl", 0x08);
sysbus_init_mmio(sbd, &fdctrl->iomem);
@@ -2389,13 +2551,36 @@ static void sysbus_fdc_common_realize(DeviceState *dev, Error **errp)
fdctrl_realize_common(fdctrl, errp);
}
-FDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i)
+FloppyDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i)
{
FDCtrlISABus *isa = ISA_FDC(fdc);
return isa->state.drives[i].drive;
}
+void isa_fdc_get_drive_max_chs(FloppyDriveType type,
+ uint8_t *maxc, uint8_t *maxh, uint8_t *maxs)
+{
+ const FDFormat *fdf;
+
+ *maxc = *maxh = *maxs = 0;
+ for (fdf = fd_formats; fdf->drive != FLOPPY_DRIVE_TYPE_NONE; fdf++) {
+ if (fdf->drive != type) {
+ continue;
+ }
+ if (*maxc < fdf->max_track) {
+ *maxc = fdf->max_track;
+ }
+ if (*maxh < fdf->max_head) {
+ *maxh = fdf->max_head;
+ }
+ if (*maxs < fdf->last_sect) {
+ *maxs = fdf->last_sect;
+ }
+ }
+ (*maxc)--;
+}
+
static const VMStateDescription vmstate_isa_fdc ={
.name = "fdc",
.version_id = 2,
@@ -2414,6 +2599,15 @@ static Property isa_fdc_properties[] = {
DEFINE_PROP_DRIVE("driveB", FDCtrlISABus, state.drives[1].blk),
DEFINE_PROP_BIT("check_media_rate", FDCtrlISABus, state.check_media_rate,
0, true),
+ DEFINE_PROP_DEFAULT("fdtypeA", FDCtrlISABus, state.drives[0].drive,
+ FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type,
+ FloppyDriveType),
+ DEFINE_PROP_DEFAULT("fdtypeB", FDCtrlISABus, state.drives[1].drive,
+ FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type,
+ FloppyDriveType),
+ DEFINE_PROP_DEFAULT("fallback", FDCtrlISABus, state.fallback,
+ FLOPPY_DRIVE_TYPE_288, qdev_prop_fdc_drive_type,
+ FloppyDriveType),
DEFINE_PROP_END_OF_LIST(),
};
@@ -2462,6 +2656,15 @@ static const VMStateDescription vmstate_sysbus_fdc ={
static Property sysbus_fdc_properties[] = {
DEFINE_PROP_DRIVE("driveA", FDCtrlSysBus, state.drives[0].blk),
DEFINE_PROP_DRIVE("driveB", FDCtrlSysBus, state.drives[1].blk),
+ DEFINE_PROP_DEFAULT("fdtypeA", FDCtrlSysBus, state.drives[0].drive,
+ FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type,
+ FloppyDriveType),
+ DEFINE_PROP_DEFAULT("fdtypeB", FDCtrlSysBus, state.drives[1].drive,
+ FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type,
+ FloppyDriveType),
+ DEFINE_PROP_DEFAULT("fallback", FDCtrlISABus, state.fallback,
+ FLOPPY_DRIVE_TYPE_144, qdev_prop_fdc_drive_type,
+ FloppyDriveType),
DEFINE_PROP_END_OF_LIST(),
};
@@ -2482,6 +2685,12 @@ static const TypeInfo sysbus_fdc_info = {
static Property sun4m_fdc_properties[] = {
DEFINE_PROP_DRIVE("drive", FDCtrlSysBus, state.drives[0].blk),
+ DEFINE_PROP_DEFAULT("fdtype", FDCtrlSysBus, state.drives[0].drive,
+ FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type,
+ FloppyDriveType),
+ DEFINE_PROP_DEFAULT("fallback", FDCtrlISABus, state.fallback,
+ FLOPPY_DRIVE_TYPE_144, qdev_prop_fdc_drive_type,
+ FloppyDriveType),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/qemu/hw/block/hd-geometry.c b/qemu/hw/block/hd-geometry.c
index b187878fa..6d02192db 100644
--- a/qemu/hw/block/hd-geometry.c
+++ b/qemu/hw/block/hd-geometry.c
@@ -30,6 +30,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "sysemu/block-backend.h"
#include "hw/block/block.h"
#include "trace.h"
diff --git a/qemu/hw/block/m25p80.c b/qemu/hw/block/m25p80.c
index efc43dde6..906b71257 100644
--- a/qemu/hw/block/m25p80.c
+++ b/qemu/hw/block/m25p80.c
@@ -21,10 +21,12 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
-#include "hw/ssi.h"
+#include "hw/ssi/ssi.h"
+#include "qemu/bitops.h"
#ifndef M25P80_ERR_DEBUG
#define M25P80_ERR_DEBUG 0
@@ -45,7 +47,10 @@
/* set to allow the page program command to write 0s back to 1. Useful for
* modelling EEPROM with SPI flash command set
*/
-#define WR_1 0x100
+#define EEPROM 0x100
+
+/* 16 MiB max in 3 byte address mode */
+#define MAX_3BYTES_SIZE 0x1000000
typedef struct FlashPartInfo {
const char *part_name;
@@ -60,7 +65,7 @@ typedef struct FlashPartInfo {
uint32_t sector_size;
uint32_t n_sectors;
uint32_t page_size;
- uint8_t flags;
+ uint16_t flags;
} FlashPartInfo;
/* adapted from linux */
@@ -78,6 +83,30 @@ typedef struct FlashPartInfo {
#define JEDEC_WINBOND 0xEF
#define JEDEC_SPANSION 0x01
+/* Numonyx (Micron) Configuration register macros */
+#define VCFG_DUMMY 0x1
+#define VCFG_WRAP_SEQUENTIAL 0x2
+#define NVCFG_XIP_MODE_DISABLED (7 << 9)
+#define NVCFG_XIP_MODE_MASK (7 << 9)
+#define VCFG_XIP_MODE_ENABLED (1 << 3)
+#define CFG_DUMMY_CLK_LEN 4
+#define NVCFG_DUMMY_CLK_POS 12
+#define VCFG_DUMMY_CLK_POS 4
+#define EVCFG_OUT_DRIVER_STRENGHT_DEF 7
+#define EVCFG_VPP_ACCELERATOR (1 << 3)
+#define EVCFG_RESET_HOLD_ENABLED (1 << 4)
+#define NVCFG_DUAL_IO_MASK (1 << 2)
+#define EVCFG_DUAL_IO_ENABLED (1 << 6)
+#define NVCFG_QUAD_IO_MASK (1 << 3)
+#define EVCFG_QUAD_IO_ENABLED (1 << 7)
+#define NVCFG_4BYTE_ADDR_MASK (1 << 0)
+#define NVCFG_LOWER_SEGMENT_MASK (1 << 1)
+#define CFG_UPPER_128MB_SEG_ENABLED 0x3
+
+/* Numonyx (Micron) Flag Status Register macros */
+#define FSR_4BYTE_ADDR_MODE_ENABLED 0x1
+#define FSR_FLASH_READY (1 << 7)
+
static const FlashPartInfo known_devices[] = {
/* Atmel -- some are (confusingly) marketed as "DataFlash" */
{ INFO("at25fs010", 0x1f6601, 0, 32 << 10, 4, ER_4K) },
@@ -94,6 +123,12 @@ static const FlashPartInfo known_devices[] = {
{ INFO("at45db081d", 0x1f2500, 0, 64 << 10, 16, ER_4K) },
+ /* Atmel EEPROMS - it is assumed, that don't care bit in command
+ * is set to 0. Block protection is not supported.
+ */
+ { INFO("at25128a-nonjedec", 0x0, 0, 1, 131072, EEPROM) },
+ { INFO("at25256a-nonjedec", 0x0, 0, 1, 262144, EEPROM) },
+
/* EON -- en25xxx */
{ INFO("en25f32", 0x1c3116, 0, 64 << 10, 64, ER_4K) },
{ INFO("en25p32", 0x1c2016, 0, 64 << 10, 64, 0) },
@@ -163,6 +198,7 @@ static const FlashPartInfo known_devices[] = {
{ INFO("sst25wf010", 0xbf2502, 0, 64 << 10, 2, ER_4K) },
{ INFO("sst25wf020", 0xbf2503, 0, 64 << 10, 4, ER_4K) },
{ INFO("sst25wf040", 0xbf2504, 0, 64 << 10, 8, ER_4K) },
+ { INFO("sst25wf080", 0xbf2505, 0, 64 << 10, 16, ER_4K) },
/* ST Microelectronics -- newer production may have feature updates */
{ INFO("m25p05", 0x202010, 0, 32 << 10, 2, 0) },
@@ -204,8 +240,9 @@ static const FlashPartInfo known_devices[] = {
{ INFO("w25q80bl", 0xef4014, 0, 64 << 10, 16, ER_4K) },
{ INFO("w25q256", 0xef4019, 0, 64 << 10, 512, ER_4K) },
- /* Numonyx -- n25q128 */
{ INFO("n25q128", 0x20ba18, 0, 64 << 10, 256, 0) },
+ { INFO("n25q256a", 0x20ba19, 0, 64 << 10, 512, ER_4K) },
+ { INFO("n25q512a", 0x20ba20, 0, 64 << 10, 1024, ER_4K) },
};
typedef enum {
@@ -216,21 +253,49 @@ typedef enum {
WREN = 0x6,
JEDEC_READ = 0x9f,
BULK_ERASE = 0xc7,
+ READ_FSR = 0x70,
- READ = 0x3,
- FAST_READ = 0xb,
+ READ = 0x03,
+ READ4 = 0x13,
+ FAST_READ = 0x0b,
+ FAST_READ4 = 0x0c,
DOR = 0x3b,
+ DOR4 = 0x3c,
QOR = 0x6b,
+ QOR4 = 0x6c,
DIOR = 0xbb,
+ DIOR4 = 0xbc,
QIOR = 0xeb,
+ QIOR4 = 0xec,
- PP = 0x2,
+ PP = 0x02,
+ PP4 = 0x12,
DPP = 0xa2,
QPP = 0x32,
ERASE_4K = 0x20,
+ ERASE4_4K = 0x21,
ERASE_32K = 0x52,
ERASE_SECTOR = 0xd8,
+ ERASE4_SECTOR = 0xdc,
+
+ EN_4BYTE_ADDR = 0xB7,
+ EX_4BYTE_ADDR = 0xE9,
+
+ EXTEND_ADDR_READ = 0xC8,
+ EXTEND_ADDR_WRITE = 0xC5,
+
+ RESET_ENABLE = 0x66,
+ RESET_MEMORY = 0x99,
+
+ RNVCR = 0xB5,
+ WNVCR = 0xB1,
+
+ RVCR = 0x85,
+ WVCR = 0x81,
+
+ REVCR = 0x65,
+ WEVCR = 0x61,
} FlashCMD;
typedef enum {
@@ -244,8 +309,6 @@ typedef enum {
typedef struct Flash {
SSISlave parent_obj;
- uint32_t r;
-
BlockBackend *blk;
uint8_t *storage;
@@ -259,7 +322,13 @@ typedef struct Flash {
uint8_t needed_bytes;
uint8_t cmd_in_progress;
uint64_t cur_addr;
+ uint32_t nonvolatile_cfg;
+ uint32_t volatile_cfg;
+ uint32_t enh_volatile_cfg;
bool write_enable;
+ bool four_bytes_address_mode;
+ bool reset_enable;
+ uint8_t ear;
int64_t dirty_page;
@@ -331,6 +400,7 @@ static void flash_erase(Flash *s, int offset, FlashCMD cmd)
switch (cmd) {
case ERASE_4K:
+ case ERASE4_4K:
len = 4 << 10;
capa_to_assert = ER_4K;
break;
@@ -339,6 +409,7 @@ static void flash_erase(Flash *s, int offset, FlashCMD cmd)
capa_to_assert = ER_32K;
break;
case ERASE_SECTOR:
+ case ERASE4_SECTOR:
len = s->pi->sector_size;
break;
case BULK_ERASE:
@@ -385,7 +456,7 @@ void flash_write8(Flash *s, uint64_t addr, uint8_t data)
" -> %" PRIx8 "\n", addr, prev, data);
}
- if (s->pi->flags & WR_1) {
+ if (s->pi->flags & EEPROM) {
s->storage[s->cur_addr] = data;
} else {
s->storage[s->cur_addr] &= data;
@@ -395,11 +466,43 @@ void flash_write8(Flash *s, uint64_t addr, uint8_t data)
s->dirty_page = page;
}
+static inline int get_addr_length(Flash *s)
+{
+ /* check if eeprom is in use */
+ if (s->pi->flags == EEPROM) {
+ return 2;
+ }
+
+ switch (s->cmd_in_progress) {
+ case PP4:
+ case READ4:
+ case QIOR4:
+ case ERASE4_4K:
+ case ERASE4_SECTOR:
+ case FAST_READ4:
+ case DOR4:
+ case QOR4:
+ case DIOR4:
+ return 4;
+ default:
+ return s->four_bytes_address_mode ? 4 : 3;
+ }
+}
+
static void complete_collecting_data(Flash *s)
{
- s->cur_addr = s->data[0] << 16;
- s->cur_addr |= s->data[1] << 8;
- s->cur_addr |= s->data[2];
+ int i;
+
+ s->cur_addr = 0;
+
+ for (i = 0; i < get_addr_length(s); ++i) {
+ s->cur_addr <<= 8;
+ s->cur_addr |= s->data[i];
+ }
+
+ if (get_addr_length(s) == 3) {
+ s->cur_addr += (s->ear & 0x3) * MAX_3BYTES_SIZE;
+ }
s->state = STATE_IDLE;
@@ -407,19 +510,28 @@ static void complete_collecting_data(Flash *s)
case DPP:
case QPP:
case PP:
+ case PP4:
s->state = STATE_PAGE_PROGRAM;
break;
case READ:
+ case READ4:
case FAST_READ:
+ case FAST_READ4:
case DOR:
+ case DOR4:
case QOR:
+ case QOR4:
case DIOR:
+ case DIOR4:
case QIOR:
+ case QIOR4:
s->state = STATE_READ;
break;
case ERASE_4K:
+ case ERASE4_4K:
case ERASE_32K:
case ERASE_SECTOR:
+ case ERASE4_SECTOR:
flash_erase(s, s->cur_addr, s->cmd_in_progress);
break;
case WRSR:
@@ -427,49 +539,128 @@ static void complete_collecting_data(Flash *s)
s->write_enable = false;
}
break;
+ case EXTEND_ADDR_WRITE:
+ s->ear = s->data[0];
+ break;
+ case WNVCR:
+ s->nonvolatile_cfg = s->data[0] | (s->data[1] << 8);
+ break;
+ case WVCR:
+ s->volatile_cfg = s->data[0];
+ break;
+ case WEVCR:
+ s->enh_volatile_cfg = s->data[0];
+ break;
default:
break;
}
}
+static void reset_memory(Flash *s)
+{
+ s->cmd_in_progress = NOP;
+ s->cur_addr = 0;
+ s->ear = 0;
+ s->four_bytes_address_mode = false;
+ s->len = 0;
+ s->needed_bytes = 0;
+ s->pos = 0;
+ s->state = STATE_IDLE;
+ s->write_enable = false;
+ s->reset_enable = false;
+
+ if (((s->pi->jedec >> 16) & 0xFF) == JEDEC_NUMONYX) {
+ s->volatile_cfg = 0;
+ s->volatile_cfg |= VCFG_DUMMY;
+ s->volatile_cfg |= VCFG_WRAP_SEQUENTIAL;
+ if ((s->nonvolatile_cfg & NVCFG_XIP_MODE_MASK)
+ != NVCFG_XIP_MODE_DISABLED) {
+ s->volatile_cfg |= VCFG_XIP_MODE_ENABLED;
+ }
+ s->volatile_cfg |= deposit32(s->volatile_cfg,
+ VCFG_DUMMY_CLK_POS,
+ CFG_DUMMY_CLK_LEN,
+ extract32(s->nonvolatile_cfg,
+ NVCFG_DUMMY_CLK_POS,
+ CFG_DUMMY_CLK_LEN)
+ );
+
+ s->enh_volatile_cfg = 0;
+ s->enh_volatile_cfg |= EVCFG_OUT_DRIVER_STRENGHT_DEF;
+ s->enh_volatile_cfg |= EVCFG_VPP_ACCELERATOR;
+ s->enh_volatile_cfg |= EVCFG_RESET_HOLD_ENABLED;
+ if (s->nonvolatile_cfg & NVCFG_DUAL_IO_MASK) {
+ s->enh_volatile_cfg |= EVCFG_DUAL_IO_ENABLED;
+ }
+ if (s->nonvolatile_cfg & NVCFG_QUAD_IO_MASK) {
+ s->enh_volatile_cfg |= EVCFG_QUAD_IO_ENABLED;
+ }
+ if (!(s->nonvolatile_cfg & NVCFG_4BYTE_ADDR_MASK)) {
+ s->four_bytes_address_mode = true;
+ }
+ if (!(s->nonvolatile_cfg & NVCFG_LOWER_SEGMENT_MASK)) {
+ s->ear = CFG_UPPER_128MB_SEG_ENABLED;
+ }
+ }
+
+ DB_PRINT_L(0, "Reset done.\n");
+}
+
static void decode_new_cmd(Flash *s, uint32_t value)
{
s->cmd_in_progress = value;
DB_PRINT_L(0, "decoded new command:%x\n", value);
+ if (value != RESET_MEMORY) {
+ s->reset_enable = false;
+ }
+
switch (value) {
case ERASE_4K:
+ case ERASE4_4K:
case ERASE_32K:
case ERASE_SECTOR:
+ case ERASE4_SECTOR:
case READ:
+ case READ4:
case DPP:
case QPP:
case PP:
- s->needed_bytes = 3;
+ case PP4:
+ s->needed_bytes = get_addr_length(s);
s->pos = 0;
s->len = 0;
s->state = STATE_COLLECTING_DATA;
break;
case FAST_READ:
+ case FAST_READ4:
case DOR:
+ case DOR4:
case QOR:
- s->needed_bytes = 4;
+ case QOR4:
+ s->needed_bytes = get_addr_length(s);
+ if (((s->pi->jedec >> 16) & 0xFF) == JEDEC_NUMONYX) {
+ /* Dummy cycles modeled with bytes writes instead of bits */
+ s->needed_bytes += extract32(s->volatile_cfg, 4, 4);
+ }
s->pos = 0;
s->len = 0;
s->state = STATE_COLLECTING_DATA;
break;
case DIOR:
+ case DIOR4:
switch ((s->pi->jedec >> 16) & 0xFF) {
case JEDEC_WINBOND:
case JEDEC_SPANSION:
s->needed_bytes = 4;
break;
- case JEDEC_NUMONYX:
default:
- s->needed_bytes = 5;
+ s->needed_bytes = get_addr_length(s);
+ /* Dummy cycles modeled with bytes writes instead of bits */
+ s->needed_bytes += extract32(s->volatile_cfg, 4, 4);
}
s->pos = 0;
s->len = 0;
@@ -477,14 +668,16 @@ static void decode_new_cmd(Flash *s, uint32_t value)
break;
case QIOR:
+ case QIOR4:
switch ((s->pi->jedec >> 16) & 0xFF) {
case JEDEC_WINBOND:
case JEDEC_SPANSION:
s->needed_bytes = 6;
break;
- case JEDEC_NUMONYX:
default:
- s->needed_bytes = 8;
+ s->needed_bytes = get_addr_length(s);
+ /* Dummy cycles modeled with bytes writes instead of bits */
+ s->needed_bytes += extract32(s->volatile_cfg, 4, 4);
}
s->pos = 0;
s->len = 0;
@@ -514,6 +707,16 @@ static void decode_new_cmd(Flash *s, uint32_t value)
s->state = STATE_READING_DATA;
break;
+ case READ_FSR:
+ s->data[0] = FSR_FLASH_READY;
+ if (s->four_bytes_address_mode) {
+ s->data[0] |= FSR_4BYTE_ADDR_MODE_ENABLED;
+ }
+ s->pos = 0;
+ s->len = 1;
+ s->state = STATE_READING_DATA;
+ break;
+
case JEDEC_READ:
DB_PRINT_L(0, "populated jedec code\n");
s->data[0] = (s->pi->jedec >> 16) & 0xff;
@@ -541,6 +744,77 @@ static void decode_new_cmd(Flash *s, uint32_t value)
break;
case NOP:
break;
+ case EN_4BYTE_ADDR:
+ s->four_bytes_address_mode = true;
+ break;
+ case EX_4BYTE_ADDR:
+ s->four_bytes_address_mode = false;
+ break;
+ case EXTEND_ADDR_READ:
+ s->data[0] = s->ear;
+ s->pos = 0;
+ s->len = 1;
+ s->state = STATE_READING_DATA;
+ break;
+ case EXTEND_ADDR_WRITE:
+ if (s->write_enable) {
+ s->needed_bytes = 1;
+ s->pos = 0;
+ s->len = 0;
+ s->state = STATE_COLLECTING_DATA;
+ }
+ break;
+ case RNVCR:
+ s->data[0] = s->nonvolatile_cfg & 0xFF;
+ s->data[1] = (s->nonvolatile_cfg >> 8) & 0xFF;
+ s->pos = 0;
+ s->len = 2;
+ s->state = STATE_READING_DATA;
+ break;
+ case WNVCR:
+ if (s->write_enable) {
+ s->needed_bytes = 2;
+ s->pos = 0;
+ s->len = 0;
+ s->state = STATE_COLLECTING_DATA;
+ }
+ break;
+ case RVCR:
+ s->data[0] = s->volatile_cfg & 0xFF;
+ s->pos = 0;
+ s->len = 1;
+ s->state = STATE_READING_DATA;
+ break;
+ case WVCR:
+ if (s->write_enable) {
+ s->needed_bytes = 1;
+ s->pos = 0;
+ s->len = 0;
+ s->state = STATE_COLLECTING_DATA;
+ }
+ break;
+ case REVCR:
+ s->data[0] = s->enh_volatile_cfg & 0xFF;
+ s->pos = 0;
+ s->len = 1;
+ s->state = STATE_READING_DATA;
+ break;
+ case WEVCR:
+ if (s->write_enable) {
+ s->needed_bytes = 1;
+ s->pos = 0;
+ s->len = 0;
+ s->state = STATE_COLLECTING_DATA;
+ }
+ break;
+ case RESET_ENABLE:
+ s->reset_enable = true;
+ break;
+ case RESET_MEMORY:
+ if (s->reset_enable) {
+ reset_memory(s);
+ }
+ break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Unknown cmd %x\n", value);
break;
@@ -647,14 +921,26 @@ static int m25p80_init(SSISlave *ss)
return 0;
}
+static void m25p80_reset(DeviceState *d)
+{
+ Flash *s = M25P80(d);
+
+ reset_memory(s);
+}
+
static void m25p80_pre_save(void *opaque)
{
flash_sync_dirty((Flash *)opaque, -1);
}
+static Property m25p80_properties[] = {
+ DEFINE_PROP_UINT32("nonvolatile-cfg", Flash, nonvolatile_cfg, 0x8FFF),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static const VMStateDescription vmstate_m25p80 = {
.name = "xilinx_spi",
- .version_id = 1,
+ .version_id = 2,
.minimum_version_id = 1,
.pre_save = m25p80_pre_save,
.fields = (VMStateField[]) {
@@ -666,6 +952,12 @@ static const VMStateDescription vmstate_m25p80 = {
VMSTATE_UINT8(cmd_in_progress, Flash),
VMSTATE_UINT64(cur_addr, Flash),
VMSTATE_BOOL(write_enable, Flash),
+ VMSTATE_BOOL_V(reset_enable, Flash, 2),
+ VMSTATE_UINT8_V(ear, Flash, 2),
+ VMSTATE_BOOL_V(four_bytes_address_mode, Flash, 2),
+ VMSTATE_UINT32_V(nonvolatile_cfg, Flash, 2),
+ VMSTATE_UINT32_V(volatile_cfg, Flash, 2),
+ VMSTATE_UINT32_V(enh_volatile_cfg, Flash, 2),
VMSTATE_END_OF_LIST()
}
};
@@ -681,6 +973,8 @@ static void m25p80_class_init(ObjectClass *klass, void *data)
k->set_cs = m25p80_cs;
k->cs_polarity = SSI_CS_LOW;
dc->vmsd = &vmstate_m25p80;
+ dc->props = m25p80_properties;
+ dc->reset = m25p80_reset;
mc->pi = data;
}
diff --git a/qemu/hw/block/nand.c b/qemu/hw/block/nand.c
index 61d2cec03..29c659681 100644
--- a/qemu/hw/block/nand.c
+++ b/qemu/hw/block/nand.c
@@ -18,10 +18,12 @@
#ifndef NAND_IO
-# include "hw/hw.h"
-# include "hw/block/flash.h"
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "hw/block/flash.h"
#include "sysemu/block-backend.h"
#include "hw/qdev.h"
+#include "qapi/error.h"
#include "qemu/error-report.h"
# define NAND_CMD_READ0 0x00
@@ -522,8 +524,8 @@ void nand_setio(DeviceState *dev, uint32_t value)
if (s->ale) {
unsigned int shift = s->addrlen * 8;
- unsigned int mask = ~(0xff << shift);
- unsigned int v = value << shift;
+ uint64_t mask = ~(0xffull << shift);
+ uint64_t v = (uint64_t)value << shift;
s->addr = (s->addr & mask) | v;
s->addrlen ++;
@@ -635,7 +637,7 @@ DeviceState *nand_init(BlockBackend *blk, int manf_id, int chip_id)
qdev_prop_set_uint8(dev, "manufacturer_id", manf_id);
qdev_prop_set_uint8(dev, "chip_id", chip_id);
if (blk) {
- qdev_prop_set_drive_nofail(dev, "drive", blk);
+ qdev_prop_set_drive(dev, "drive", blk, &error_fatal);
}
qdev_init_nofail(dev);
@@ -712,7 +714,7 @@ static void glue(nand_blk_erase_, PAGE_SIZE)(NANDFlashState *s)
memset(s->storage + (PAGE(addr) << OOB_SHIFT),
0xff, OOB_SIZE << s->erase_shift);
i = SECTOR(addr);
- page = SECTOR(addr + (ADDR_SHIFT + s->erase_shift));
+ page = SECTOR(addr + (1 << (ADDR_SHIFT + s->erase_shift)));
for (; i < page; i ++)
if (blk_write(s->blk, i, iobuf, 1) < 0) {
printf("%s: write error in sector %" PRIu64 "\n", __func__, i);
diff --git a/qemu/hw/block/nvme.c b/qemu/hw/block/nvme.c
index 40d488032..173988ee8 100644
--- a/qemu/hw/block/nvme.c
+++ b/qemu/hw/block/nvme.c
@@ -20,11 +20,13 @@
* -device nvme,drive=<drive_id>,serial=<serial>,id=<id[optional]>
*/
+#include "qemu/osdep.h"
#include <hw/block/block.h>
#include <hw/hw.h>
#include <hw/pci/msix.h>
#include <hw/pci/pci.h>
#include "sysemu/sysemu.h"
+#include "qapi/error.h"
#include "qapi/visitor.h"
#include "sysemu/block-backend.h"
@@ -201,10 +203,11 @@ static void nvme_rw_cb(void *opaque, int ret)
NvmeCtrl *n = sq->ctrl;
NvmeCQueue *cq = n->cq[sq->cqid];
- block_acct_done(blk_get_stats(n->conf.blk), &req->acct);
if (!ret) {
+ block_acct_done(blk_get_stats(n->conf.blk), &req->acct);
req->status = NVME_SUCCESS;
} else {
+ block_acct_failed(blk_get_stats(n->conf.blk), &req->acct);
req->status = NVME_INTERNAL_DEV_ERROR;
}
if (req->has_sg) {
@@ -238,18 +241,22 @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
uint64_t data_size = (uint64_t)nlb << data_shift;
uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS);
int is_write = rw->opcode == NVME_CMD_WRITE ? 1 : 0;
+ enum BlockAcctType acct = is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ;
if ((slba + nlb) > ns->id_ns.nsze) {
+ block_acct_invalid(blk_get_stats(n->conf.blk), acct);
return NVME_LBA_RANGE | NVME_DNR;
}
+
if (nvme_map_prp(&req->qsg, prp1, prp2, data_size, n)) {
+ block_acct_invalid(blk_get_stats(n->conf.blk), acct);
return NVME_INVALID_FIELD | NVME_DNR;
}
+
assert((nlb << data_shift) == req->qsg.size);
req->has_sg = true;
- dma_acct_start(n->conf.blk, &req->acct, &req->qsg,
- is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ);
+ dma_acct_start(n->conf.blk, &req->acct, &req->qsg, acct);
req->aiocb = is_write ?
dma_blk_write(n->conf.blk, &req->qsg, aio_slba, nvme_rw_cb, req) :
dma_blk_read(n->conf.blk, &req->qsg, aio_slba, nvme_rw_cb, req);
@@ -805,7 +812,7 @@ static int nvme_init(PCIDevice *pci_dev)
n->num_namespaces = 1;
n->num_queues = 64;
- n->reg_size = 1 << qemu_fls(0x1004 + 2 * (n->num_queues + 1) * 4);
+ n->reg_size = pow2ceil(0x1004 + 2 * (n->num_queues + 1) * 4);
n->ns_size = bs_size / (uint64_t)n->num_namespaces;
n->namespaces = g_new0(NvmeNamespace, n->num_namespaces);
@@ -910,45 +917,13 @@ static void nvme_class_init(ObjectClass *oc, void *data)
dc->vmsd = &nvme_vmstate;
}
-static void nvme_get_bootindex(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
-{
- NvmeCtrl *s = NVME(obj);
-
- visit_type_int32(v, &s->conf.bootindex, name, errp);
-}
-
-static void nvme_set_bootindex(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void nvme_instance_init(Object *obj)
{
NvmeCtrl *s = NVME(obj);
- int32_t boot_index;
- Error *local_err = NULL;
- visit_type_int32(v, &boot_index, name, &local_err);
- if (local_err) {
- goto out;
- }
- /* check whether bootindex is present in fw_boot_order list */
- check_boot_index(boot_index, &local_err);
- if (local_err) {
- goto out;
- }
- /* change bootindex to a new one */
- s->conf.bootindex = boot_index;
-
-out:
- if (local_err) {
- error_propagate(errp, local_err);
- }
-}
-
-static void nvme_instance_init(Object *obj)
-{
- object_property_add(obj, "bootindex", "int32",
- nvme_get_bootindex,
- nvme_set_bootindex, NULL, NULL, NULL);
- object_property_set_int(obj, -1, "bootindex", NULL);
+ device_add_bootindex_property(obj, &s->conf.bootindex,
+ "bootindex", "/namespace@1,0",
+ DEVICE(obj), &error_abort);
}
static const TypeInfo nvme_info = {
diff --git a/qemu/hw/block/nvme.h b/qemu/hw/block/nvme.h
index bf3a3ccac..8fb0c1075 100644
--- a/qemu/hw/block/nvme.h
+++ b/qemu/hw/block/nvme.h
@@ -1,5 +1,6 @@
#ifndef HW_NVME_H
#define HW_NVME_H
+#include "qemu/cutils.h"
typedef struct NvmeBar {
uint64_t cap;
diff --git a/qemu/hw/block/onenand.c b/qemu/hw/block/onenand.c
index 1b2c89375..883f4b1fa 100644
--- a/qemu/hw/block/onenand.c
+++ b/qemu/hw/block/onenand.c
@@ -18,6 +18,8 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu-common.h"
#include "hw/hw.h"
#include "hw/block/flash.h"
@@ -786,7 +788,7 @@ static int onenand_initfn(SysBusDevice *sbd)
s->otp = memset(g_malloc((64 + 2) << PAGE_SHIFT),
0xff, (64 + 2) << PAGE_SHIFT);
memory_region_init_ram(&s->ram, OBJECT(s), "onenand.ram",
- 0xc000 << s->shift, &error_abort);
+ 0xc000 << s->shift, &error_fatal);
vmstate_register_ram_global(&s->ram);
ram = memory_region_get_ram_ptr(&s->ram);
s->boot[0] = ram + (0x0000 << s->shift);
diff --git a/qemu/hw/block/pflash_cfi01.c b/qemu/hw/block/pflash_cfi01.c
index 2ba6c7729..106a77523 100644
--- a/qemu/hw/block/pflash_cfi01.c
+++ b/qemu/hw/block/pflash_cfi01.c
@@ -36,14 +36,17 @@
* It does not implement much more ...
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/block/flash.h"
#include "sysemu/block-backend.h"
+#include "qapi/error.h"
#include "qemu/timer.h"
#include "qemu/bitops.h"
#include "exec/address-spaces.h"
#include "qemu/host-utils.h"
#include "hw/sysbus.h"
+#include "sysemu/sysemu.h"
#define PFLASH_BUG(fmt, ...) \
do { \
@@ -95,6 +98,7 @@ struct pflash_t {
MemoryRegion mem;
char *name;
void *storage;
+ VMChangeStateEntry *vmstate;
};
static int pflash_post_load(void *opaque, int version_id);
@@ -942,13 +946,25 @@ MemoryRegion *pflash_cfi01_get_memory(pflash_t *fl)
return &fl->mem;
}
+static void postload_update_cb(void *opaque, int running, RunState state)
+{
+ pflash_t *pfl = opaque;
+
+ /* This is called after bdrv_invalidate_cache_all. */
+ qemu_del_vm_change_state_handler(pfl->vmstate);
+ pfl->vmstate = NULL;
+
+ DPRINTF("%s: updating bdrv for %s\n", __func__, pfl->name);
+ pflash_update(pfl, 0, pfl->sector_len * pfl->nb_blocs);
+}
+
static int pflash_post_load(void *opaque, int version_id)
{
pflash_t *pfl = opaque;
if (!pfl->ro) {
- DPRINTF("%s: updating bdrv for %s\n", __func__, pfl->name);
- pflash_update(pfl, 0, pfl->sector_len * pfl->nb_blocs);
+ pfl->vmstate = qemu_add_vm_change_state_handler(postload_update_cb,
+ pfl);
}
return 0;
}
diff --git a/qemu/hw/block/pflash_cfi02.c b/qemu/hw/block/pflash_cfi02.c
index 074a005f6..b13172c6e 100644
--- a/qemu/hw/block/pflash_cfi02.c
+++ b/qemu/hw/block/pflash_cfi02.c
@@ -35,8 +35,10 @@
* It does not implement multiple sectors erase
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/block/flash.h"
+#include "qapi/error.h"
#include "qemu/timer.h"
#include "sysemu/block-backend.h"
#include "exec/address-spaces.h"
@@ -430,8 +432,8 @@ static void pflash_write (pflash_t *pfl, hwaddr offset,
}
pfl->status = 0x00;
/* Let's wait 5 seconds before chip erase is done */
- timer_mod(pfl->timer,
- qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (get_ticks_per_sec() * 5));
+ timer_mod(pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ (NANOSECONDS_PER_SECOND * 5));
break;
case 0x30:
/* Sector erase */
@@ -445,8 +447,8 @@ static void pflash_write (pflash_t *pfl, hwaddr offset,
}
pfl->status = 0x00;
/* Let's wait 1/2 second before sector erase is done */
- timer_mod(pfl->timer,
- qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (get_ticks_per_sec() / 2));
+ timer_mod(pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ (NANOSECONDS_PER_SECOND / 2));
break;
default:
DPRINTF("%s: invalid command %02x (wc 5)\n", __func__, cmd);
diff --git a/qemu/hw/block/tc58128.c b/qemu/hw/block/tc58128.c
index 728f1c3b6..7909d5041 100644
--- a/qemu/hw/block/tc58128.c
+++ b/qemu/hw/block/tc58128.c
@@ -1,3 +1,4 @@
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sh4/sh.h"
#include "hw/loader.h"
diff --git a/qemu/hw/block/virtio-blk.c b/qemu/hw/block/virtio-blk.c
index 1556c9cf5..3f88f8cf5 100644
--- a/qemu/hw/block/virtio-blk.c
+++ b/qemu/hw/block/virtio-blk.c
@@ -11,6 +11,8 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu-common.h"
#include "qemu/iov.h"
#include "qemu/error-report.h"
@@ -20,7 +22,6 @@
#include "sysemu/blockdev.h"
#include "hw/virtio/virtio-blk.h"
#include "dataplane/virtio-blk.h"
-#include "migration/migration.h"
#include "block/scsi.h"
#ifdef __linux__
# include <scsi/sg.h>
@@ -28,26 +29,23 @@
#include "hw/virtio/virtio-bus.h"
#include "hw/virtio/virtio-access.h"
-VirtIOBlockReq *virtio_blk_alloc_request(VirtIOBlock *s)
+void virtio_blk_init_request(VirtIOBlock *s, VirtIOBlockReq *req)
{
- VirtIOBlockReq *req = g_slice_new(VirtIOBlockReq);
req->dev = s;
req->qiov.size = 0;
req->in_len = 0;
req->next = NULL;
req->mr_next = NULL;
- return req;
}
void virtio_blk_free_request(VirtIOBlockReq *req)
{
if (req) {
- g_slice_free(VirtIOBlockReq, req);
+ g_free(req);
}
}
-static void virtio_blk_complete_request(VirtIOBlockReq *req,
- unsigned char status)
+static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status)
{
VirtIOBlock *s = req->dev;
VirtIODevice *vdev = VIRTIO_DEVICE(s);
@@ -56,12 +54,11 @@ static void virtio_blk_complete_request(VirtIOBlockReq *req,
stb_p(&req->in->status, status);
virtqueue_push(s->vq, &req->elem, req->in_len);
- virtio_notify(vdev, s->vq);
-}
-
-static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status)
-{
- req->dev->complete_request(req, status);
+ if (s->dataplane_started && !s->dataplane_disabled) {
+ virtio_blk_data_plane_notify(s->dataplane);
+ } else {
+ virtio_notify(vdev, s->vq);
+ }
}
static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error,
@@ -72,11 +69,14 @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error,
VirtIOBlock *s = req->dev;
if (action == BLOCK_ERROR_ACTION_STOP) {
+ /* Break the link as the next request is going to be parsed from the
+ * ring again. Otherwise we may end up doing a double completion! */
+ req->mr_next = NULL;
req->next = s->rq;
s->rq = req;
} else if (action == BLOCK_ERROR_ACTION_REPORT) {
virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
- block_acct_done(blk_get_stats(s->blk), &req->acct);
+ block_acct_failed(blk_get_stats(s->blk), &req->acct);
virtio_blk_free_request(req);
}
@@ -189,13 +189,11 @@ out:
static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s)
{
- VirtIOBlockReq *req = virtio_blk_alloc_request(s);
+ VirtIOBlockReq *req = virtqueue_pop(s->vq, sizeof(VirtIOBlockReq));
- if (!virtqueue_pop(s->vq, &req->elem)) {
- virtio_blk_free_request(req);
- return NULL;
+ if (req) {
+ virtio_blk_init_request(s, req);
}
-
return req;
}
@@ -404,24 +402,16 @@ void virtio_blk_submit_multireq(BlockBackend *blk, MultiReqBuffer *mrb)
for (i = 0; i < mrb->num_reqs; i++) {
VirtIOBlockReq *req = mrb->reqs[i];
if (num_reqs > 0) {
- bool merge = true;
-
- /* merge would exceed maximum number of IOVs */
- if (niov + req->qiov.niov > IOV_MAX) {
- merge = false;
- }
-
- /* merge would exceed maximum transfer length of backend device */
- if (req->qiov.size / BDRV_SECTOR_SIZE + nb_sectors > max_xfer_len) {
- merge = false;
- }
-
- /* requests are not sequential */
- if (sector_num + nb_sectors != req->sector_num) {
- merge = false;
- }
-
- if (!merge) {
+ /*
+ * NOTE: We cannot merge the requests in below situations:
+ * 1. requests are not sequential
+ * 2. merge would exceed maximum number of IOVs
+ * 3. merge would exceed maximum transfer length of backend device
+ */
+ if (sector_num + nb_sectors != req->sector_num ||
+ niov > blk_get_max_iov(blk) - req->qiov.niov ||
+ req->qiov.size / BDRV_SECTOR_SIZE > max_xfer_len ||
+ nb_sectors > max_xfer_len - req->qiov.size / BDRV_SECTOR_SIZE) {
submit_requests(blk, mrb, start, num_reqs, niov);
num_reqs = 0;
}
@@ -536,6 +526,8 @@ void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
if (!virtio_blk_sect_range_ok(req->dev, req->sector_num,
req->qiov.size)) {
virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
+ block_acct_invalid(blk_get_stats(req->dev->blk),
+ is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ);
virtio_blk_free_request(req);
return;
}
@@ -586,19 +578,12 @@ void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
}
}
-static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
+void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq)
{
- VirtIOBlock *s = VIRTIO_BLK(vdev);
VirtIOBlockReq *req;
MultiReqBuffer mrb = {};
- /* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start
- * dataplane here instead of waiting for .set_status().
- */
- if (s->dataplane) {
- virtio_blk_data_plane_start(s->dataplane);
- return;
- }
+ blk_io_plug(s->blk);
while ((req = virtio_blk_get_request(s))) {
virtio_blk_handle_request(req, &mrb);
@@ -607,6 +592,24 @@ static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
if (mrb.num_reqs) {
virtio_blk_submit_multireq(s->blk, &mrb);
}
+
+ blk_io_unplug(s->blk);
+}
+
+static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOBlock *s = (VirtIOBlock *)vdev;
+
+ if (s->dataplane) {
+ /* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start
+ * dataplane here instead of waiting for .set_status().
+ */
+ virtio_blk_data_plane_start(s->dataplane);
+ if (!s->dataplane_disabled) {
+ return;
+ }
+ }
+ virtio_blk_handle_vq(s, vq);
}
static void virtio_blk_dma_restart_bh(void *opaque)
@@ -731,7 +734,7 @@ static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features,
virtio_add_feature(&features, VIRTIO_BLK_F_GEOMETRY);
virtio_add_feature(&features, VIRTIO_BLK_F_TOPOLOGY);
virtio_add_feature(&features, VIRTIO_BLK_F_BLK_SIZE);
- if (__virtio_has_feature(features, VIRTIO_F_VERSION_1)) {
+ if (virtio_has_feature(features, VIRTIO_F_VERSION_1)) {
if (s->conf.scsi) {
error_setg(errp, "Please set scsi=off for virtio-blk devices in order to use virtio 1.0");
return 0;
@@ -782,10 +785,11 @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status)
*
* s->blk would erroneously be placed in writethrough mode.
*/
- if (!virtio_has_feature(vdev, VIRTIO_BLK_F_CONFIG_WCE)) {
+ if (!virtio_vdev_has_feature(vdev, VIRTIO_BLK_F_CONFIG_WCE)) {
aio_context_acquire(blk_get_aio_context(s->blk));
blk_set_enable_write_cache(s->blk,
- virtio_has_feature(vdev, VIRTIO_BLK_F_WCE));
+ virtio_vdev_has_feature(vdev,
+ VIRTIO_BLK_F_WCE));
aio_context_release(blk_get_aio_context(s->blk));
}
}
@@ -793,6 +797,11 @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status)
static void virtio_blk_save(QEMUFile *f, void *opaque)
{
VirtIODevice *vdev = VIRTIO_DEVICE(opaque);
+ VirtIOBlock *s = VIRTIO_BLK(vdev);
+
+ if (s->dataplane) {
+ virtio_blk_data_plane_stop(s->dataplane);
+ }
virtio_save(vdev, f);
}
@@ -804,8 +813,7 @@ static void virtio_blk_save_device(VirtIODevice *vdev, QEMUFile *f)
while (req) {
qemu_put_sbyte(f, 1);
- qemu_put_buffer(f, (unsigned char *)&req->elem,
- sizeof(VirtQueueElement));
+ qemu_put_virtqueue_element(f, &req->elem);
req = req->next;
}
qemu_put_sbyte(f, 0);
@@ -828,16 +836,11 @@ static int virtio_blk_load_device(VirtIODevice *vdev, QEMUFile *f,
VirtIOBlock *s = VIRTIO_BLK(vdev);
while (qemu_get_sbyte(f)) {
- VirtIOBlockReq *req = virtio_blk_alloc_request(s);
- qemu_get_buffer(f, (unsigned char *)&req->elem,
- sizeof(VirtQueueElement));
+ VirtIOBlockReq *req;
+ req = qemu_get_virtqueue_element(f, sizeof(VirtIOBlockReq));
+ virtio_blk_init_request(s, req);
req->next = s->rq;
s->rq = req;
-
- virtqueue_map_sg(req->elem.in_sg, req->elem.in_addr,
- req->elem.in_num, 1);
- virtqueue_map_sg(req->elem.out_sg, req->elem.out_addr,
- req->elem.out_num, 0);
}
return 0;
@@ -854,36 +857,6 @@ static const BlockDevOps virtio_block_ops = {
.resize_cb = virtio_blk_resize,
};
-/* Disable dataplane thread during live migration since it does not
- * update the dirty memory bitmap yet.
- */
-static void virtio_blk_migration_state_changed(Notifier *notifier, void *data)
-{
- VirtIOBlock *s = container_of(notifier, VirtIOBlock,
- migration_state_notifier);
- MigrationState *mig = data;
- Error *err = NULL;
-
- if (migration_in_setup(mig)) {
- if (!s->dataplane) {
- return;
- }
- virtio_blk_data_plane_destroy(s->dataplane);
- s->dataplane = NULL;
- } else if (migration_has_finished(mig) ||
- migration_has_failed(mig)) {
- if (s->dataplane) {
- return;
- }
- blk_drain_all(); /* complete in-flight non-dataplane requests */
- virtio_blk_data_plane_create(VIRTIO_DEVICE(s), &s->conf,
- &s->dataplane, &err);
- if (err != NULL) {
- error_report_err(err);
- }
- }
-}
-
static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
@@ -918,15 +891,12 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
s->sector_mask = (s->conf.conf.logical_block_size / BDRV_SECTOR_SIZE) - 1;
s->vq = virtio_add_queue(vdev, 128, virtio_blk_handle_output);
- s->complete_request = virtio_blk_complete_request;
virtio_blk_data_plane_create(vdev, conf, &s->dataplane, &err);
if (err != NULL) {
error_propagate(errp, err);
virtio_cleanup(vdev);
return;
}
- s->migration_state_notifier.notify = virtio_blk_migration_state_changed;
- add_migration_state_change_notifier(&s->migration_state_notifier);
s->change = qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s);
register_savevm(dev, "virtio-blk", virtio_blk_id++, 2,
@@ -942,7 +912,6 @@ static void virtio_blk_device_unrealize(DeviceState *dev, Error **errp)
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VirtIOBlock *s = VIRTIO_BLK(dev);
- remove_migration_state_change_notifier(&s->migration_state_notifier);
virtio_blk_data_plane_destroy(s->dataplane);
s->dataplane = NULL;
qemu_del_vm_change_state_handler(s->change);
@@ -970,11 +939,10 @@ static Property virtio_blk_properties[] = {
DEFINE_PROP_STRING("serial", VirtIOBlock, conf.serial),
DEFINE_PROP_BIT("config-wce", VirtIOBlock, conf.config_wce, 0, true),
#ifdef __linux__
- DEFINE_PROP_BIT("scsi", VirtIOBlock, conf.scsi, 0, true),
+ DEFINE_PROP_BIT("scsi", VirtIOBlock, conf.scsi, 0, false),
#endif
DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0,
true),
- DEFINE_PROP_BIT("x-data-plane", VirtIOBlock, conf.data_plane, 0, false),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/qemu/hw/block/xen_blkif.h b/qemu/hw/block/xen_blkif.h
index 711b69274..c68487cb3 100644
--- a/qemu/hw/block/xen_blkif.h
+++ b/qemu/hw/block/xen_blkif.h
@@ -85,8 +85,10 @@ static inline void blkif_get_x86_32_req(blkif_request_t *dst, blkif_x86_32_reque
d->nr_sectors = s->nr_sectors;
return;
}
- if (n > src->nr_segments)
- n = src->nr_segments;
+ /* prevent the compiler from optimizing the code and using src->nr_segments instead */
+ barrier();
+ if (n > dst->nr_segments)
+ n = dst->nr_segments;
for (i = 0; i < n; i++)
dst->seg[i] = src->seg[i];
}
@@ -106,8 +108,10 @@ static inline void blkif_get_x86_64_req(blkif_request_t *dst, blkif_x86_64_reque
d->nr_sectors = s->nr_sectors;
return;
}
- if (n > src->nr_segments)
- n = src->nr_segments;
+ /* prevent the compiler from optimizing the code and using src->nr_segments instead */
+ barrier();
+ if (n > dst->nr_segments)
+ n = dst->nr_segments;
for (i = 0; i < n; i++)
dst->seg[i] = src->seg[i];
}
diff --git a/qemu/hw/block/xen_disk.c b/qemu/hw/block/xen_disk.c
index 267d8a8c7..d4ce380fe 100644
--- a/qemu/hw/block/xen_disk.c
+++ b/qemu/hw/block/xen_disk.c
@@ -19,19 +19,8 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <unistd.h>
-#include <signal.h>
-#include <inttypes.h>
-#include <time.h>
-#include <fcntl.h>
-#include <errno.h>
+#include "qemu/osdep.h"
#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/uio.h>
@@ -40,6 +29,7 @@
#include "xen_blkif.h"
#include "sysemu/blockdev.h"
#include "sysemu/block-backend.h"
+#include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
@@ -76,7 +66,6 @@ struct ioreq {
off_t start;
QEMUIOVector v;
int presync;
- int postsync;
uint8_t mapped;
/* grant mapping */
@@ -145,7 +134,6 @@ static void ioreq_reset(struct ioreq *ioreq)
ioreq->status = 0;
ioreq->start = 0;
ioreq->presync = 0;
- ioreq->postsync = 0;
ioreq->mapped = 0;
memset(ioreq->domids, 0, sizeof(ioreq->domids));
@@ -174,11 +162,11 @@ static gint int_cmp(gconstpointer a, gconstpointer b, gpointer user_data)
static void destroy_grant(gpointer pgnt)
{
PersistentGrant *grant = pgnt;
- XenGnttab gnt = grant->blkdev->xendev.gnttabdev;
+ xengnttab_handle *gnt = grant->blkdev->xendev.gnttabdev;
- if (xc_gnttab_munmap(gnt, grant->page, 1) != 0) {
+ if (xengnttab_unmap(gnt, grant->page, 1) != 0) {
xen_be_printf(&grant->blkdev->xendev, 0,
- "xc_gnttab_munmap failed: %s\n",
+ "xengnttab_unmap failed: %s\n",
strerror(errno));
}
grant->blkdev->persistent_gnt_count--;
@@ -191,11 +179,11 @@ static void remove_persistent_region(gpointer data, gpointer dev)
{
PersistentRegion *region = data;
struct XenBlkDev *blkdev = dev;
- XenGnttab gnt = blkdev->xendev.gnttabdev;
+ xengnttab_handle *gnt = blkdev->xendev.gnttabdev;
- if (xc_gnttab_munmap(gnt, region->addr, region->num) != 0) {
+ if (xengnttab_unmap(gnt, region->addr, region->num) != 0) {
xen_be_printf(&blkdev->xendev, 0,
- "xc_gnttab_munmap region %p failed: %s\n",
+ "xengnttab_unmap region %p failed: %s\n",
region->addr, strerror(errno));
}
xen_be_printf(&blkdev->xendev, 3,
@@ -330,7 +318,7 @@ err:
static void ioreq_unmap(struct ioreq *ioreq)
{
- XenGnttab gnt = ioreq->blkdev->xendev.gnttabdev;
+ xengnttab_handle *gnt = ioreq->blkdev->xendev.gnttabdev;
int i;
if (ioreq->num_unmap == 0 || ioreq->mapped == 0) {
@@ -340,8 +328,9 @@ static void ioreq_unmap(struct ioreq *ioreq)
if (!ioreq->pages) {
return;
}
- if (xc_gnttab_munmap(gnt, ioreq->pages, ioreq->num_unmap) != 0) {
- xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n",
+ if (xengnttab_unmap(gnt, ioreq->pages, ioreq->num_unmap) != 0) {
+ xen_be_printf(&ioreq->blkdev->xendev, 0,
+ "xengnttab_unmap failed: %s\n",
strerror(errno));
}
ioreq->blkdev->cnt_map -= ioreq->num_unmap;
@@ -351,8 +340,9 @@ static void ioreq_unmap(struct ioreq *ioreq)
if (!ioreq->page[i]) {
continue;
}
- if (xc_gnttab_munmap(gnt, ioreq->page[i], 1) != 0) {
- xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n",
+ if (xengnttab_unmap(gnt, ioreq->page[i], 1) != 0) {
+ xen_be_printf(&ioreq->blkdev->xendev, 0,
+ "xengnttab_unmap failed: %s\n",
strerror(errno));
}
ioreq->blkdev->cnt_map--;
@@ -364,7 +354,7 @@ static void ioreq_unmap(struct ioreq *ioreq)
static int ioreq_map(struct ioreq *ioreq)
{
- XenGnttab gnt = ioreq->blkdev->xendev.gnttabdev;
+ xengnttab_handle *gnt = ioreq->blkdev->xendev.gnttabdev;
uint32_t domids[BLKIF_MAX_SEGMENTS_PER_REQUEST];
uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST];
void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST];
@@ -415,7 +405,7 @@ static int ioreq_map(struct ioreq *ioreq)
}
if (batch_maps && new_maps) {
- ioreq->pages = xc_gnttab_map_grant_refs
+ ioreq->pages = xengnttab_map_grant_refs
(gnt, new_maps, domids, refs, ioreq->prot);
if (ioreq->pages == NULL) {
xen_be_printf(&ioreq->blkdev->xendev, 0,
@@ -431,7 +421,7 @@ static int ioreq_map(struct ioreq *ioreq)
ioreq->blkdev->cnt_map += new_maps;
} else if (new_maps) {
for (i = 0; i < new_maps; i++) {
- ioreq->page[i] = xc_gnttab_map_grant_ref
+ ioreq->page[i] = xengnttab_map_grant_ref
(gnt, domids[i], refs[i], ioreq->prot);
if (ioreq->page[i] == NULL) {
xen_be_printf(&ioreq->blkdev->xendev, 0,
@@ -521,12 +511,6 @@ static void qemu_aio_complete(void *opaque, int ret)
if (ioreq->aio_inflight > 0) {
return;
}
- if (ioreq->postsync) {
- ioreq->postsync = 0;
- ioreq->aio_inflight++;
- blk_aio_flush(ioreq->blkdev->blk, qemu_aio_complete, ioreq);
- return;
- }
ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY;
ioreq_unmap(ioreq);
@@ -538,7 +522,11 @@ static void qemu_aio_complete(void *opaque, int ret)
break;
}
case BLKIF_OP_READ:
- block_acct_done(blk_get_stats(ioreq->blkdev->blk), &ioreq->acct);
+ if (ioreq->status == BLKIF_RSP_OKAY) {
+ block_acct_done(blk_get_stats(ioreq->blkdev->blk), &ioreq->acct);
+ } else {
+ block_acct_failed(blk_get_stats(ioreq->blkdev->blk), &ioreq->acct);
+ }
break;
case BLKIF_OP_DISCARD:
default:
@@ -577,7 +565,9 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq)
}
block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct,
- ioreq->v.size, BLOCK_ACCT_WRITE);
+ ioreq->v.size,
+ ioreq->req.operation == BLKIF_OP_WRITE ?
+ BLOCK_ACCT_WRITE : BLOCK_ACCT_FLUSH);
ioreq->aio_inflight++;
blk_aio_writev(blkdev->blk, ioreq->start / BLOCK_SIZE,
&ioreq->v, ioreq->v.size / BLOCK_SIZE,
@@ -721,6 +711,23 @@ static void blk_handle_requests(struct XenBlkDev *blkdev)
/* parse them */
if (ioreq_parse(ioreq) != 0) {
+
+ switch (ioreq->req.operation) {
+ case BLKIF_OP_READ:
+ block_acct_invalid(blk_get_stats(blkdev->blk),
+ BLOCK_ACCT_READ);
+ break;
+ case BLKIF_OP_WRITE:
+ block_acct_invalid(blk_get_stats(blkdev->blk),
+ BLOCK_ACCT_WRITE);
+ break;
+ case BLKIF_OP_FLUSH_DISKCACHE:
+ block_acct_invalid(blk_get_stats(blkdev->blk),
+ BLOCK_ACCT_FLUSH);
+ default:
+ break;
+ };
+
if (blk_send_response_one(ioreq)) {
xen_be_send_notify(&blkdev->xendev);
}
@@ -764,9 +771,9 @@ static void blk_alloc(struct XenDevice *xendev)
if (xen_mode != XEN_EMULATE) {
batch_maps = 1;
}
- if (xc_gnttab_set_max_grants(xendev->gnttabdev,
+ if (xengnttab_set_max_grants(xendev->gnttabdev,
MAX_GRANTS(max_requests, BLKIF_MAX_SEGMENTS_PER_REQUEST)) < 0) {
- xen_be_printf(xendev, 0, "xc_gnttab_set_max_grants failed: %s\n",
+ xen_be_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n",
strerror(errno));
}
}
@@ -811,6 +818,9 @@ static int blk_init(struct XenDevice *xendev)
if (!strcmp("aio", blkdev->fileproto)) {
blkdev->fileproto = "raw";
}
+ if (!strcmp("vhd", blkdev->fileproto)) {
+ blkdev->fileproto = "vpc";
+ }
if (blkdev->mode == NULL) {
blkdev->mode = xenstore_read_be_str(&blkdev->xendev, "mode");
}
@@ -879,12 +889,14 @@ static int blk_connect(struct XenDevice *xendev)
struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
int pers, index, qflags;
bool readonly = true;
+ bool writethrough = true;
/* read-only ? */
if (blkdev->directiosafe) {
qflags = BDRV_O_NOCACHE | BDRV_O_NATIVE_AIO;
} else {
- qflags = BDRV_O_CACHE_WB;
+ qflags = 0;
+ writethrough = false;
}
if (strcmp(blkdev->mode, "w") == 0) {
qflags |= BDRV_O_RDWR;
@@ -908,7 +920,7 @@ static int blk_connect(struct XenDevice *xendev)
/* setup via xenbus -> create new block driver instance */
xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n");
- blkdev->blk = blk_new_open(blkdev->dev, blkdev->filename, NULL, options,
+ blkdev->blk = blk_new_open(blkdev->filename, NULL, options,
qflags, &local_err);
if (!blkdev->blk) {
xen_be_printf(&blkdev->xendev, 0, "error: %s\n",
@@ -916,6 +928,7 @@ static int blk_connect(struct XenDevice *xendev)
error_free(local_err);
return -1;
}
+ blk_set_enable_write_cache(blkdev->blk, !writethrough);
} else {
/* setup via qemu cmdline -> already setup for us */
xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n");
@@ -932,9 +945,11 @@ static int blk_connect(struct XenDevice *xendev)
blk_attach_dev_nofail(blkdev->blk, blkdev);
blkdev->file_size = blk_getlength(blkdev->blk);
if (blkdev->file_size < 0) {
+ BlockDriverState *bs = blk_bs(blkdev->blk);
+ const char *drv_name = bs ? bdrv_get_format_name(bs) : NULL;
xen_be_printf(&blkdev->xendev, 1, "blk_getlength: %d (%s) | drv %s\n",
(int)blkdev->file_size, strerror(-blkdev->file_size),
- bdrv_get_format_name(blk_bs(blkdev->blk)) ?: "-");
+ drv_name ?: "-");
blkdev->file_size = 0;
}
@@ -971,7 +986,7 @@ static int blk_connect(struct XenDevice *xendev)
}
}
- blkdev->sring = xc_gnttab_map_grant_ref(blkdev->xendev.gnttabdev,
+ blkdev->sring = xengnttab_map_grant_ref(blkdev->xendev.gnttabdev,
blkdev->xendev.dom,
blkdev->ring_ref,
PROT_READ | PROT_WRITE);
@@ -1036,7 +1051,7 @@ static void blk_disconnect(struct XenDevice *xendev)
xen_be_unbind_evtchn(&blkdev->xendev);
if (blkdev->sring) {
- xc_gnttab_munmap(blkdev->xendev.gnttabdev, blkdev->sring, 1);
+ xengnttab_unmap(blkdev->xendev.gnttabdev, blkdev->sring, 1);
blkdev->cnt_map--;
blkdev->sring = NULL;
}
diff --git a/qemu/hw/bt/core.c b/qemu/hw/bt/core.c
index 0ffc94889..615f0af07 100644
--- a/qemu/hw/bt/core.c
+++ b/qemu/hw/bt/core.c
@@ -17,6 +17,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "sysemu/bt.h"
#include "hw/bt.h"
diff --git a/qemu/hw/bt/hci-csr.c b/qemu/hw/bt/hci-csr.c
index 7b9b91608..2e970b656 100644
--- a/qemu/hw/bt/hci-csr.c
+++ b/qemu/hw/bt/hci-csr.c
@@ -18,6 +18,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "sysemu/char.h"
#include "qemu/timer.h"
@@ -362,7 +363,7 @@ static int csrhci_ioctl(struct CharDriverState *chr, int cmd, void *arg)
switch (cmd) {
case CHR_IOCTL_SERIAL_SET_PARAMS:
ssp = (QEMUSerialSetParams *) arg;
- s->baud_delay = get_ticks_per_sec() / ssp->speed;
+ s->baud_delay = NANOSECONDS_PER_SECOND / ssp->speed;
/* Moments later... (but shorter than 100ms) */
s->modem_state |= CHR_TIOCM_CTS;
break;
@@ -388,7 +389,7 @@ static void csrhci_reset(struct csrhci_s *s)
s->out_len = 0;
s->out_size = FIFO_LEN;
s->in_len = 0;
- s->baud_delay = get_ticks_per_sec();
+ s->baud_delay = NANOSECONDS_PER_SECOND;
s->enable = 0;
s->in_hdr = INT_MAX;
s->in_data = INT_MAX;
diff --git a/qemu/hw/bt/hci.c b/qemu/hw/bt/hci.c
index 7ea3dc6b7..7d5220509 100644
--- a/qemu/hw/bt/hci.c
+++ b/qemu/hw/bt/hci.c
@@ -18,11 +18,16 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu-common.h"
#include "qemu/timer.h"
#include "hw/usb.h"
#include "sysemu/bt.h"
#include "hw/bt.h"
+#include "qapi/qmp/qerror.h"
+#include "sysemu/replay.h"
+#include "qemu/cutils.h"
struct bt_hci_s {
uint8_t *(*evt_packet)(void *opaque);
@@ -72,6 +77,8 @@ struct bt_hci_s {
struct HCIInfo info;
struct bt_device_s device;
+
+ Error *replay_blocker;
};
#define DEFAULT_RSSI_DBM 20
@@ -595,7 +602,7 @@ static void bt_hci_inquiry_result(struct bt_hci_s *hci,
static void bt_hci_mod_timer_1280ms(QEMUTimer *timer, int period)
{
timer_mod(timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
- muldiv64(period << 7, get_ticks_per_sec(), 100));
+ (uint64_t)(period << 7) * 10000000);
}
static void bt_hci_inquiry_start(struct bt_hci_s *hci, int length)
@@ -1099,7 +1106,7 @@ static int bt_hci_mode_change(struct bt_hci_s *hci, uint16_t handle,
bt_hci_event_status(hci, HCI_SUCCESS);
timer_mod(link->acl_mode_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
- muldiv64(interval * 625, get_ticks_per_sec(), 1000000));
+ ((uint64_t)interval * 625) * 1000);
bt_hci_lmp_mode_change_master(hci, link->link, mode, interval);
return 0;
@@ -1151,8 +1158,7 @@ static void bt_hci_reset(struct bt_hci_s *hci)
hci->event_mask[7] = 0x00;
hci->device.inquiry_scan = 0;
hci->device.page_scan = 0;
- if (hci->device.lmp_name)
- g_free((void *) hci->device.lmp_name);
+ g_free((void *) hci->device.lmp_name);
hci->device.lmp_name = NULL;
hci->device.class[0] = 0x00;
hci->device.class[1] = 0x00;
@@ -1829,8 +1835,7 @@ static void bt_submit_hci(struct HCIInfo *info,
case cmd_opcode_pack(OGF_HOST_CTL, OCF_CHANGE_LOCAL_NAME):
LENGTH_CHECK(change_local_name);
- if (hci->device.lmp_name)
- g_free((void *) hci->device.lmp_name);
+ g_free((void *) hci->device.lmp_name);
hci->device.lmp_name = g_strndup(PARAM(change_local_name, name),
sizeof(PARAM(change_local_name, name)));
bt_hci_event_complete_status(hci, HCI_SUCCESS);
@@ -2191,6 +2196,9 @@ struct HCIInfo *bt_new_hci(struct bt_scatternet_s *net)
s->device.handle_destroy = bt_hci_destroy;
+ error_setg(&s->replay_blocker, QERR_REPLAY_NOT_SUPPORTED, "-bt hci");
+ replay_add_blocker(s->replay_blocker);
+
return &s->info;
}
@@ -2231,8 +2239,7 @@ static void bt_hci_done(struct HCIInfo *info)
bt_device_done(&hci->device);
- if (hci->device.lmp_name)
- g_free((void *) hci->device.lmp_name);
+ g_free((void *) hci->device.lmp_name);
/* Be gentle and send DISCONNECT to all connected peers and those
* currently waiting for us to accept or reject a connection request.
diff --git a/qemu/hw/bt/hid.c b/qemu/hw/bt/hid.c
index af494e1e0..f6affbbb4 100644
--- a/qemu/hw/bt/hid.c
+++ b/qemu/hw/bt/hid.c
@@ -18,6 +18,7 @@
* with this program; if not, if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/timer.h"
#include "ui/console.h"
diff --git a/qemu/hw/bt/l2cap.c b/qemu/hw/bt/l2cap.c
index 591e04778..806525194 100644
--- a/qemu/hw/bt/l2cap.c
+++ b/qemu/hw/bt/l2cap.c
@@ -17,6 +17,7 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/timer.h"
#include "hw/bt.h"
diff --git a/qemu/hw/bt/sdp.c b/qemu/hw/bt/sdp.c
index c90374795..be26009b0 100644
--- a/qemu/hw/bt/sdp.c
+++ b/qemu/hw/bt/sdp.c
@@ -17,6 +17,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "hw/bt.h"
@@ -42,7 +43,7 @@ struct bt_l2cap_sdp_state_s {
static ssize_t sdp_datalen(const uint8_t **element, ssize_t *left)
{
- size_t len = *(*element) ++ & SDP_DSIZE_MASK;
+ uint32_t len = *(*element) ++ & SDP_DSIZE_MASK;
if (!*left)
return -1;
@@ -150,12 +151,14 @@ static ssize_t sdp_svc_search(struct bt_l2cap_sdp_state_s *sdp,
if (seqlen < 3 || len < seqlen)
return -SDP_INVALID_SYNTAX;
len -= seqlen;
-
while (seqlen)
if (sdp_svc_match(sdp, &req, &seqlen))
return -SDP_INVALID_SYNTAX;
- } else if (sdp_svc_match(sdp, &req, &seqlen))
- return -SDP_INVALID_SYNTAX;
+ } else {
+ if (sdp_svc_match(sdp, &req, &len)) {
+ return -SDP_INVALID_SYNTAX;
+ }
+ }
if (len < 3)
return -SDP_INVALID_SYNTAX;
@@ -278,8 +281,11 @@ static ssize_t sdp_attr_get(struct bt_l2cap_sdp_state_s *sdp,
while (seqlen)
if (sdp_attr_match(record, &req, &seqlen))
return -SDP_INVALID_SYNTAX;
- } else if (sdp_attr_match(record, &req, &seqlen))
- return -SDP_INVALID_SYNTAX;
+ } else {
+ if (sdp_attr_match(record, &req, &len)) {
+ return -SDP_INVALID_SYNTAX;
+ }
+ }
if (len < 1)
return -SDP_INVALID_SYNTAX;
@@ -393,8 +399,11 @@ static ssize_t sdp_svc_search_attr_get(struct bt_l2cap_sdp_state_s *sdp,
while (seqlen)
if (sdp_svc_match(sdp, &req, &seqlen))
return -SDP_INVALID_SYNTAX;
- } else if (sdp_svc_match(sdp, &req, &seqlen))
- return -SDP_INVALID_SYNTAX;
+ } else {
+ if (sdp_svc_match(sdp, &req, &len)) {
+ return -SDP_INVALID_SYNTAX;
+ }
+ }
if (len < 3)
return -SDP_INVALID_SYNTAX;
@@ -413,8 +422,11 @@ static ssize_t sdp_svc_search_attr_get(struct bt_l2cap_sdp_state_s *sdp,
while (seqlen)
if (sdp_svc_attr_match(sdp, &req, &seqlen))
return -SDP_INVALID_SYNTAX;
- } else if (sdp_svc_attr_match(sdp, &req, &seqlen))
- return -SDP_INVALID_SYNTAX;
+ } else {
+ if (sdp_svc_attr_match(sdp, &req, &len)) {
+ return -SDP_INVALID_SYNTAX;
+ }
+ }
if (len < 1)
return -SDP_INVALID_SYNTAX;
diff --git a/qemu/hw/char/Makefile.objs b/qemu/hw/char/Makefile.objs
index 5931cc840..69a553cd8 100644
--- a/qemu/hw/char/Makefile.objs
+++ b/qemu/hw/char/Makefile.objs
@@ -16,6 +16,7 @@ obj-$(CONFIG_SH4) += sh_serial.o
obj-$(CONFIG_PSERIES) += spapr_vty.o
obj-$(CONFIG_DIGIC) += digic-uart.o
obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o
+obj-$(CONFIG_RASPI) += bcm2835_aux.o
common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o
common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o
diff --git a/qemu/hw/char/bcm2835_aux.c b/qemu/hw/char/bcm2835_aux.c
new file mode 100644
index 000000000..0394d11a8
--- /dev/null
+++ b/qemu/hw/char/bcm2835_aux.c
@@ -0,0 +1,316 @@
+/*
+ * BCM2835 (Raspberry Pi / Pi 2) Aux block (mini UART and SPI).
+ * Copyright (c) 2015, Microsoft
+ * Written by Andrew Baumann
+ * Based on pl011.c, copyright terms below:
+ *
+ * Arm PrimeCell PL011 UART
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ *
+ * At present only the core UART functions (data path for tx/rx) are
+ * implemented. The following features/registers are unimplemented:
+ * - Line/modem control
+ * - Scratch register
+ * - Extra control
+ * - Baudrate
+ * - SPI interfaces
+ */
+
+#include "qemu/osdep.h"
+#include "hw/char/bcm2835_aux.h"
+
+#define AUX_IRQ 0x0
+#define AUX_ENABLES 0x4
+#define AUX_MU_IO_REG 0x40
+#define AUX_MU_IER_REG 0x44
+#define AUX_MU_IIR_REG 0x48
+#define AUX_MU_LCR_REG 0x4c
+#define AUX_MU_MCR_REG 0x50
+#define AUX_MU_LSR_REG 0x54
+#define AUX_MU_MSR_REG 0x58
+#define AUX_MU_SCRATCH 0x5c
+#define AUX_MU_CNTL_REG 0x60
+#define AUX_MU_STAT_REG 0x64
+#define AUX_MU_BAUD_REG 0x68
+
+/* bits in IER/IIR registers */
+#define TX_INT 0x1
+#define RX_INT 0x2
+
+static void bcm2835_aux_update(BCM2835AuxState *s)
+{
+ /* signal an interrupt if either:
+ * 1. rx interrupt is enabled and we have a non-empty rx fifo, or
+ * 2. the tx interrupt is enabled (since we instantly drain the tx fifo)
+ */
+ s->iir = 0;
+ if ((s->ier & RX_INT) && s->read_count != 0) {
+ s->iir |= RX_INT;
+ }
+ if (s->ier & TX_INT) {
+ s->iir |= TX_INT;
+ }
+ qemu_set_irq(s->irq, s->iir != 0);
+}
+
+static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset, unsigned size)
+{
+ BCM2835AuxState *s = opaque;
+ uint32_t c, res;
+
+ switch (offset) {
+ case AUX_IRQ:
+ return s->iir != 0;
+
+ case AUX_ENABLES:
+ return 1; /* mini UART permanently enabled */
+
+ case AUX_MU_IO_REG:
+ /* "DLAB bit set means access baudrate register" is NYI */
+ c = s->read_fifo[s->read_pos];
+ if (s->read_count > 0) {
+ s->read_count--;
+ if (++s->read_pos == BCM2835_AUX_RX_FIFO_LEN) {
+ s->read_pos = 0;
+ }
+ }
+ if (s->chr) {
+ qemu_chr_accept_input(s->chr);
+ }
+ bcm2835_aux_update(s);
+ return c;
+
+ case AUX_MU_IER_REG:
+ /* "DLAB bit set means access baudrate register" is NYI */
+ return 0xc0 | s->ier; /* FIFO enables always read 1 */
+
+ case AUX_MU_IIR_REG:
+ res = 0xc0; /* FIFO enables */
+ /* The spec is unclear on what happens when both tx and rx
+ * interrupts are active, besides that this cannot occur. At
+ * present, we choose to prioritise the rx interrupt, since
+ * the tx fifo is always empty. */
+ if (s->read_count != 0) {
+ res |= 0x4;
+ } else {
+ res |= 0x2;
+ }
+ if (s->iir == 0) {
+ res |= 0x1;
+ }
+ return res;
+
+ case AUX_MU_LCR_REG:
+ qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__);
+ return 0;
+
+ case AUX_MU_MCR_REG:
+ qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__);
+ return 0;
+
+ case AUX_MU_LSR_REG:
+ res = 0x60; /* tx idle, empty */
+ if (s->read_count != 0) {
+ res |= 0x1;
+ }
+ return res;
+
+ case AUX_MU_MSR_REG:
+ qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MSR_REG unsupported\n", __func__);
+ return 0;
+
+ case AUX_MU_SCRATCH:
+ qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__);
+ return 0;
+
+ case AUX_MU_CNTL_REG:
+ return 0x3; /* tx, rx enabled */
+
+ case AUX_MU_STAT_REG:
+ res = 0x30e; /* space in the output buffer, empty tx fifo, idle tx/rx */
+ if (s->read_count > 0) {
+ res |= 0x1; /* data in input buffer */
+ assert(s->read_count < BCM2835_AUX_RX_FIFO_LEN);
+ res |= ((uint32_t)s->read_count) << 16; /* rx fifo fill level */
+ }
+ return res;
+
+ case AUX_MU_BAUD_REG:
+ qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__);
+ return 0;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return 0;
+ }
+}
+
+static void bcm2835_aux_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ BCM2835AuxState *s = opaque;
+ unsigned char ch;
+
+ switch (offset) {
+ case AUX_ENABLES:
+ if (value != 1) {
+ qemu_log_mask(LOG_UNIMP, "%s: unsupported attempt to enable SPI "
+ "or disable UART\n", __func__);
+ }
+ break;
+
+ case AUX_MU_IO_REG:
+ /* "DLAB bit set means access baudrate register" is NYI */
+ ch = value;
+ if (s->chr) {
+ qemu_chr_fe_write(s->chr, &ch, 1);
+ }
+ break;
+
+ case AUX_MU_IER_REG:
+ /* "DLAB bit set means access baudrate register" is NYI */
+ s->ier = value & (TX_INT | RX_INT);
+ bcm2835_aux_update(s);
+ break;
+
+ case AUX_MU_IIR_REG:
+ if (value & 0x2) {
+ s->read_count = 0;
+ }
+ break;
+
+ case AUX_MU_LCR_REG:
+ qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__);
+ break;
+
+ case AUX_MU_MCR_REG:
+ qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__);
+ break;
+
+ case AUX_MU_SCRATCH:
+ qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__);
+ break;
+
+ case AUX_MU_CNTL_REG:
+ qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_CNTL_REG unsupported\n", __func__);
+ break;
+
+ case AUX_MU_BAUD_REG:
+ qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__);
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ }
+
+ bcm2835_aux_update(s);
+}
+
+static int bcm2835_aux_can_receive(void *opaque)
+{
+ BCM2835AuxState *s = opaque;
+
+ return s->read_count < BCM2835_AUX_RX_FIFO_LEN;
+}
+
+static void bcm2835_aux_put_fifo(void *opaque, uint8_t value)
+{
+ BCM2835AuxState *s = opaque;
+ int slot;
+
+ slot = s->read_pos + s->read_count;
+ if (slot >= BCM2835_AUX_RX_FIFO_LEN) {
+ slot -= BCM2835_AUX_RX_FIFO_LEN;
+ }
+ s->read_fifo[slot] = value;
+ s->read_count++;
+ if (s->read_count == BCM2835_AUX_RX_FIFO_LEN) {
+ /* buffer full */
+ }
+ bcm2835_aux_update(s);
+}
+
+static void bcm2835_aux_receive(void *opaque, const uint8_t *buf, int size)
+{
+ bcm2835_aux_put_fifo(opaque, *buf);
+}
+
+static const MemoryRegionOps bcm2835_aux_ops = {
+ .read = bcm2835_aux_read,
+ .write = bcm2835_aux_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+static const VMStateDescription vmstate_bcm2835_aux = {
+ .name = TYPE_BCM2835_AUX,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8_ARRAY(read_fifo, BCM2835AuxState,
+ BCM2835_AUX_RX_FIFO_LEN),
+ VMSTATE_UINT8(read_pos, BCM2835AuxState),
+ VMSTATE_UINT8(read_count, BCM2835AuxState),
+ VMSTATE_UINT8(ier, BCM2835AuxState),
+ VMSTATE_UINT8(iir, BCM2835AuxState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void bcm2835_aux_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ BCM2835AuxState *s = BCM2835_AUX(obj);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_aux_ops, s,
+ TYPE_BCM2835_AUX, 0x100);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->irq);
+}
+
+static void bcm2835_aux_realize(DeviceState *dev, Error **errp)
+{
+ BCM2835AuxState *s = BCM2835_AUX(dev);
+
+ if (s->chr) {
+ qemu_chr_add_handlers(s->chr, bcm2835_aux_can_receive,
+ bcm2835_aux_receive, NULL, s);
+ }
+}
+
+static Property bcm2835_aux_props[] = {
+ DEFINE_PROP_CHR("chardev", BCM2835AuxState, chr),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void bcm2835_aux_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = bcm2835_aux_realize;
+ dc->vmsd = &vmstate_bcm2835_aux;
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
+ dc->props = bcm2835_aux_props;
+}
+
+static const TypeInfo bcm2835_aux_info = {
+ .name = TYPE_BCM2835_AUX,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BCM2835AuxState),
+ .instance_init = bcm2835_aux_init,
+ .class_init = bcm2835_aux_class_init,
+};
+
+static void bcm2835_aux_register_types(void)
+{
+ type_register_static(&bcm2835_aux_info);
+}
+
+type_init(bcm2835_aux_register_types)
diff --git a/qemu/hw/char/cadence_uart.c b/qemu/hw/char/cadence_uart.c
index 9d379e5b1..797787823 100644
--- a/qemu/hw/char/cadence_uart.c
+++ b/qemu/hw/char/cadence_uart.c
@@ -16,6 +16,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/char/cadence_uart.h"
#ifdef CADENCE_UART_ERR_DEBUG
@@ -204,7 +205,7 @@ static void uart_parameters_setup(CadenceUARTState *s)
}
packet_size += ssp.data_bits + ssp.stop_bits;
- s->char_tx_time = (get_ticks_per_sec() / ssp.speed) * packet_size;
+ s->char_tx_time = (NANOSECONDS_PER_SECOND / ssp.speed) * packet_size;
if (s->chr) {
qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
}
@@ -374,6 +375,9 @@ static void uart_write(void *opaque, hwaddr offset,
DB_PRINT(" offset:%x data:%08x\n", (unsigned)offset, (unsigned)value);
offset >>= 2;
+ if (offset >= CADENCE_UART_R_MAX) {
+ return;
+ }
switch (offset) {
case R_IER: /* ier (wts imr) */
s->r[R_IMR] |= value;
@@ -478,7 +482,7 @@ static void cadence_uart_init(Object *obj)
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
- s->char_tx_time = (get_ticks_per_sec() / 9600) * 10;
+ s->char_tx_time = (NANOSECONDS_PER_SECOND / 9600) * 10;
}
static int cadence_uart_post_load(void *opaque, int version_id)
diff --git a/qemu/hw/char/debugcon.c b/qemu/hw/char/debugcon.c
index 36f1c4adb..e7f025ec6 100644
--- a/qemu/hw/char/debugcon.c
+++ b/qemu/hw/char/debugcon.c
@@ -24,6 +24,8 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "sysemu/char.h"
#include "hw/isa/isa.h"
diff --git a/qemu/hw/char/digic-uart.c b/qemu/hw/char/digic-uart.c
index 6d44576ff..d3bc533d7 100644
--- a/qemu/hw/char/digic-uart.c
+++ b/qemu/hw/char/digic-uart.c
@@ -26,6 +26,7 @@
*
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "sysemu/char.h"
diff --git a/qemu/hw/char/escc.c b/qemu/hw/char/escc.c
index ba653efd6..7bf09a007 100644
--- a/qemu/hw/char/escc.c
+++ b/qemu/hw/char/escc.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "hw/char/escc.h"
@@ -714,7 +715,7 @@ MemoryRegion *escc_init(hwaddr base, qemu_irq irqA, qemu_irq irqB,
return &d->mmio;
}
-static const uint8_t qcode_to_keycode[Q_KEY_CODE_MAX] = {
+static const uint8_t qcode_to_keycode[Q_KEY_CODE__MAX] = {
[Q_KEY_CODE_SHIFT] = 99,
[Q_KEY_CODE_SHIFT_R] = 110,
[Q_KEY_CODE_ALT] = 19,
@@ -841,14 +842,16 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src,
{
ChannelState *s = (ChannelState *)dev;
int qcode, keycode;
+ InputKeyEvent *key;
- assert(evt->kind == INPUT_EVENT_KIND_KEY);
- qcode = qemu_input_key_value_to_qcode(evt->key->key);
+ assert(evt->type == INPUT_EVENT_KIND_KEY);
+ key = evt->u.key.data;
+ qcode = qemu_input_key_value_to_qcode(key->key);
trace_escc_sunkbd_event_in(qcode, QKeyCode_lookup[qcode],
- evt->key->down);
+ key->down);
if (qcode == Q_KEY_CODE_CAPS_LOCK) {
- if (evt->key->down) {
+ if (key->down) {
s->caps_lock_mode ^= 1;
if (s->caps_lock_mode == 2) {
return; /* Drop second press */
@@ -862,7 +865,7 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src,
}
if (qcode == Q_KEY_CODE_NUM_LOCK) {
- if (evt->key->down) {
+ if (key->down) {
s->num_lock_mode ^= 1;
if (s->num_lock_mode == 2) {
return; /* Drop second press */
@@ -876,7 +879,7 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src,
}
keycode = qcode_to_keycode[qcode];
- if (!evt->key->down) {
+ if (!key->down) {
keycode |= 0x80;
}
trace_escc_sunkbd_event_out(keycode);
@@ -1035,6 +1038,7 @@ static void escc_class_init(ObjectClass *klass, void *data)
dc->reset = escc_reset;
dc->vmsd = &vmstate_escc;
dc->props = escc_properties;
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
}
static const TypeInfo escc_info = {
diff --git a/qemu/hw/char/etraxfs_ser.c b/qemu/hw/char/etraxfs_ser.c
index 857c13621..146b387e7 100644
--- a/qemu/hw/char/etraxfs_ser.c
+++ b/qemu/hw/char/etraxfs_ser.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "sysemu/char.h"
#include "qemu/log.h"
@@ -165,7 +166,7 @@ static void serial_receive(void *opaque, const uint8_t *buf, int size)
/* Got a byte. */
if (s->rx_fifo_len >= 16) {
- qemu_log("WARNING: UART dropped char.\n");
+ D(qemu_log("WARNING: UART dropped char.\n"));
return;
}
@@ -182,15 +183,13 @@ static void serial_receive(void *opaque, const uint8_t *buf, int size)
static int serial_can_receive(void *opaque)
{
ETRAXSerial *s = opaque;
- int r;
/* Is the receiver enabled? */
if (!(s->regs[RW_REC_CTRL] & (1 << 3))) {
return 0;
}
- r = sizeof(s->rx_fifo) - s->rx_fifo_len;
- return r;
+ return sizeof(s->rx_fifo) - s->rx_fifo_len;
}
static void serial_event(void *opaque, int event)
diff --git a/qemu/hw/char/exynos4210_uart.c b/qemu/hw/char/exynos4210_uart.c
index 7614e5860..885ecc027 100644
--- a/qemu/hw/char/exynos4210_uart.c
+++ b/qemu/hw/char/exynos4210_uart.c
@@ -19,7 +19,9 @@
*
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
+#include "qemu/error-report.h"
#include "sysemu/sysemu.h"
#include "sysemu/char.h"
@@ -234,10 +236,8 @@ static int fifo_empty_elements_number(Exynos4210UartFIFO *q)
static void fifo_reset(Exynos4210UartFIFO *q)
{
- if (q->data != NULL) {
- g_free(q->data);
- q->data = NULL;
- }
+ g_free(q->data);
+ q->data = NULL;
q->data = (uint8_t *)g_malloc0(q->size);
@@ -597,15 +597,17 @@ DeviceState *exynos4210_uart_create(hwaddr addr,
if (!chr) {
if (channel >= MAX_SERIAL_PORTS) {
- hw_error("Only %d serial ports are supported by QEMU.\n",
- MAX_SERIAL_PORTS);
+ error_report("Only %d serial ports are supported by QEMU",
+ MAX_SERIAL_PORTS);
+ exit(1);
}
chr = serial_hds[channel];
if (!chr) {
snprintf(label, ARRAY_SIZE(label), "%s%d", chr_name, channel);
chr = qemu_chr_new(label, "null", NULL);
if (!(chr)) {
- hw_error("Can't assign serial port to UART%d.\n", channel);
+ error_report("Can't assign serial port to UART%d", channel);
+ exit(1);
}
}
}
diff --git a/qemu/hw/char/grlib_apbuart.c b/qemu/hw/char/grlib_apbuart.c
index 35ef66177..871524c82 100644
--- a/qemu/hw/char/grlib_apbuart.c
+++ b/qemu/hw/char/grlib_apbuart.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "sysemu/char.h"
diff --git a/qemu/hw/char/imx_serial.c b/qemu/hw/char/imx_serial.c
index f3fbc776b..6df74ac7c 100644
--- a/qemu/hw/char/imx_serial.c
+++ b/qemu/hw/char/imx_serial.c
@@ -4,6 +4,7 @@
* Copyright (c) 2008 OKL
* Originally Written by Hans Jiang
* Copyright (c) 2011 NICTA Pty Ltd.
+ * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
@@ -17,63 +18,25 @@
* is a real serial device.
*/
-#include "hw/hw.h"
-#include "hw/sysbus.h"
+#include "qemu/osdep.h"
+#include "hw/char/imx_serial.h"
#include "sysemu/sysemu.h"
#include "sysemu/char.h"
-#include "hw/arm/imx.h"
-//#define DEBUG_SERIAL 1
-#ifdef DEBUG_SERIAL
-#define DPRINTF(fmt, args...) \
-do { printf("imx_serial: " fmt , ##args); } while (0)
-#else
-#define DPRINTF(fmt, args...) do {} while (0)
-#endif
-
-/*
- * Define to 1 for messages about attempts to
- * access unimplemented registers or similar.
- */
-//#define DEBUG_IMPLEMENTATION 1
-#ifdef DEBUG_IMPLEMENTATION
-# define IPRINTF(fmt, args...) \
- do { fprintf(stderr, "imx_serial: " fmt, ##args); } while (0)
-#else
-# define IPRINTF(fmt, args...) do {} while (0)
+#ifndef DEBUG_IMX_UART
+#define DEBUG_IMX_UART 0
#endif
-#define TYPE_IMX_SERIAL "imx-serial"
-#define IMX_SERIAL(obj) OBJECT_CHECK(IMXSerialState, (obj), TYPE_IMX_SERIAL)
-
-typedef struct IMXSerialState {
- SysBusDevice parent_obj;
-
- MemoryRegion iomem;
- int32_t readbuff;
-
- uint32_t usr1;
- uint32_t usr2;
- uint32_t ucr1;
- uint32_t ucr2;
- uint32_t uts1;
-
- /*
- * The registers below are implemented just so that the
- * guest OS sees what it has written
- */
- uint32_t onems;
- uint32_t ufcr;
- uint32_t ubmr;
- uint32_t ubrc;
- uint32_t ucr3;
-
- qemu_irq irq;
- CharDriverState *chr;
-} IMXSerialState;
+#define DPRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX_UART) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_SERIAL, \
+ __func__, ##args); \
+ } \
+ } while (0)
static const VMStateDescription vmstate_imx_serial = {
- .name = "imx-serial",
+ .name = TYPE_IMX_SERIAL,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
@@ -91,61 +54,14 @@ static const VMStateDescription vmstate_imx_serial = {
},
};
-
-#define URXD_CHARRDY (1<<15) /* character read is valid */
-#define URXD_ERR (1<<14) /* Character has error */
-#define URXD_BRK (1<<11) /* Break received */
-
-#define USR1_PARTYER (1<<15) /* Parity Error */
-#define USR1_RTSS (1<<14) /* RTS pin status */
-#define USR1_TRDY (1<<13) /* Tx ready */
-#define USR1_RTSD (1<<12) /* RTS delta: pin changed state */
-#define USR1_ESCF (1<<11) /* Escape sequence interrupt */
-#define USR1_FRAMERR (1<<10) /* Framing error */
-#define USR1_RRDY (1<<9) /* receiver ready */
-#define USR1_AGTIM (1<<8) /* Aging timer interrupt */
-#define USR1_DTRD (1<<7) /* DTR changed */
-#define USR1_RXDS (1<<6) /* Receiver is idle */
-#define USR1_AIRINT (1<<5) /* Aysnch IR interrupt */
-#define USR1_AWAKE (1<<4) /* Falling edge detected on RXd pin */
-
-#define USR2_ADET (1<<15) /* Autobaud complete */
-#define USR2_TXFE (1<<14) /* Transmit FIFO empty */
-#define USR2_DTRF (1<<13) /* DTR/DSR transition */
-#define USR2_IDLE (1<<12) /* UART has been idle for too long */
-#define USR2_ACST (1<<11) /* Autobaud counter stopped */
-#define USR2_RIDELT (1<<10) /* Ring Indicator delta */
-#define USR2_RIIN (1<<9) /* Ring Indicator Input */
-#define USR2_IRINT (1<<8) /* Serial Infrared Interrupt */
-#define USR2_WAKE (1<<7) /* Start bit detected */
-#define USR2_DCDDELT (1<<6) /* Data Carrier Detect delta */
-#define USR2_DCDIN (1<<5) /* Data Carrier Detect Input */
-#define USR2_RTSF (1<<4) /* RTS transition */
-#define USR2_TXDC (1<<3) /* Transmission complete */
-#define USR2_BRCD (1<<2) /* Break condition detected */
-#define USR2_ORE (1<<1) /* Overrun error */
-#define USR2_RDR (1<<0) /* Receive data ready */
-
-#define UCR1_TRDYEN (1<<13) /* Tx Ready Interrupt Enable */
-#define UCR1_RRDYEN (1<<9) /* Rx Ready Interrupt Enable */
-#define UCR1_TXMPTYEN (1<<6) /* Tx Empty Interrupt Enable */
-#define UCR1_UARTEN (1<<0) /* UART Enable */
-
-#define UCR2_TXEN (1<<2) /* Transmitter enable */
-#define UCR2_RXEN (1<<1) /* Receiver enable */
-#define UCR2_SRST (1<<0) /* Reset complete */
-
-#define UTS1_TXEMPTY (1<<6)
-#define UTS1_RXEMPTY (1<<5)
-#define UTS1_TXFULL (1<<4)
-#define UTS1_RXFULL (1<<3)
-
static void imx_update(IMXSerialState *s)
{
uint32_t flags;
flags = (s->usr1 & s->ucr1) & (USR1_TRDY|USR1_RRDY);
- if (!(s->ucr1 & UCR1_TXMPTYEN)) {
+ if (s->ucr1 & UCR1_TXMPTYEN) {
+ flags |= (s->uts1 & UTS1_TXEMPTY);
+ } else {
flags &= ~USR1_TRDY;
}
@@ -192,7 +108,8 @@ static uint64_t imx_serial_read(void *opaque, hwaddr offset,
IMXSerialState *s = (IMXSerialState *)opaque;
uint32_t c;
- DPRINTF("read(offset=%x)\n", offset >> 2);
+ DPRINTF("read(offset=0x%" HWADDR_PRIx ")\n", offset);
+
switch (offset >> 2) {
case 0x0: /* URXD */
c = s->readbuff;
@@ -203,7 +120,9 @@ static uint64_t imx_serial_read(void *opaque, hwaddr offset,
s->usr2 &= ~USR2_RDR;
s->uts1 |= UTS1_RXEMPTY;
imx_update(s);
- qemu_chr_accept_input(s->chr);
+ if (s->chr) {
+ qemu_chr_accept_input(s->chr);
+ }
}
return c;
@@ -242,20 +161,20 @@ static uint64_t imx_serial_read(void *opaque, hwaddr offset,
return 0x0; /* TODO */
default:
- IPRINTF("imx_serial_read: bad offset: 0x%x\n", (int)offset);
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_SERIAL, __func__, offset);
return 0;
}
}
static void imx_serial_write(void *opaque, hwaddr offset,
- uint64_t value, unsigned size)
+ uint64_t value, unsigned size)
{
IMXSerialState *s = (IMXSerialState *)opaque;
unsigned char ch;
- DPRINTF("write(offset=%x, value = %x) to %s\n",
- offset >> 2,
- (unsigned int)value, s->chr ? s->chr->label : "NODEV");
+ DPRINTF("write(offset=0x%" HWADDR_PRIx ", value = 0x%x) to %s\n",
+ offset, (unsigned int)value, s->chr ? s->chr->label : "NODEV");
switch (offset >> 2) {
case 0x10: /* UTXD */
@@ -273,7 +192,9 @@ static void imx_serial_write(void *opaque, hwaddr offset,
case 0x20: /* UCR1 */
s->ucr1 = value & 0xffff;
+
DPRINTF("write(ucr1=%x)\n", (unsigned int)value);
+
imx_update(s);
break;
@@ -290,7 +211,9 @@ static void imx_serial_write(void *opaque, hwaddr offset,
}
if (value & UCR2_RXEN) {
if (!(s->ucr2 & UCR2_RXEN)) {
- qemu_chr_accept_input(s->chr);
+ if (s->chr) {
+ qemu_chr_accept_input(s->chr);
+ }
}
}
s->ucr2 = value & 0xffff;
@@ -298,25 +221,25 @@ static void imx_serial_write(void *opaque, hwaddr offset,
case 0x25: /* USR1 */
value &= USR1_AWAKE | USR1_AIRINT | USR1_DTRD | USR1_AGTIM |
- USR1_FRAMERR | USR1_ESCF | USR1_RTSD | USR1_PARTYER;
+ USR1_FRAMERR | USR1_ESCF | USR1_RTSD | USR1_PARTYER;
s->usr1 &= ~value;
break;
case 0x26: /* USR2 */
- /*
- * Writing 1 to some bits clears them; all other
- * values are ignored
- */
+ /*
+ * Writing 1 to some bits clears them; all other
+ * values are ignored
+ */
value &= USR2_ADET | USR2_DTRF | USR2_IDLE | USR2_ACST |
- USR2_RIDELT | USR2_IRINT | USR2_WAKE |
- USR2_DCDDELT | USR2_RTSF | USR2_BRCD | USR2_ORE;
+ USR2_RIDELT | USR2_IRINT | USR2_WAKE |
+ USR2_DCDDELT | USR2_RTSF | USR2_BRCD | USR2_ORE;
s->usr2 &= ~value;
break;
- /*
- * Linux expects to see what it writes to these registers
- * We don't currently alter the baud rate
- */
+ /*
+ * Linux expects to see what it writes to these registers
+ * We don't currently alter the baud rate
+ */
case 0x29: /* UBIR */
s->ubrc = value & 0xffff;
break;
@@ -339,12 +262,14 @@ static void imx_serial_write(void *opaque, hwaddr offset,
case 0x2d: /* UTS1 */
case 0x23: /* UCR4 */
- IPRINTF("Unimplemented Register %x written to\n", offset >> 2);
+ qemu_log_mask(LOG_UNIMP, "[%s]%s: Unimplemented reg 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_SERIAL, __func__, offset);
/* TODO */
break;
default:
- IPRINTF("imx_serial_write: Bad offset 0x%x\n", (int)offset);
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_SERIAL, __func__, offset);
}
}
@@ -357,7 +282,9 @@ static int imx_can_receive(void *opaque)
static void imx_put_data(void *opaque, uint32_t value)
{
IMXSerialState *s = (IMXSerialState *)opaque;
+
DPRINTF("received char\n");
+
s->usr1 |= USR1_RRDY;
s->usr2 |= USR2_RDR;
s->uts1 &= ~UTS1_RXEMPTY;
@@ -384,62 +311,30 @@ static const struct MemoryRegionOps imx_serial_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static int imx_serial_init(SysBusDevice *dev)
+static void imx_serial_realize(DeviceState *dev, Error **errp)
{
IMXSerialState *s = IMX_SERIAL(dev);
-
- memory_region_init_io(&s->iomem, OBJECT(s), &imx_serial_ops, s,
- "imx-serial", 0x1000);
- sysbus_init_mmio(dev, &s->iomem);
- sysbus_init_irq(dev, &s->irq);
-
if (s->chr) {
qemu_chr_add_handlers(s->chr, imx_can_receive, imx_receive,
imx_event, s);
} else {
- DPRINTF("No char dev for uart at 0x%lx\n",
- (unsigned long)s->iomem.ram_addr);
+ DPRINTF("No char dev for uart\n");
}
-
- return 0;
}
-void imx_serial_create(int uart, const hwaddr addr, qemu_irq irq)
+static void imx_serial_init(Object *obj)
{
- DeviceState *dev;
- SysBusDevice *bus;
- CharDriverState *chr;
- const char chr_name[] = "serial";
- char label[ARRAY_SIZE(chr_name) + 1];
-
- dev = qdev_create(NULL, TYPE_IMX_SERIAL);
-
- if (uart >= MAX_SERIAL_PORTS) {
- hw_error("Cannot assign uart %d: QEMU supports only %d ports\n",
- uart, MAX_SERIAL_PORTS);
- }
- chr = serial_hds[uart];
- if (!chr) {
- snprintf(label, ARRAY_SIZE(label), "%s%d", chr_name, uart);
- chr = qemu_chr_new(label, "null", NULL);
- if (!(chr)) {
- hw_error("Can't assign serial port to imx-uart%d.\n", uart);
- }
- }
-
- qdev_prop_set_chr(dev, "chardev", chr);
- bus = SYS_BUS_DEVICE(dev);
- qdev_init_nofail(dev);
- if (addr != (hwaddr)-1) {
- sysbus_mmio_map(bus, 0, addr);
- }
- sysbus_connect_irq(bus, 0, irq);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ IMXSerialState *s = IMX_SERIAL(obj);
+ memory_region_init_io(&s->iomem, obj, &imx_serial_ops, s,
+ TYPE_IMX_SERIAL, 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->irq);
}
-
-static Property imx32_serial_properties[] = {
+static Property imx_serial_properties[] = {
DEFINE_PROP_CHR("chardev", IMXSerialState, chr),
DEFINE_PROP_END_OF_LIST(),
};
@@ -447,21 +342,21 @@ static Property imx32_serial_properties[] = {
static void imx_serial_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = imx_serial_init;
+ dc->realize = imx_serial_realize;
dc->vmsd = &vmstate_imx_serial;
dc->reset = imx_serial_reset_at_boot;
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
dc->desc = "i.MX series UART";
- dc->props = imx32_serial_properties;
+ dc->props = imx_serial_properties;
}
static const TypeInfo imx_serial_info = {
- .name = TYPE_IMX_SERIAL,
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(IMXSerialState),
- .class_init = imx_serial_class_init,
+ .name = TYPE_IMX_SERIAL,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(IMXSerialState),
+ .instance_init = imx_serial_init,
+ .class_init = imx_serial_class_init,
};
static void imx_serial_register_types(void)
diff --git a/qemu/hw/char/ipoctal232.c b/qemu/hw/char/ipoctal232.c
index c8d5cdb36..bc0ae4980 100644
--- a/qemu/hw/char/ipoctal232.c
+++ b/qemu/hw/char/ipoctal232.c
@@ -8,6 +8,7 @@
* later version.
*/
+#include "qemu/osdep.h"
#include "hw/ipack/ipack.h"
#include "qemu/bitops.h"
#include "sysemu/char.h"
diff --git a/qemu/hw/char/lm32_juart.c b/qemu/hw/char/lm32_juart.c
index 62763f2f4..5bf8acfe8 100644
--- a/qemu/hw/char/lm32_juart.c
+++ b/qemu/hw/char/lm32_juart.c
@@ -17,6 +17,7 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "trace.h"
diff --git a/qemu/hw/char/lm32_uart.c b/qemu/hw/char/lm32_uart.c
index 837a46e8e..036813d0f 100644
--- a/qemu/hw/char/lm32_uart.c
+++ b/qemu/hw/char/lm32_uart.c
@@ -22,6 +22,7 @@
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "trace.h"
diff --git a/qemu/hw/char/mcf_uart.c b/qemu/hw/char/mcf_uart.c
index 98fd44e66..3c0438fd7 100644
--- a/qemu/hw/char/mcf_uart.c
+++ b/qemu/hw/char/mcf_uart.c
@@ -5,6 +5,7 @@
*
* This code is licensed under the GPL
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/m68k/mcf.h"
#include "sysemu/char.h"
@@ -126,7 +127,7 @@ static void mcf_uart_do_tx(mcf_uart_state *s)
static void mcf_do_command(mcf_uart_state *s, uint8_t cmd)
{
/* Misc command. */
- switch ((cmd >> 4) & 3) {
+ switch ((cmd >> 4) & 7) {
case 0: /* No-op. */
break;
case 1: /* Reset mode register pointer. */
diff --git a/qemu/hw/char/milkymist-uart.c b/qemu/hw/char/milkymist-uart.c
index 9b89b7e63..03b36b223 100644
--- a/qemu/hw/char/milkymist-uart.c
+++ b/qemu/hw/char/milkymist-uart.c
@@ -21,6 +21,7 @@
* http://www.milkymist.org/socdoc/uart.pdf
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "trace.h"
diff --git a/qemu/hw/char/omap_uart.c b/qemu/hw/char/omap_uart.c
index 88f20943e..415bec5fa 100644
--- a/qemu/hw/char/omap_uart.c
+++ b/qemu/hw/char/omap_uart.c
@@ -17,6 +17,7 @@
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "sysemu/char.h"
#include "hw/hw.h"
#include "hw/arm/omap.h"
@@ -55,8 +56,7 @@ struct omap_uart_s *omap_uart_init(hwaddr base,
qemu_irq txdma, qemu_irq rxdma,
const char *label, CharDriverState *chr)
{
- struct omap_uart_s *s = (struct omap_uart_s *)
- g_malloc0(sizeof(struct omap_uart_s));
+ struct omap_uart_s *s = g_new0(struct omap_uart_s, 1);
s->base = base;
s->fclk = fclk;
diff --git a/qemu/hw/char/parallel.c b/qemu/hw/char/parallel.c
index c2b553f0d..11c78fed8 100644
--- a/qemu/hw/char/parallel.c
+++ b/qemu/hw/char/parallel.c
@@ -22,6 +22,8 @@
* 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/hw.h"
#include "sysemu/char.h"
#include "hw/isa/isa.h"
diff --git a/qemu/hw/char/pl011.c b/qemu/hw/char/pl011.c
index eac6fac09..210c87b4c 100644
--- a/qemu/hw/char/pl011.c
+++ b/qemu/hw/char/pl011.c
@@ -7,6 +7,7 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "sysemu/char.h"
diff --git a/qemu/hw/char/sclpconsole-lm.c b/qemu/hw/char/sclpconsole-lm.c
index 02ac80b65..7d4ff8120 100644
--- a/qemu/hw/char/sclpconsole-lm.c
+++ b/qemu/hw/char/sclpconsole-lm.c
@@ -13,6 +13,7 @@
*
*/
+#include "qemu/osdep.h"
#include "hw/qdev.h"
#include "qemu/thread.h"
#include "qemu/error-report.h"
diff --git a/qemu/hw/char/sclpconsole.c b/qemu/hw/char/sclpconsole.c
index b014c7f52..45997ff4a 100644
--- a/qemu/hw/char/sclpconsole.c
+++ b/qemu/hw/char/sclpconsole.c
@@ -12,6 +12,7 @@
*
*/
+#include "qemu/osdep.h"
#include <hw/qdev.h>
#include "qemu/thread.h"
#include "qemu/error-report.h"
diff --git a/qemu/hw/char/serial-isa.c b/qemu/hw/char/serial-isa.c
index f3db024d0..1594ec4db 100644
--- a/qemu/hw/char/serial-isa.c
+++ b/qemu/hw/char/serial-isa.c
@@ -23,6 +23,8 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/char/serial.h"
#include "hw/isa/isa.h"
diff --git a/qemu/hw/char/serial-pci.c b/qemu/hw/char/serial-pci.c
index 1c8b9be5c..303104dd1 100644
--- a/qemu/hw/char/serial-pci.c
+++ b/qemu/hw/char/serial-pci.c
@@ -25,6 +25,8 @@
/* see docs/specs/pci-serial.txt */
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/char/serial.h"
#include "hw/pci/pci.h"
diff --git a/qemu/hw/char/serial.c b/qemu/hw/char/serial.c
index 513d73c27..6d815b5c6 100644
--- a/qemu/hw/char/serial.c
+++ b/qemu/hw/char/serial.c
@@ -23,8 +23,10 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/char/serial.h"
#include "sysemu/char.h"
+#include "qapi/error.h"
#include "qemu/timer.h"
#include "exec/address-spaces.h"
#include "qemu/error-report.h"
@@ -177,7 +179,7 @@ static void serial_update_parameters(SerialState *s)
ssp.parity = parity;
ssp.data_bits = data_bits;
ssp.stop_bits = stop_bits;
- s->char_transmit_time = (get_ticks_per_sec() / speed) * frame_size;
+ s->char_transmit_time = (NANOSECONDS_PER_SECOND / speed) * frame_size;
qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
DPRINTF("speed=%d parity=%c data=%d stop=%d\n",
@@ -215,8 +217,10 @@ static void serial_update_msl(SerialState *s)
/* The real 16550A apparently has a 250ns response latency to line status changes.
We'll be lazy and poll only every 10ms, and only poll it at all if MSI interrupts are turned on */
- if (s->poll_msl)
- timer_mod(s->modem_status_poll, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + get_ticks_per_sec() / 100);
+ if (s->poll_msl) {
+ timer_mod(s->modem_status_poll, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ NANOSECONDS_PER_SECOND / 100);
+ }
}
static gboolean serial_xmit(GIOChannel *chan, GIOCondition cond, void *opaque)
@@ -822,7 +826,7 @@ static void serial_reset(void *opaque)
s->mcr = UART_MCR_OUT2;
s->scr = 0;
s->tsr_retry = 0;
- s->char_transmit_time = (get_ticks_per_sec() / 9600) * 10;
+ s->char_transmit_time = (NANOSECONDS_PER_SECOND / 9600) * 10;
s->poll_msl = 0;
s->timeout_ipending = 0;
@@ -888,18 +892,13 @@ SerialState *serial_init(int base, qemu_irq irq, int baudbase,
CharDriverState *chr, MemoryRegion *system_io)
{
SerialState *s;
- Error *err = NULL;
s = g_malloc0(sizeof(SerialState));
s->irq = irq;
s->baudbase = baudbase;
s->chr = chr;
- serial_realize_core(s, &err);
- if (err != NULL) {
- error_report_err(err);
- exit(1);
- }
+ serial_realize_core(s, &error_fatal);
vmstate_register(NULL, base, &vmstate_serial, s);
@@ -949,7 +948,6 @@ SerialState *serial_mm_init(MemoryRegion *address_space,
CharDriverState *chr, enum device_endian end)
{
SerialState *s;
- Error *err = NULL;
s = g_malloc0(sizeof(SerialState));
@@ -958,11 +956,7 @@ SerialState *serial_mm_init(MemoryRegion *address_space,
s->baudbase = baudbase;
s->chr = chr;
- serial_realize_core(s, &err);
- if (err != NULL) {
- error_report_err(err);
- exit(1);
- }
+ serial_realize_core(s, &error_fatal);
vmstate_register(NULL, base, &vmstate_serial, s);
memory_region_init_io(&s->io, NULL, &serial_mm_ops[end], s,
diff --git a/qemu/hw/char/sh_serial.c b/qemu/hw/char/sh_serial.c
index 9328dd1b5..4c55dcb7d 100644
--- a/qemu/hw/char/sh_serial.c
+++ b/qemu/hw/char/sh_serial.c
@@ -24,6 +24,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sh4/sh.h"
#include "sysemu/char.h"
diff --git a/qemu/hw/char/spapr_vty.c b/qemu/hw/char/spapr_vty.c
index 36b328b9a..3498d7b05 100644
--- a/qemu/hw/char/spapr_vty.c
+++ b/qemu/hw/char/spapr_vty.c
@@ -1,3 +1,7 @@
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/qdev.h"
#include "sysemu/char.h"
#include "hw/ppc/spapr.h"
diff --git a/qemu/hw/char/stm32f2xx_usart.c b/qemu/hw/char/stm32f2xx_usart.c
index c9d3a1be7..a94d61ceb 100644
--- a/qemu/hw/char/stm32f2xx_usart.c
+++ b/qemu/hw/char/stm32f2xx_usart.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/char/stm32f2xx_usart.h"
#ifndef STM_USART_ERR_DEBUG
diff --git a/qemu/hw/char/virtio-console.c b/qemu/hw/char/virtio-console.c
index 2a867cb4e..2e36481a7 100644
--- a/qemu/hw/char/virtio-console.c
+++ b/qemu/hw/char/virtio-console.c
@@ -10,6 +10,7 @@
* the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
#include "sysemu/char.h"
#include "qemu/error-report.h"
#include "trace.h"
diff --git a/qemu/hw/char/virtio-serial-bus.c b/qemu/hw/char/virtio-serial-bus.c
index bc56f5d52..6e5de6dec 100644
--- a/qemu/hw/char/virtio-serial-bus.c
+++ b/qemu/hw/char/virtio-serial-bus.c
@@ -18,6 +18,8 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu/iov.h"
#include "monitor/monitor.h"
#include "qemu/error-report.h"
@@ -76,13 +78,13 @@ static VirtIOSerialPort *find_port_by_name(char *name)
static bool use_multiport(VirtIOSerial *vser)
{
VirtIODevice *vdev = VIRTIO_DEVICE(vser);
- return virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT);
+ return virtio_vdev_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT);
}
static size_t write_to_port(VirtIOSerialPort *port,
const uint8_t *buf, size_t size)
{
- VirtQueueElement elem;
+ VirtQueueElement *elem;
VirtQueue *vq;
size_t offset;
@@ -95,15 +97,17 @@ static size_t write_to_port(VirtIOSerialPort *port,
while (offset < size) {
size_t len;
- if (!virtqueue_pop(vq, &elem)) {
+ elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
+ if (!elem) {
break;
}
- len = iov_from_buf(elem.in_sg, elem.in_num, 0,
+ len = iov_from_buf(elem->in_sg, elem->in_num, 0,
buf + offset, size - offset);
offset += len;
- virtqueue_push(vq, &elem, len);
+ virtqueue_push(vq, elem, len);
+ g_free(elem);
}
virtio_notify(VIRTIO_DEVICE(port->vser), vq);
@@ -112,13 +116,18 @@ static size_t write_to_port(VirtIOSerialPort *port,
static void discard_vq_data(VirtQueue *vq, VirtIODevice *vdev)
{
- VirtQueueElement elem;
+ VirtQueueElement *elem;
if (!virtio_queue_ready(vq)) {
return;
}
- while (virtqueue_pop(vq, &elem)) {
- virtqueue_push(vq, &elem, 0);
+ for (;;) {
+ elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
+ if (!elem) {
+ break;
+ }
+ virtqueue_push(vq, elem, 0);
+ g_free(elem);
}
virtio_notify(vdev, vq);
}
@@ -137,21 +146,22 @@ static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq,
unsigned int i;
/* Pop an elem only if we haven't left off a previous one mid-way */
- if (!port->elem.out_num) {
- if (!virtqueue_pop(vq, &port->elem)) {
+ if (!port->elem) {
+ port->elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
+ if (!port->elem) {
break;
}
port->iov_idx = 0;
port->iov_offset = 0;
}
- for (i = port->iov_idx; i < port->elem.out_num; i++) {
+ for (i = port->iov_idx; i < port->elem->out_num; i++) {
size_t buf_size;
ssize_t ret;
- buf_size = port->elem.out_sg[i].iov_len - port->iov_offset;
+ buf_size = port->elem->out_sg[i].iov_len - port->iov_offset;
ret = vsc->have_data(port,
- port->elem.out_sg[i].iov_base
+ port->elem->out_sg[i].iov_base
+ port->iov_offset,
buf_size);
if (port->throttled) {
@@ -166,8 +176,9 @@ static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq,
if (port->throttled) {
break;
}
- virtqueue_push(vq, &port->elem, 0);
- port->elem.out_num = 0;
+ virtqueue_push(vq, port->elem, 0);
+ g_free(port->elem);
+ port->elem = NULL;
}
virtio_notify(vdev, vq);
}
@@ -184,22 +195,26 @@ static void flush_queued_data(VirtIOSerialPort *port)
static size_t send_control_msg(VirtIOSerial *vser, void *buf, size_t len)
{
- VirtQueueElement elem;
+ VirtQueueElement *elem;
VirtQueue *vq;
vq = vser->c_ivq;
if (!virtio_queue_ready(vq)) {
return 0;
}
- if (!virtqueue_pop(vq, &elem)) {
+
+ elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
+ if (!elem) {
return 0;
}
/* TODO: detect a buffer that's too short, set NEEDS_RESET */
- iov_from_buf(elem.in_sg, elem.in_num, 0, buf, len);
+ iov_from_buf(elem->in_sg, elem->in_num, 0, buf, len);
- virtqueue_push(vq, &elem, len);
+ virtqueue_push(vq, elem, len);
virtio_notify(VIRTIO_DEVICE(vser), vq);
+ g_free(elem);
+
return len;
}
@@ -413,7 +428,7 @@ static void control_in(VirtIODevice *vdev, VirtQueue *vq)
static void control_out(VirtIODevice *vdev, VirtQueue *vq)
{
- VirtQueueElement elem;
+ VirtQueueElement *elem;
VirtIOSerial *vser;
uint8_t *buf;
size_t len;
@@ -422,10 +437,15 @@ static void control_out(VirtIODevice *vdev, VirtQueue *vq)
len = 0;
buf = NULL;
- while (virtqueue_pop(vq, &elem)) {
+ for (;;) {
size_t cur_len;
- cur_len = iov_size(elem.out_sg, elem.out_num);
+ elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
+ if (!elem) {
+ break;
+ }
+
+ cur_len = iov_size(elem->out_sg, elem->out_num);
/*
* Allocate a new buf only if we didn't have one previously or
* if the size of the buf differs
@@ -436,10 +456,11 @@ static void control_out(VirtIODevice *vdev, VirtQueue *vq)
buf = g_malloc(cur_len);
len = cur_len;
}
- iov_to_buf(elem.out_sg, elem.out_num, 0, buf, cur_len);
+ iov_to_buf(elem->out_sg, elem->out_num, 0, buf, cur_len);
handle_control_message(vser, buf, cur_len);
- virtqueue_push(vq, &elem, 0);
+ virtqueue_push(vq, elem, 0);
+ g_free(elem);
}
g_free(buf);
virtio_notify(vdev, vq);
@@ -619,16 +640,14 @@ static void virtio_serial_save_device(VirtIODevice *vdev, QEMUFile *f)
qemu_put_byte(f, port->host_connected);
elem_popped = 0;
- if (port->elem.out_num) {
+ if (port->elem) {
elem_popped = 1;
}
qemu_put_be32s(f, &elem_popped);
if (elem_popped) {
qemu_put_be32s(f, &port->iov_idx);
qemu_put_be64s(f, &port->iov_offset);
-
- qemu_put_buffer(f, (unsigned char *)&port->elem,
- sizeof(port->elem));
+ qemu_put_virtqueue_element(f, port->elem);
}
}
}
@@ -703,12 +722,8 @@ static int fetch_active_ports_list(QEMUFile *f, int version_id,
qemu_get_be32s(f, &port->iov_idx);
qemu_get_be64s(f, &port->iov_offset);
- qemu_get_buffer(f, (unsigned char *)&port->elem,
- sizeof(port->elem));
- virtqueue_map_sg(port->elem.in_sg, port->elem.in_addr,
- port->elem.in_num, 1);
- virtqueue_map_sg(port->elem.out_sg, port->elem.out_addr,
- port->elem.out_num, 1);
+ port->elem =
+ qemu_get_virtqueue_element(f, sizeof(VirtQueueElement));
/*
* Port was throttled on source machine. Let's
@@ -801,7 +816,7 @@ static const TypeInfo virtser_bus_info = {
static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent)
{
- VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, qdev);
+ VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(qdev);
monitor_printf(mon, "%*sport %d, guest %s, host %s, throttle %s\n",
indent, "", port->id,
@@ -930,7 +945,7 @@ static void virtser_port_device_realize(DeviceState *dev, Error **errp)
return;
}
- port->elem.out_num = 0;
+ port->elem = NULL;
}
static void virtser_port_device_plug(HotplugHandler *hotplug_dev,
diff --git a/qemu/hw/char/xen_console.c b/qemu/hw/char/xen_console.c
index eb7f450ab..cbf1dccbb 100644
--- a/qemu/hw/char/xen_console.c
+++ b/qemu/hw/char/xen_console.c
@@ -19,14 +19,9 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
+#include "qemu/osdep.h"
#include <sys/select.h>
-#include <fcntl.h>
-#include <unistd.h>
#include <termios.h>
-#include <stdarg.h>
#include <sys/mman.h>
#include "hw/hw.h"
@@ -228,12 +223,12 @@ static int con_initialise(struct XenDevice *xendev)
con->buffer.max_capacity = limit;
if (!xendev->dev) {
- con->sring = xc_map_foreign_range(xen_xc, con->xendev.dom,
- XC_PAGE_SIZE,
+ xen_pfn_t mfn = con->ring_ref;
+ con->sring = xenforeignmemory_map(xen_fmem, con->xendev.dom,
PROT_READ|PROT_WRITE,
- con->ring_ref);
+ 1, &mfn, NULL);
} else {
- con->sring = xc_gnttab_map_grant_ref(xendev->gnttabdev, con->xendev.dom,
+ con->sring = xengnttab_map_grant_ref(xendev->gnttabdev, con->xendev.dom,
con->ring_ref,
PROT_READ|PROT_WRITE);
}
@@ -265,9 +260,6 @@ static void con_disconnect(struct XenDevice *xendev)
{
struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
- if (!xendev->dev) {
- return;
- }
if (con->chr) {
qemu_chr_add_handlers(con->chr, NULL, NULL, NULL, NULL);
qemu_chr_fe_release(con->chr);
@@ -275,12 +267,12 @@ static void con_disconnect(struct XenDevice *xendev)
xen_be_unbind_evtchn(&con->xendev);
if (con->sring) {
- if (!xendev->gnttabdev) {
- munmap(con->sring, XC_PAGE_SIZE);
+ if (!xendev->dev) {
+ xenforeignmemory_unmap(xen_fmem, con->sring, 1);
} else {
- xc_gnttab_munmap(xendev->gnttabdev, con->sring, 1);
+ xengnttab_unmap(xendev->gnttabdev, con->sring, 1);
}
- con->sring = NULL;
+ con->sring = NULL;
}
}
diff --git a/qemu/hw/char/xilinx_uartlite.c b/qemu/hw/char/xilinx_uartlite.c
index ef883a898..911af4a0d 100644
--- a/qemu/hw/char/xilinx_uartlite.c
+++ b/qemu/hw/char/xilinx_uartlite.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "sysemu/char.h"
diff --git a/qemu/hw/core/empty_slot.c b/qemu/hw/core/empty_slot.c
index 612b1093a..c1b9c2b10 100644
--- a/qemu/hw/core/empty_slot.c
+++ b/qemu/hw/core/empty_slot.c
@@ -9,6 +9,7 @@
* version.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "hw/empty_slot.h"
diff --git a/qemu/hw/core/fw-path-provider.c b/qemu/hw/core/fw-path-provider.c
index 7442d322d..33b99830e 100644
--- a/qemu/hw/core/fw-path-provider.c
+++ b/qemu/hw/core/fw-path-provider.c
@@ -15,6 +15,7 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/fw-path-provider.h"
char *fw_path_provider_get_dev_path(FWPathProvider *p, BusState *bus,
diff --git a/qemu/hw/core/hotplug.c b/qemu/hw/core/hotplug.c
index 4e0107455..645cfca1b 100644
--- a/qemu/hw/core/hotplug.c
+++ b/qemu/hw/core/hotplug.c
@@ -9,6 +9,7 @@
* 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 "hw/hotplug.h"
#include "qemu/module.h"
diff --git a/qemu/hw/core/irq.c b/qemu/hw/core/irq.c
index 8a62a36d5..49ff2e64f 100644
--- a/qemu/hw/core/irq.c
+++ b/qemu/hw/core/irq.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "hw/irq.h"
#include "qom/object.h"
diff --git a/qemu/hw/core/loader.c b/qemu/hw/core/loader.c
index 216eeeb91..c0499571c 100644
--- a/qemu/hw/core/loader.c
+++ b/qemu/hw/core/loader.c
@@ -42,6 +42,8 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "disas/disas.h"
#include "monitor/monitor.h"
@@ -51,12 +53,11 @@
#include "hw/nvram/fw_cfg.h"
#include "exec/memory.h"
#include "exec/address-spaces.h"
+#include "hw/boards.h"
+#include "qemu/cutils.h"
#include <zlib.h>
-bool option_rom_has_mr = false;
-bool rom_file_has_mr = true;
-
static int roms_loaded;
/* return the size or -1 if error */
@@ -148,6 +149,28 @@ int load_image_targphys(const char *filename,
return size;
}
+int load_image_mr(const char *filename, MemoryRegion *mr)
+{
+ int size;
+
+ if (!memory_access_is_direct(mr, false)) {
+ /* Can only load an image into RAM or ROM */
+ return -1;
+ }
+
+ size = get_image_size(filename);
+
+ if (size > memory_region_size(mr)) {
+ return -1;
+ }
+ if (size > 0) {
+ if (rom_add_file_mr(filename, mr, -1) < 0) {
+ return -1;
+ }
+ }
+ return size;
+}
+
void pstrcpy_targphys(const char *name, hwaddr dest, int buf_size,
const char *source)
{
@@ -333,10 +356,66 @@ const char *load_elf_strerror(int error)
}
}
+void load_elf_hdr(const char *filename, void *hdr, bool *is64, Error **errp)
+{
+ int fd;
+ uint8_t e_ident_local[EI_NIDENT];
+ uint8_t *e_ident;
+ size_t hdr_size, off;
+ bool is64l;
+
+ if (!hdr) {
+ hdr = e_ident_local;
+ }
+ e_ident = hdr;
+
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if (fd < 0) {
+ error_setg_errno(errp, errno, "Failed to open file: %s", filename);
+ return;
+ }
+ if (read(fd, hdr, EI_NIDENT) != EI_NIDENT) {
+ error_setg_errno(errp, errno, "Failed to read file: %s", filename);
+ goto fail;
+ }
+ if (e_ident[0] != ELFMAG0 ||
+ e_ident[1] != ELFMAG1 ||
+ e_ident[2] != ELFMAG2 ||
+ e_ident[3] != ELFMAG3) {
+ error_setg(errp, "Bad ELF magic");
+ goto fail;
+ }
+
+ is64l = e_ident[EI_CLASS] == ELFCLASS64;
+ hdr_size = is64l ? sizeof(Elf64_Ehdr) : sizeof(Elf32_Ehdr);
+ if (is64) {
+ *is64 = is64l;
+ }
+
+ off = EI_NIDENT;
+ while (hdr != e_ident_local && off < hdr_size) {
+ size_t br = read(fd, hdr + off, hdr_size - off);
+ switch (br) {
+ case 0:
+ error_setg(errp, "File too short: %s", filename);
+ goto fail;
+ case -1:
+ error_setg_errno(errp, errno, "Failed to read file: %s",
+ filename);
+ goto fail;
+ }
+ off += br;
+ }
+
+fail:
+ close(fd);
+}
+
/* return < 0 if error, otherwise the number of bytes loaded in memory */
int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t),
void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr,
- uint64_t *highaddr, int big_endian, int elf_machine, int clear_lsb)
+ uint64_t *highaddr, int big_endian, int elf_machine,
+ int clear_lsb, int data_swab)
{
int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED;
uint8_t e_ident[EI_NIDENT];
@@ -375,10 +454,12 @@ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t),
lseek(fd, 0, SEEK_SET);
if (e_ident[EI_CLASS] == ELFCLASS64) {
ret = load_elf64(filename, fd, translate_fn, translate_opaque, must_swab,
- pentry, lowaddr, highaddr, elf_machine, clear_lsb);
+ pentry, lowaddr, highaddr, elf_machine, clear_lsb,
+ data_swab);
} else {
ret = load_elf32(filename, fd, translate_fn, translate_opaque, must_swab,
- pentry, lowaddr, highaddr, elf_machine, clear_lsb);
+ pentry, lowaddr, highaddr, elf_machine, clear_lsb,
+ data_swab);
}
fail:
@@ -594,8 +675,7 @@ static int load_uboot_image(const char *filename, hwaddr *ep, hwaddr *loadaddr,
ret = hdr->ih_size;
out:
- if (data)
- g_free(data);
+ g_free(data);
close(fd);
return ret;
}
@@ -741,7 +821,7 @@ static void *rom_set_mr(Rom *rom, Object *owner, const char *name)
memory_region_init_resizeable_ram(rom->mr, owner, name,
rom->datasize, rom->romsize,
fw_cfg_resized,
- &error_abort);
+ &error_fatal);
memory_region_set_readonly(rom->mr, true);
vmstate_register_ram_global(rom->mr);
@@ -753,8 +833,9 @@ static void *rom_set_mr(Rom *rom, Object *owner, const char *name)
int rom_add_file(const char *file, const char *fw_dir,
hwaddr addr, int32_t bootindex,
- bool option_rom)
+ bool option_rom, MemoryRegion *mr)
{
+ MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
Rom *rom;
int rc, fd = -1;
char devpath[100];
@@ -811,7 +892,7 @@ int rom_add_file(const char *file, const char *fw_dir,
basename);
snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name);
- if ((!option_rom || option_rom_has_mr) && rom_file_has_mr) {
+ if ((!option_rom || mc->option_rom_has_mr) && mc->rom_file_has_mr) {
data = rom_set_mr(rom, OBJECT(fw_cfg), devpath);
} else {
data = rom->data;
@@ -819,7 +900,12 @@ int rom_add_file(const char *file, const char *fw_dir,
fw_cfg_add_file(fw_cfg, fw_file_name, data, rom->romsize);
} else {
- snprintf(devpath, sizeof(devpath), "/rom@" TARGET_FMT_plx, addr);
+ if (mr) {
+ rom->mr = mr;
+ snprintf(devpath, sizeof(devpath), "/rom@%s", file);
+ } else {
+ snprintf(devpath, sizeof(devpath), "/rom@" TARGET_FMT_plx, addr);
+ }
}
add_boot_device_path(bootindex, NULL, devpath);
@@ -839,6 +925,7 @@ MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len,
size_t max_len, hwaddr addr, const char *fw_file_name,
FWCfgReadCallback fw_callback, void *callback_opaque)
{
+ MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
Rom *rom;
MemoryRegion *mr = NULL;
@@ -856,7 +943,7 @@ MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len,
snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name);
- if (rom_file_has_mr) {
+ if (mc->rom_file_has_mr) {
data = rom_set_mr(rom, OBJECT(fw_cfg), devpath);
mr = rom->mr;
} else {
@@ -892,12 +979,12 @@ int rom_add_elf_program(const char *name, void *data, size_t datasize,
int rom_add_vga(const char *file)
{
- return rom_add_file(file, "vgaroms", 0, -1, true);
+ return rom_add_file(file, "vgaroms", 0, -1, true, NULL);
}
int rom_add_option(const char *file, int32_t bootindex)
{
- return rom_add_file(file, "genroms", 0, bootindex, true);
+ return rom_add_file(file, "genroms", 0, bootindex, true, NULL);
}
static void rom_reset(void *unused)
@@ -966,6 +1053,20 @@ void rom_set_fw(FWCfgState *f)
fw_cfg = f;
}
+void rom_set_order_override(int order)
+{
+ if (!fw_cfg)
+ return;
+ fw_cfg_set_order_override(fw_cfg, order);
+}
+
+void rom_reset_order_override(void)
+{
+ if (!fw_cfg)
+ return;
+ fw_cfg_reset_order_override(fw_cfg);
+}
+
static Rom *find_rom(hwaddr addr)
{
Rom *rom;
diff --git a/qemu/hw/core/machine.c b/qemu/hw/core/machine.c
index ac4654e9d..6dbbc85b9 100644
--- a/qemu/hw/core/machine.c
+++ b/qemu/hw/core/machine.c
@@ -10,11 +10,15 @@
* See the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
#include "hw/boards.h"
+#include "qapi/error.h"
+#include "qapi-visit.h"
#include "qapi/visitor.h"
#include "hw/sysbus.h"
#include "sysemu/sysemu.h"
#include "qemu/error-report.h"
+#include "qemu/cutils.h"
static char *machine_get_accel(Object *obj, Error **errp)
{
@@ -31,33 +35,60 @@ static void machine_set_accel(Object *obj, const char *value, Error **errp)
ms->accel = g_strdup(value);
}
-static void machine_set_kernel_irqchip(Object *obj, bool value, Error **errp)
+static void machine_set_kernel_irqchip(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
{
+ Error *err = NULL;
MachineState *ms = MACHINE(obj);
+ OnOffSplit mode;
- ms->kernel_irqchip_allowed = value;
- ms->kernel_irqchip_required = value;
+ visit_type_OnOffSplit(v, name, &mode, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ } else {
+ switch (mode) {
+ case ON_OFF_SPLIT_ON:
+ ms->kernel_irqchip_allowed = true;
+ ms->kernel_irqchip_required = true;
+ ms->kernel_irqchip_split = false;
+ break;
+ case ON_OFF_SPLIT_OFF:
+ ms->kernel_irqchip_allowed = false;
+ ms->kernel_irqchip_required = false;
+ ms->kernel_irqchip_split = false;
+ break;
+ case ON_OFF_SPLIT_SPLIT:
+ ms->kernel_irqchip_allowed = true;
+ ms->kernel_irqchip_required = true;
+ ms->kernel_irqchip_split = true;
+ break;
+ default:
+ abort();
+ }
+ }
}
static void machine_get_kvm_shadow_mem(Object *obj, Visitor *v,
- void *opaque, const char *name,
+ const char *name, void *opaque,
Error **errp)
{
MachineState *ms = MACHINE(obj);
int64_t value = ms->kvm_shadow_mem;
- visit_type_int(v, &value, name, errp);
+ visit_type_int(v, name, &value, errp);
}
static void machine_set_kvm_shadow_mem(Object *obj, Visitor *v,
- void *opaque, const char *name,
+ const char *name, void *opaque,
Error **errp)
{
MachineState *ms = MACHINE(obj);
Error *error = NULL;
int64_t value;
- visit_type_int(v, &value, name, &error);
+ visit_type_int(v, name, &value, &error);
if (error) {
error_propagate(errp, error);
return;
@@ -142,24 +173,24 @@ static void machine_set_dumpdtb(Object *obj, const char *value, Error **errp)
}
static void machine_get_phandle_start(Object *obj, Visitor *v,
- void *opaque, const char *name,
- Error **errp)
+ const char *name, void *opaque,
+ Error **errp)
{
MachineState *ms = MACHINE(obj);
int64_t value = ms->phandle_start;
- visit_type_int(v, &value, name, errp);
+ visit_type_int(v, name, &value, errp);
}
static void machine_set_phandle_start(Object *obj, Visitor *v,
- void *opaque, const char *name,
- Error **errp)
+ const char *name, void *opaque,
+ Error **errp)
{
MachineState *ms = MACHINE(obj);
Error *error = NULL;
int64_t value;
- visit_type_int(v, &value, name, &error);
+ visit_type_int(v, name, &value, &error);
if (error) {
error_propagate(errp, error);
return;
@@ -226,6 +257,20 @@ static void machine_set_usb(Object *obj, bool value, Error **errp)
ms->usb_disabled = !value;
}
+static bool machine_get_igd_gfx_passthru(Object *obj, Error **errp)
+{
+ MachineState *ms = MACHINE(obj);
+
+ return ms->igd_gfx_passthru;
+}
+
+static void machine_set_igd_gfx_passthru(Object *obj, bool value, Error **errp)
+{
+ MachineState *ms = MACHINE(obj);
+
+ ms->igd_gfx_passthru = value;
+}
+
static char *machine_get_firmware(Object *obj, Error **errp)
{
MachineState *ms = MACHINE(obj);
@@ -269,6 +314,21 @@ static bool machine_get_suppress_vmdesc(Object *obj, Error **errp)
return ms->suppress_vmdesc;
}
+static void machine_set_enforce_config_section(Object *obj, bool value,
+ Error **errp)
+{
+ MachineState *ms = MACHINE(obj);
+
+ ms->enforce_config_section = value;
+}
+
+static bool machine_get_enforce_config_section(Object *obj, Error **errp)
+{
+ MachineState *ms = MACHINE(obj);
+
+ return ms->enforce_config_section;
+}
+
static int error_on_sysbus_device(SysBusDevice *sbdev, void *opaque)
{
error_report("Option '-device %s' cannot be handled by this machine",
@@ -300,6 +360,18 @@ static void machine_class_init(ObjectClass *oc, void *data)
/* Default 128 MB as guest ram size */
mc->default_ram_size = 128 * M_BYTE;
+ mc->rom_file_has_mr = true;
+}
+
+static void machine_class_base_init(ObjectClass *oc, void *data)
+{
+ if (!object_class_is_abstract(oc)) {
+ MachineClass *mc = MACHINE_CLASS(oc);
+ const char *cname = object_class_get_name(oc);
+ assert(g_str_has_suffix(cname, TYPE_MACHINE_SUFFIX));
+ mc->name = g_strndup(cname,
+ strlen(cname) - strlen(TYPE_MACHINE_SUFFIX));
+ }
}
static void machine_initfn(Object *obj)
@@ -316,12 +388,12 @@ static void machine_initfn(Object *obj)
object_property_set_description(obj, "accel",
"Accelerator list",
NULL);
- object_property_add_bool(obj, "kernel-irqchip",
- NULL,
- machine_set_kernel_irqchip,
- NULL);
+ object_property_add(obj, "kernel-irqchip", "OnOffSplit",
+ NULL,
+ machine_set_kernel_irqchip,
+ NULL, NULL, NULL);
object_property_set_description(obj, "kernel-irqchip",
- "Use KVM in-kernel irqchip",
+ "Configure KVM in-kernel irqchip",
NULL);
object_property_add(obj, "kvm-shadow-mem", "int",
machine_get_kvm_shadow_mem,
@@ -388,6 +460,12 @@ static void machine_initfn(Object *obj)
object_property_set_description(obj, "usb",
"Set on/off to enable/disable usb",
NULL);
+ object_property_add_bool(obj, "igd-passthru",
+ machine_get_igd_gfx_passthru,
+ machine_set_igd_gfx_passthru, NULL);
+ object_property_set_description(obj, "igd-passthru",
+ "Set on/off to enable/disable igd passthrou",
+ NULL);
object_property_add_str(obj, "firmware",
machine_get_firmware,
machine_set_firmware, NULL);
@@ -406,6 +484,12 @@ static void machine_initfn(Object *obj)
object_property_set_description(obj, "suppress-vmdesc",
"Set on to disable self-describing migration",
NULL);
+ object_property_add_bool(obj, "enforce-config-section",
+ machine_get_enforce_config_section,
+ machine_set_enforce_config_section, NULL);
+ object_property_set_description(obj, "enforce-config-section",
+ "Set on to enforce configuration section migration",
+ NULL);
/* Register notifier when init is done for sysbus sanity checks */
ms->sysbus_notifier.notify = machine_init_notify;
@@ -431,11 +515,6 @@ bool machine_usb(MachineState *machine)
return machine->usb;
}
-bool machine_iommu(MachineState *machine)
-{
- return machine->iommu;
-}
-
bool machine_kernel_irqchip_allowed(MachineState *machine)
{
return machine->kernel_irqchip_allowed;
@@ -446,6 +525,11 @@ bool machine_kernel_irqchip_required(MachineState *machine)
return machine->kernel_irqchip_required;
}
+bool machine_kernel_irqchip_split(MachineState *machine)
+{
+ return machine->kernel_irqchip_split;
+}
+
int machine_kvm_shadow_mem(MachineState *machine)
{
return machine->kvm_shadow_mem;
@@ -472,6 +556,7 @@ static const TypeInfo machine_info = {
.abstract = true,
.class_size = sizeof(MachineClass),
.class_init = machine_class_init,
+ .class_base_init = machine_class_base_init,
.instance_size = sizeof(MachineState),
.instance_init = machine_initfn,
.instance_finalize = machine_finalize,
diff --git a/qemu/hw/core/nmi.c b/qemu/hw/core/nmi.c
index de1d1f8cb..e8bcc4177 100644
--- a/qemu/hw/core/nmi.c
+++ b/qemu/hw/core/nmi.c
@@ -19,13 +19,15 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/nmi.h"
+#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "monitor/monitor.h"
struct do_nmi_s {
int cpu_index;
- Error *errp;
+ Error *err;
bool handled;
};
@@ -40,8 +42,8 @@ static int do_nmi(Object *o, void *opaque)
NMIClass *nc = NMI_GET_CLASS(n);
ns->handled = true;
- nc->nmi_monitor_handler(n, ns->cpu_index, &ns->errp);
- if (ns->errp) {
+ nc->nmi_monitor_handler(n, ns->cpu_index, &ns->err);
+ if (ns->err) {
return -1;
}
}
@@ -59,13 +61,13 @@ void nmi_monitor_handle(int cpu_index, Error **errp)
{
struct do_nmi_s ns = {
.cpu_index = cpu_index,
- .errp = NULL,
+ .err = NULL,
.handled = false
};
nmi_children(object_get_root(), &ns);
if (ns.handled) {
- error_propagate(errp, ns.errp);
+ error_propagate(errp, ns.err);
} else {
error_setg(errp, QERR_UNSUPPORTED);
}
diff --git a/qemu/hw/core/null-machine.c b/qemu/hw/core/null-machine.c
index 1ec7c3bbe..0351ba782 100644
--- a/qemu/hw/core/null-machine.c
+++ b/qemu/hw/core/null-machine.c
@@ -11,6 +11,7 @@
*
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "hw/hw.h"
#include "hw/boards.h"
@@ -19,17 +20,11 @@ static void machine_none_init(MachineState *machine)
{
}
-static QEMUMachine machine_none = {
- .name = "none",
- .desc = "empty machine",
- .init = machine_none_init,
- .max_cpus = 0,
-};
-
-static void register_machines(void)
+static void machine_none_machine_init(MachineClass *mc)
{
- qemu_register_machine(&machine_none);
+ mc->desc = "empty machine";
+ mc->init = machine_none_init;
+ mc->max_cpus = 0;
}
-machine_init(register_machines);
-
+DEFINE_MACHINE("none", machine_none_machine_init)
diff --git a/qemu/hw/core/platform-bus.c b/qemu/hw/core/platform-bus.c
index 70e051890..36f84ab72 100644
--- a/qemu/hw/core/platform-bus.c
+++ b/qemu/hw/core/platform-bus.c
@@ -19,8 +19,10 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/platform-bus.h"
#include "exec/address-spaces.h"
+#include "qemu/error-report.h"
#include "sysemu/sysemu.h"
@@ -106,31 +108,29 @@ static void plaform_bus_refresh_irqs(PlatformBusDevice *pbus)
pbus->done_gathering = true;
}
-static int platform_bus_map_irq(PlatformBusDevice *pbus, SysBusDevice *sbdev,
- int n)
+static void platform_bus_map_irq(PlatformBusDevice *pbus, SysBusDevice *sbdev,
+ int n)
{
int max_irqs = pbus->num_irqs;
int irqn;
if (sysbus_is_irq_connected(sbdev, n)) {
/* IRQ is already mapped, nothing to do */
- return 0;
+ return;
}
irqn = find_first_zero_bit(pbus->used_irqs, max_irqs);
if (irqn >= max_irqs) {
- hw_error("Platform Bus: Can not fit IRQ line");
- return -1;
+ error_report("Platform Bus: Can not fit IRQ line");
+ exit(1);
}
set_bit(irqn, pbus->used_irqs);
sysbus_connect_irq(sbdev, n, pbus->irqs[irqn]);
-
- return 0;
}
-static int platform_bus_map_mmio(PlatformBusDevice *pbus, SysBusDevice *sbdev,
- int n)
+static void platform_bus_map_mmio(PlatformBusDevice *pbus, SysBusDevice *sbdev,
+ int n)
{
MemoryRegion *sbdev_mr = sysbus_mmio_get_region(sbdev, n);
uint64_t size = memory_region_size(sbdev_mr);
@@ -140,7 +140,7 @@ static int platform_bus_map_mmio(PlatformBusDevice *pbus, SysBusDevice *sbdev,
if (memory_region_is_mapped(sbdev_mr)) {
/* Region is already mapped, nothing to do */
- return 0;
+ return;
}
/*
@@ -155,13 +155,13 @@ static int platform_bus_map_mmio(PlatformBusDevice *pbus, SysBusDevice *sbdev,
}
if (!found_region) {
- hw_error("Platform Bus: Can not fit MMIO region of size %"PRIx64, size);
+ error_report("Platform Bus: Can not fit MMIO region of size %"PRIx64,
+ size);
+ exit(1);
}
/* Map the device's region into our Platform Bus MMIO space */
memory_region_add_subregion(&pbus->mmio, off, sbdev_mr);
-
- return 0;
}
/*
diff --git a/qemu/hw/core/ptimer.c b/qemu/hw/core/ptimer.c
index 8437bd6e8..153c83513 100644
--- a/qemu/hw/core/ptimer.c
+++ b/qemu/hw/core/ptimer.c
@@ -5,10 +5,12 @@
*
* This code is licensed under the GNU LGPL.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "qemu/timer.h"
#include "hw/ptimer.h"
#include "qemu/host-utils.h"
+#include "sysemu/replay.h"
struct ptimer_state
{
@@ -27,7 +29,7 @@ struct ptimer_state
static void ptimer_trigger(ptimer_state *s)
{
if (s->bh) {
- qemu_bh_schedule(s->bh);
+ replay_bh_schedule_event(s->bh);
}
}
diff --git a/qemu/hw/core/qdev-properties-system.c b/qemu/hw/core/qdev-properties-system.c
index 921e799db..891219ae0 100644
--- a/qemu/hw/core/qdev-properties-system.c
+++ b/qemu/hw/core/qdev-properties-system.c
@@ -10,8 +10,10 @@
* See the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
#include "net/net.h"
#include "hw/qdev.h"
+#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
@@ -30,7 +32,7 @@ static void get_pointer(Object *obj, Visitor *v, Property *prop,
char *p;
p = *ptr ? print(*ptr) : g_strdup("");
- visit_type_str(v, &p, name, errp);
+ visit_type_str(v, name, &p, errp);
g_free(p);
}
@@ -50,7 +52,7 @@ static void set_pointer(Object *obj, Visitor *v, Property *prop,
return;
}
- visit_type_str(v, &str, name, &local_err);
+ visit_type_str(v, name, &str, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
@@ -111,14 +113,14 @@ static char *print_drive(void *ptr)
return g_strdup(blk_name(ptr));
}
-static void get_drive(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void get_drive(Object *obj, Visitor *v, const char *name, void *opaque,
+ Error **errp)
{
get_pointer(obj, v, opaque, print_drive, name, errp);
}
-static void set_drive(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void set_drive(Object *obj, Visitor *v, const char *name, void *opaque,
+ Error **errp)
{
set_pointer(obj, v, opaque, parse_drive, name, errp);
}
@@ -172,14 +174,14 @@ static char *print_chr(void *ptr)
return g_strdup(val);
}
-static void get_chr(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void get_chr(Object *obj, Visitor *v, const char *name, void *opaque,
+ Error **errp)
{
get_pointer(obj, v, opaque, print_chr, name, errp);
}
-static void set_chr(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque,
+ Error **errp)
{
set_pointer(obj, v, opaque, parse_chr, name, errp);
}
@@ -193,20 +195,20 @@ PropertyInfo qdev_prop_chr = {
};
/* --- netdev device --- */
-static void get_netdev(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void get_netdev(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop);
char *p = g_strdup(peers_ptr->ncs[0] ? peers_ptr->ncs[0]->name : "");
- visit_type_str(v, &p, name, errp);
+ visit_type_str(v, name, &p, errp);
g_free(p);
}
-static void set_netdev(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void set_netdev(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
@@ -222,7 +224,7 @@ static void set_netdev(Object *obj, Visitor *v, void *opaque,
return;
}
- visit_type_str(v, &str, name, &local_err);
+ visit_type_str(v, name, &str, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
@@ -292,8 +294,8 @@ static int print_vlan(DeviceState *dev, Property *prop, char *dest, size_t len)
return snprintf(dest, len, "<null>");
}
-static void get_vlan(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void get_vlan(Object *obj, Visitor *v, const char *name, void *opaque,
+ Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
@@ -307,11 +309,11 @@ static void get_vlan(Object *obj, Visitor *v, void *opaque,
}
}
- visit_type_int32(v, &id, name, errp);
+ visit_type_int32(v, name, &id, errp);
}
-static void set_vlan(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void set_vlan(Object *obj, Visitor *v, const char *name, void *opaque,
+ Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
@@ -326,7 +328,7 @@ static void set_vlan(Object *obj, Visitor *v, void *opaque,
return;
}
- visit_type_int32(v, &id, name, &local_err);
+ visit_type_int32(v, name, &id, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
@@ -364,18 +366,6 @@ void qdev_prop_set_drive(DeviceState *dev, const char *name,
name, errp);
}
-void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name,
- BlockBackend *value)
-{
- Error *err = NULL;
-
- qdev_prop_set_drive(dev, name, value, &err);
- if (err) {
- error_report_err(err);
- exit(1);
- }
-}
-
void qdev_prop_set_chr(DeviceState *dev, const char *name,
CharDriverState *value)
{
diff --git a/qemu/hw/core/qdev-properties.c b/qemu/hw/core/qdev-properties.c
index 04fd80a4d..737d29c63 100644
--- a/qemu/hw/core/qdev-properties.c
+++ b/qemu/hw/core/qdev-properties.c
@@ -1,5 +1,8 @@
+#include "qemu/osdep.h"
#include "net/net.h"
#include "hw/qdev.h"
+#include "qapi/error.h"
+#include "hw/pci/pci.h"
#include "qapi/qmp/qerror.h"
#include "qemu/error-report.h"
#include "sysemu/block-backend.h"
@@ -41,19 +44,18 @@ void *qdev_get_prop_ptr(DeviceState *dev, Property *prop)
return ptr;
}
-static void get_enum(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void get_enum(Object *obj, Visitor *v, const char *name, void *opaque,
+ Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
int *ptr = qdev_get_prop_ptr(dev, prop);
- visit_type_enum(v, ptr, prop->info->enum_table,
- prop->info->name, prop->name, errp);
+ visit_type_enum(v, prop->name, ptr, prop->info->enum_table, errp);
}
-static void set_enum(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void set_enum(Object *obj, Visitor *v, const char *name, void *opaque,
+ Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
@@ -64,8 +66,7 @@ static void set_enum(Object *obj, Visitor *v, void *opaque,
return;
}
- visit_type_enum(v, ptr, prop->info->enum_table,
- prop->info->name, prop->name, errp);
+ visit_type_enum(v, prop->name, ptr, prop->info->enum_table, errp);
}
/* Bit */
@@ -87,19 +88,19 @@ static void bit_prop_set(DeviceState *dev, Property *props, bool val)
}
}
-static void prop_get_bit(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void prop_get_bit(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
uint32_t *p = qdev_get_prop_ptr(dev, prop);
bool value = (*p & qdev_get_prop_mask(prop)) != 0;
- visit_type_bool(v, &value, name, errp);
+ visit_type_bool(v, name, &value, errp);
}
-static void prop_set_bit(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void prop_set_bit(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
@@ -111,7 +112,7 @@ static void prop_set_bit(Object *obj, Visitor *v, void *opaque,
return;
}
- visit_type_bool(v, &value, name, &local_err);
+ visit_type_bool(v, name, &value, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
@@ -145,19 +146,19 @@ static void bit64_prop_set(DeviceState *dev, Property *props, bool val)
}
}
-static void prop_get_bit64(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void prop_get_bit64(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
uint64_t *p = qdev_get_prop_ptr(dev, prop);
bool value = (*p & qdev_get_prop_mask64(prop)) != 0;
- visit_type_bool(v, &value, name, errp);
+ visit_type_bool(v, name, &value, errp);
}
-static void prop_set_bit64(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void prop_set_bit64(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
@@ -169,7 +170,7 @@ static void prop_set_bit64(Object *obj, Visitor *v, void *opaque,
return;
}
- visit_type_bool(v, &value, name, &local_err);
+ visit_type_bool(v, name, &value, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
@@ -186,18 +187,18 @@ PropertyInfo qdev_prop_bit64 = {
/* --- bool --- */
-static void get_bool(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void get_bool(Object *obj, Visitor *v, const char *name, void *opaque,
+ Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
bool *ptr = qdev_get_prop_ptr(dev, prop);
- visit_type_bool(v, ptr, name, errp);
+ visit_type_bool(v, name, ptr, errp);
}
-static void set_bool(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void set_bool(Object *obj, Visitor *v, const char *name, void *opaque,
+ Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
@@ -208,7 +209,7 @@ static void set_bool(Object *obj, Visitor *v, void *opaque,
return;
}
- visit_type_bool(v, ptr, name, errp);
+ visit_type_bool(v, name, ptr, errp);
}
PropertyInfo qdev_prop_bool = {
@@ -219,18 +220,18 @@ PropertyInfo qdev_prop_bool = {
/* --- 8bit integer --- */
-static void get_uint8(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void get_uint8(Object *obj, Visitor *v, const char *name, void *opaque,
+ Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
uint8_t *ptr = qdev_get_prop_ptr(dev, prop);
- visit_type_uint8(v, ptr, name, errp);
+ visit_type_uint8(v, name, ptr, errp);
}
-static void set_uint8(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void set_uint8(Object *obj, Visitor *v, const char *name, void *opaque,
+ Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
@@ -241,7 +242,7 @@ static void set_uint8(Object *obj, Visitor *v, void *opaque,
return;
}
- visit_type_uint8(v, ptr, name, errp);
+ visit_type_uint8(v, name, ptr, errp);
}
PropertyInfo qdev_prop_uint8 = {
@@ -252,18 +253,18 @@ PropertyInfo qdev_prop_uint8 = {
/* --- 16bit integer --- */
-static void get_uint16(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void get_uint16(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
uint16_t *ptr = qdev_get_prop_ptr(dev, prop);
- visit_type_uint16(v, ptr, name, errp);
+ visit_type_uint16(v, name, ptr, errp);
}
-static void set_uint16(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void set_uint16(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
@@ -274,7 +275,7 @@ static void set_uint16(Object *obj, Visitor *v, void *opaque,
return;
}
- visit_type_uint16(v, ptr, name, errp);
+ visit_type_uint16(v, name, ptr, errp);
}
PropertyInfo qdev_prop_uint16 = {
@@ -285,18 +286,18 @@ PropertyInfo qdev_prop_uint16 = {
/* --- 32bit integer --- */
-static void get_uint32(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void get_uint32(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
uint32_t *ptr = qdev_get_prop_ptr(dev, prop);
- visit_type_uint32(v, ptr, name, errp);
+ visit_type_uint32(v, name, ptr, errp);
}
-static void set_uint32(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void set_uint32(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
@@ -307,21 +308,21 @@ static void set_uint32(Object *obj, Visitor *v, void *opaque,
return;
}
- visit_type_uint32(v, ptr, name, errp);
+ visit_type_uint32(v, name, ptr, errp);
}
-static void get_int32(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void get_int32(Object *obj, Visitor *v, const char *name, void *opaque,
+ Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
int32_t *ptr = qdev_get_prop_ptr(dev, prop);
- visit_type_int32(v, ptr, name, errp);
+ visit_type_int32(v, name, ptr, errp);
}
-static void set_int32(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void set_int32(Object *obj, Visitor *v, const char *name, void *opaque,
+ Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
@@ -332,7 +333,7 @@ static void set_int32(Object *obj, Visitor *v, void *opaque,
return;
}
- visit_type_int32(v, ptr, name, errp);
+ visit_type_int32(v, name, ptr, errp);
}
PropertyInfo qdev_prop_uint32 = {
@@ -349,18 +350,18 @@ PropertyInfo qdev_prop_int32 = {
/* --- 64bit integer --- */
-static void get_uint64(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void get_uint64(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
uint64_t *ptr = qdev_get_prop_ptr(dev, prop);
- visit_type_uint64(v, ptr, name, errp);
+ visit_type_uint64(v, name, ptr, errp);
}
-static void set_uint64(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void set_uint64(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
@@ -371,7 +372,7 @@ static void set_uint64(Object *obj, Visitor *v, void *opaque,
return;
}
- visit_type_uint64(v, ptr, name, errp);
+ visit_type_uint64(v, name, ptr, errp);
}
PropertyInfo qdev_prop_uint64 = {
@@ -388,8 +389,8 @@ static void release_string(Object *obj, const char *name, void *opaque)
g_free(*(char **)qdev_get_prop_ptr(DEVICE(obj), prop));
}
-static void get_string(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void get_string(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
@@ -397,14 +398,14 @@ static void get_string(Object *obj, Visitor *v, void *opaque,
if (!*ptr) {
char *str = (char *)"";
- visit_type_str(v, &str, name, errp);
+ visit_type_str(v, name, &str, errp);
} else {
- visit_type_str(v, ptr, name, errp);
+ visit_type_str(v, name, ptr, errp);
}
}
-static void set_string(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void set_string(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
@@ -417,14 +418,12 @@ static void set_string(Object *obj, Visitor *v, void *opaque,
return;
}
- visit_type_str(v, &str, name, &local_err);
+ visit_type_str(v, name, &str, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
- if (*ptr) {
- g_free(*ptr);
- }
+ g_free(*ptr);
*ptr = str;
}
@@ -449,8 +448,8 @@ PropertyInfo qdev_prop_ptr = {
* 01:02:03:04:05:06
* 01-02-03-04-05-06
*/
-static void get_mac(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void get_mac(Object *obj, Visitor *v, const char *name, void *opaque,
+ Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
@@ -462,11 +461,11 @@ static void get_mac(Object *obj, Visitor *v, void *opaque,
mac->a[0], mac->a[1], mac->a[2],
mac->a[3], mac->a[4], mac->a[5]);
- visit_type_str(v, &p, name, errp);
+ visit_type_str(v, name, &p, errp);
}
-static void set_mac(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void set_mac(Object *obj, Visitor *v, const char *name, void *opaque,
+ Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
@@ -480,7 +479,7 @@ static void set_mac(Object *obj, Visitor *v, void *opaque,
return;
}
- visit_type_str(v, &str, name, &local_err);
+ visit_type_str(v, name, &str, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
@@ -519,6 +518,16 @@ PropertyInfo qdev_prop_macaddr = {
.set = set_mac,
};
+/* --- on/off/auto --- */
+
+PropertyInfo qdev_prop_on_off_auto = {
+ .name = "OnOffAuto",
+ .description = "on/off/auto",
+ .enum_table = OnOffAuto_lookup,
+ .get = get_enum,
+ .set = set_enum,
+};
+
/* --- lost tick policy --- */
QEMU_BUILD_BUG_ON(sizeof(LostTickPolicy) != sizeof(int));
@@ -543,13 +552,24 @@ PropertyInfo qdev_prop_bios_chs_trans = {
.set = set_enum,
};
+/* --- FDC default drive types */
+
+PropertyInfo qdev_prop_fdc_drive_type = {
+ .name = "FdcDriveType",
+ .description = "FDC drive type, "
+ "144/288/120/none/auto",
+ .enum_table = FloppyDriveType_lookup,
+ .get = get_enum,
+ .set = set_enum
+};
+
/* --- pci address --- */
/*
* bus-local address, i.e. "$slot" or "$slot.$fn"
*/
-static void set_pci_devfn(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void set_pci_devfn(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
@@ -563,11 +583,11 @@ static void set_pci_devfn(Object *obj, Visitor *v, void *opaque,
return;
}
- visit_type_str(v, &str, name, &local_err);
+ visit_type_str(v, name, &str, &local_err);
if (local_err) {
error_free(local_err);
local_err = NULL;
- visit_type_int32(v, &value, name, &local_err);
+ visit_type_int32(v, name, &value, &local_err);
if (local_err) {
error_propagate(errp, local_err);
} else if (value < -1 || value > 255) {
@@ -619,8 +639,8 @@ PropertyInfo qdev_prop_pci_devfn = {
/* --- blocksize --- */
-static void set_blocksize(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void set_blocksize(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
@@ -634,7 +654,7 @@ static void set_blocksize(Object *obj, Visitor *v, void *opaque,
return;
}
- visit_type_uint16(v, &value, name, &local_err);
+ visit_type_uint16(v, name, &value, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
@@ -666,8 +686,8 @@ PropertyInfo qdev_prop_blocksize = {
/* --- pci host address --- */
-static void get_pci_host_devaddr(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void get_pci_host_devaddr(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
@@ -680,15 +700,15 @@ static void get_pci_host_devaddr(Object *obj, Visitor *v, void *opaque,
addr->domain, addr->bus, addr->slot, addr->function);
assert(rc == sizeof(buffer) - 1);
- visit_type_str(v, &p, name, errp);
+ visit_type_str(v, name, &p, errp);
}
/*
* Parse [<domain>:]<bus>:<slot>.<func>
* if <domain> is not supplied, it's assumed to be 0.
*/
-static void set_pci_host_devaddr(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void set_pci_host_devaddr(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
@@ -705,7 +725,7 @@ static void set_pci_host_devaddr(Object *obj, Visitor *v, void *opaque,
return;
}
- visit_type_str(v, &str, name, &local_err);
+ visit_type_str(v, name, &str, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
@@ -801,8 +821,8 @@ static void array_element_release(Object *obj, const char *name, void *opaque)
g_free(p);
}
-static void set_prop_arraylen(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void set_prop_arraylen(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
/* Setter for the property which defines the length of a
* variable-sized property array. As well as actually setting the
@@ -827,7 +847,7 @@ static void set_prop_arraylen(Object *obj, Visitor *v, void *opaque,
name);
return;
}
- visit_type_uint32(v, alenptr, name, &local_err);
+ visit_type_uint32(v, name, alenptr, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
@@ -1065,10 +1085,8 @@ static void qdev_prop_set_globals_for_type(DeviceState *dev,
object_property_parse(OBJECT(dev), prop->value, prop->property, &err);
if (err != NULL) {
assert(prop->user_provided);
- error_report("Warning: global %s.%s=%s ignored (%s)",
- prop->driver, prop->property, prop->value,
- error_get_pretty(err));
- error_free(err);
+ error_reportf_err(err, "Warning: global %s.%s=%s ignored: ",
+ prop->driver, prop->property, prop->value);
return;
}
}
@@ -1086,24 +1104,24 @@ void qdev_prop_set_globals(DeviceState *dev)
/* --- 64bit unsigned int 'size' type --- */
-static void get_size(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void get_size(Object *obj, Visitor *v, const char *name, void *opaque,
+ Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
uint64_t *ptr = qdev_get_prop_ptr(dev, prop);
- visit_type_size(v, ptr, name, errp);
+ visit_type_size(v, name, ptr, errp);
}
-static void set_size(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void set_size(Object *obj, Visitor *v, const char *name, void *opaque,
+ Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
uint64_t *ptr = qdev_get_prop_ptr(dev, prop);
- visit_type_size(v, ptr, name, errp);
+ visit_type_size(v, name, ptr, errp);
}
PropertyInfo qdev_prop_size = {
diff --git a/qemu/hw/core/qdev.c b/qemu/hw/core/qdev.c
index b2f404a76..db41aa1f2 100644
--- a/qemu/hw/core/qdev.c
+++ b/qemu/hw/core/qdev.c
@@ -25,10 +25,10 @@
inherit from a particular bus (e.g. PCI or I2C) rather than
this API directly. */
+#include "qemu/osdep.h"
#include "hw/qdev.h"
#include "hw/fw-path-provider.h"
#include "sysemu/sysemu.h"
-#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "qapi/visitor.h"
#include "qapi/qmp/qjson.h"
@@ -325,6 +325,11 @@ void qdev_reset_all(DeviceState *dev)
qdev_walk_children(dev, NULL, NULL, qdev_reset_one, qbus_reset_one, NULL);
}
+void qdev_reset_all_fn(void *opaque)
+{
+ qdev_reset_all(DEVICE(opaque));
+}
+
void qbus_reset_all(BusState *bus)
{
qbus_walk_children(bus, NULL, NULL, qdev_reset_one, qbus_reset_one, NULL);
@@ -365,9 +370,8 @@ void qdev_init_nofail(DeviceState *dev)
object_property_set_bool(OBJECT(dev), true, "realized", &err);
if (err) {
- error_report("Initialization of device %s failed: %s",
- object_get_typename(OBJECT(dev)),
- error_get_pretty(err));
+ error_reportf_err(err, "Initialization of device %s failed: ",
+ object_get_typename(OBJECT(dev)));
exit(1);
}
}
@@ -417,17 +421,21 @@ void qdev_init_gpio_in_named(DeviceState *dev, qemu_irq_handler handler,
{
int i;
NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name);
- char *propname = g_strdup_printf("%s[*]", name ? name : "unnamed-gpio-in");
assert(gpio_list->num_out == 0 || !name);
gpio_list->in = qemu_extend_irqs(gpio_list->in, gpio_list->num_in, handler,
dev, n);
+ if (!name) {
+ name = "unnamed-gpio-in";
+ }
for (i = gpio_list->num_in; i < gpio_list->num_in + n; i++) {
+ gchar *propname = g_strdup_printf("%s[%u]", name, i);
+
object_property_add_child(OBJECT(dev), propname,
OBJECT(gpio_list->in[i]), &error_abort);
+ g_free(propname);
}
- g_free(propname);
gpio_list->num_in += n;
}
@@ -442,20 +450,25 @@ void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins,
{
int i;
NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name);
- char *propname = g_strdup_printf("%s[*]", name ? name : "unnamed-gpio-out");
assert(gpio_list->num_in == 0 || !name);
- gpio_list->num_out += n;
+ if (!name) {
+ name = "unnamed-gpio-out";
+ }
+ memset(pins, 0, sizeof(*pins) * n);
for (i = 0; i < n; ++i) {
- memset(&pins[i], 0, sizeof(*pins));
+ gchar *propname = g_strdup_printf("%s[%u]", name,
+ gpio_list->num_out + i);
+
object_property_add_link(OBJECT(dev), propname, TYPE_IRQ,
(Object **)&pins[i],
object_property_allow_set_link,
OBJ_PROP_LINK_UNREF_ON_RELEASE,
&error_abort);
+ g_free(propname);
}
- g_free(propname);
+ gpio_list->num_out += n;
}
void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n)
@@ -506,7 +519,7 @@ qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n)
return ret;
}
-/* disconnect a GPIO ouput, returning the disconnected input (if any) */
+/* disconnect a GPIO output, returning the disconnected input (if any) */
static qemu_irq qdev_disconnect_gpio_out_named(DeviceState *dev,
const char *name, int n)
@@ -567,6 +580,12 @@ void qdev_pass_gpios(DeviceState *dev, DeviceState *container,
BusState *qdev_get_child_bus(DeviceState *dev, const char *name)
{
BusState *bus;
+ Object *child = object_resolve_path_component(OBJECT(dev), name);
+
+ bus = (BusState *)object_dynamic_cast(child, TYPE_BUS);
+ if (bus) {
+ return bus;
+ }
QLIST_FOREACH(bus, &dev->child_bus, sibling) {
if (strcmp(name, bus->name) == 0) {
@@ -874,8 +893,9 @@ char *qdev_get_dev_path(DeviceState *dev)
* Legacy property handling
*/
-static void qdev_get_legacy_property(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void qdev_get_legacy_property(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
@@ -884,7 +904,7 @@ static void qdev_get_legacy_property(Object *obj, Visitor *v, void *opaque,
char *ptr = buffer;
prop->info->print(dev, prop, buffer, sizeof(buffer));
- visit_type_str(v, &ptr, name, errp);
+ visit_type_str(v, name, &ptr, errp);
}
/**
@@ -1120,7 +1140,6 @@ post_realize_fail:
fail:
error_propagate(errp, local_err);
- return;
}
static bool device_get_hotpluggable(Object *obj, Error **errp)
@@ -1194,7 +1213,6 @@ static void device_finalize(Object *obj)
NamedGPIOList *ngl, *next;
DeviceState *dev = DEVICE(obj);
- qemu_opts_del(dev->opts);
QLIST_FOREACH_SAFE(ngl, &dev->gpios, node, next) {
QLIST_REMOVE(ngl, node);
@@ -1242,6 +1260,9 @@ static void device_unparent(Object *obj)
qapi_event_send_device_deleted(!!dev->id, dev->id, path, &error_abort);
g_free(path);
}
+
+ qemu_opts_del(dev->opts);
+ dev->opts = NULL;
}
static void device_class_init(ObjectClass *class, void *data)
diff --git a/qemu/hw/core/stream.c b/qemu/hw/core/stream.c
index e6a05a543..4439ecdf0 100644
--- a/qemu/hw/core/stream.c
+++ b/qemu/hw/core/stream.c
@@ -1,3 +1,4 @@
+#include "qemu/osdep.h"
#include "hw/stream.h"
size_t
diff --git a/qemu/hw/core/sysbus.c b/qemu/hw/core/sysbus.c
index 3c5862989..a7dbe2b32 100644
--- a/qemu/hw/core/sysbus.c
+++ b/qemu/hw/core/sysbus.c
@@ -17,6 +17,7 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "monitor/monitor.h"
#include "exec/address-spaces.h"
diff --git a/qemu/hw/cpu/Makefile.objs b/qemu/hw/cpu/Makefile.objs
index 6381238cc..0954a1872 100644
--- a/qemu/hw/cpu/Makefile.objs
+++ b/qemu/hw/cpu/Makefile.objs
@@ -2,5 +2,4 @@ obj-$(CONFIG_ARM11MPCORE) += arm11mpcore.o
obj-$(CONFIG_REALVIEW) += realview_mpcore.o
obj-$(CONFIG_A9MPCORE) += a9mpcore.o
obj-$(CONFIG_A15MPCORE) += a15mpcore.o
-obj-$(CONFIG_ICC_BUS) += icc_bus.o
diff --git a/qemu/hw/cpu/a15mpcore.c b/qemu/hw/cpu/a15mpcore.c
index acc419e11..bc05152fd 100644
--- a/qemu/hw/cpu/a15mpcore.c
+++ b/qemu/hw/cpu/a15mpcore.c
@@ -18,8 +18,11 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/cpu/a15mpcore.h"
#include "sysemu/kvm.h"
+#include "kvm_arm.h"
static void a15mp_priv_set_irq(void *opaque, int irq, int level)
{
@@ -33,16 +36,11 @@ static void a15mp_priv_initfn(Object *obj)
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
A15MPPrivState *s = A15MPCORE_PRIV(obj);
DeviceState *gicdev;
- const char *gictype = "arm_gic";
-
- if (kvm_irqchip_in_kernel()) {
- gictype = "kvm-arm-gic";
- }
memory_region_init(&s->container, obj, "a15mp-priv-container", 0x8000);
sysbus_init_mmio(sbd, &s->container);
- object_initialize(&s->gic, sizeof(s->gic), gictype);
+ object_initialize(&s->gic, sizeof(s->gic), gic_class_name());
gicdev = DEVICE(&s->gic);
qdev_set_parent_bus(gicdev, sysbus_get_default());
qdev_prop_set_uint32(gicdev, "revision", 2);
@@ -56,10 +54,23 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp)
SysBusDevice *busdev;
int i;
Error *err = NULL;
+ bool has_el3;
+ Object *cpuobj;
gicdev = DEVICE(&s->gic);
qdev_prop_set_uint32(gicdev, "num-cpu", s->num_cpu);
qdev_prop_set_uint32(gicdev, "num-irq", s->num_irq);
+
+ if (!kvm_irqchip_in_kernel()) {
+ /* Make the GIC's TZ support match the CPUs. We assume that
+ * either all the CPUs have TZ, or none do.
+ */
+ cpuobj = OBJECT(qemu_get_cpu(0));
+ has_el3 = object_property_find(cpuobj, "has_el3", NULL) &&
+ object_property_get_bool(cpuobj, "has_el3", &error_abort);
+ qdev_prop_set_bit(gicdev, "has-security-extensions", has_el3);
+ }
+
object_property_set_bool(OBJECT(&s->gic), true, "realized", &err);
if (err != NULL) {
error_propagate(errp, err);
@@ -79,20 +90,27 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp)
for (i = 0; i < s->num_cpu; i++) {
DeviceState *cpudev = DEVICE(qemu_get_cpu(i));
int ppibase = s->num_irq - 32 + i * 32;
- /* physical timer; we wire it up to the non-secure timer's ID,
- * since a real A15 always has TrustZone but QEMU doesn't.
+ int irq;
+ /* Mapping from the output timer irq lines from the CPU to the
+ * GIC PPI inputs used on the A15:
*/
- qdev_connect_gpio_out(cpudev, 0,
- qdev_get_gpio_in(gicdev, ppibase + 30));
- /* virtual timer */
- qdev_connect_gpio_out(cpudev, 1,
- qdev_get_gpio_in(gicdev, ppibase + 27));
+ const int timer_irq[] = {
+ [GTIMER_PHYS] = 30,
+ [GTIMER_VIRT] = 27,
+ [GTIMER_HYP] = 26,
+ [GTIMER_SEC] = 29,
+ };
+ for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) {
+ qdev_connect_gpio_out(cpudev, irq,
+ qdev_get_gpio_in(gicdev,
+ ppibase + timer_irq[irq]));
+ }
}
/* Memory map (addresses are offsets from PERIPHBASE):
* 0x0000-0x0fff -- reserved
* 0x1000-0x1fff -- GIC Distributor
- * 0x2000-0x2fff -- GIC CPU interface
+ * 0x2000-0x3fff -- GIC CPU interface
* 0x4000-0x4fff -- GIC virtual interface control (not modelled)
* 0x5000-0x5fff -- GIC virtual interface control (not modelled)
* 0x6000-0x7fff -- GIC virtual CPU interface (not modelled)
diff --git a/qemu/hw/cpu/a9mpcore.c b/qemu/hw/cpu/a9mpcore.c
index c09358c6e..5459ae8c1 100644
--- a/qemu/hw/cpu/a9mpcore.c
+++ b/qemu/hw/cpu/a9mpcore.c
@@ -8,6 +8,8 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/cpu/a9mpcore.h"
static void a9mp_priv_set_irq(void *opaque, int irq, int level)
@@ -49,6 +51,8 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp)
*wdtbusdev;
Error *err = NULL;
int i;
+ bool has_el3;
+ Object *cpuobj;
scudev = DEVICE(&s->scu);
qdev_prop_set_uint32(scudev, "num-cpu", s->num_cpu);
@@ -62,6 +66,15 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp)
gicdev = DEVICE(&s->gic);
qdev_prop_set_uint32(gicdev, "num-cpu", s->num_cpu);
qdev_prop_set_uint32(gicdev, "num-irq", s->num_irq);
+
+ /* Make the GIC's TZ support match the CPUs. We assume that
+ * either all the CPUs have TZ, or none do.
+ */
+ cpuobj = OBJECT(qemu_get_cpu(0));
+ has_el3 = object_property_find(cpuobj, "has_el3", NULL) &&
+ object_property_get_bool(cpuobj, "has_el3", &error_abort);
+ qdev_prop_set_bit(gicdev, "has-security-extensions", has_el3);
+
object_property_set_bool(OBJECT(&s->gic), true, "realized", &err);
if (err != NULL) {
error_propagate(errp, err);
diff --git a/qemu/hw/cpu/arm11mpcore.c b/qemu/hw/cpu/arm11mpcore.c
index 717d3e4f8..eb244658b 100644
--- a/qemu/hw/cpu/arm11mpcore.c
+++ b/qemu/hw/cpu/arm11mpcore.c
@@ -7,6 +7,8 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/cpu/arm11mpcore.h"
#include "hw/intc/realview_gic.h"
diff --git a/qemu/hw/cpu/icc_bus.c b/qemu/hw/cpu/icc_bus.c
deleted file mode 100644
index 6646ea2b3..000000000
--- a/qemu/hw/cpu/icc_bus.c
+++ /dev/null
@@ -1,118 +0,0 @@
-/* icc_bus.c
- * emulate x86 ICC (Interrupt Controller Communications) bus
- *
- * Copyright (c) 2013 Red Hat, Inc
- *
- * Authors:
- * Igor Mammedov <imammedo@redhat.com>
- *
- * 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 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 <http://www.gnu.org/licenses/>
- */
-#include "hw/cpu/icc_bus.h"
-#include "hw/sysbus.h"
-
-/* icc-bridge implementation */
-
-static const TypeInfo icc_bus_info = {
- .name = TYPE_ICC_BUS,
- .parent = TYPE_BUS,
- .instance_size = sizeof(ICCBus),
-};
-
-
-/* icc-device implementation */
-
-static void icc_device_realize(DeviceState *dev, Error **errp)
-{
- ICCDeviceClass *idc = ICC_DEVICE_GET_CLASS(dev);
-
- /* convert to QOM */
- if (idc->realize) {
- idc->realize(dev, errp);
- }
-
-}
-
-static void icc_device_class_init(ObjectClass *oc, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(oc);
-
- dc->realize = icc_device_realize;
- dc->bus_type = TYPE_ICC_BUS;
-}
-
-static const TypeInfo icc_device_info = {
- .name = TYPE_ICC_DEVICE,
- .parent = TYPE_DEVICE,
- .abstract = true,
- .instance_size = sizeof(ICCDevice),
- .class_size = sizeof(ICCDeviceClass),
- .class_init = icc_device_class_init,
-};
-
-
-/* icc-bridge implementation */
-
-typedef struct ICCBridgeState {
- /*< private >*/
- SysBusDevice parent_obj;
- /*< public >*/
-
- ICCBus icc_bus;
- MemoryRegion apic_container;
-} ICCBridgeState;
-
-#define ICC_BRIDGE(obj) OBJECT_CHECK(ICCBridgeState, (obj), TYPE_ICC_BRIDGE)
-
-static void icc_bridge_init(Object *obj)
-{
- ICCBridgeState *s = ICC_BRIDGE(obj);
- SysBusDevice *sb = SYS_BUS_DEVICE(obj);
-
- qbus_create_inplace(&s->icc_bus, sizeof(s->icc_bus), TYPE_ICC_BUS,
- DEVICE(s), "icc");
-
- /* Do not change order of registering regions,
- * APIC must be first registered region, board maps it by 0 index
- */
- memory_region_init(&s->apic_container, obj, "icc-apic-container",
- APIC_SPACE_SIZE);
- sysbus_init_mmio(sb, &s->apic_container);
- s->icc_bus.apic_address_space = &s->apic_container;
-}
-
-static void icc_bridge_class_init(ObjectClass *oc, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(oc);
-
- set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
-}
-
-static const TypeInfo icc_bridge_info = {
- .name = TYPE_ICC_BRIDGE,
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_init = icc_bridge_init,
- .instance_size = sizeof(ICCBridgeState),
- .class_init = icc_bridge_class_init,
-};
-
-
-static void icc_bus_register_types(void)
-{
- type_register_static(&icc_bus_info);
- type_register_static(&icc_device_info);
- type_register_static(&icc_bridge_info);
-}
-
-type_init(icc_bus_register_types)
diff --git a/qemu/hw/cpu/realview_mpcore.c b/qemu/hw/cpu/realview_mpcore.c
index c39a2da42..39d4ebeb1 100644
--- a/qemu/hw/cpu/realview_mpcore.c
+++ b/qemu/hw/cpu/realview_mpcore.c
@@ -8,6 +8,8 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/cpu/arm11mpcore.h"
#include "hw/intc/realview_gic.h"
diff --git a/qemu/hw/cris/axis_dev88.c b/qemu/hw/cris/axis_dev88.c
index 3cae480fa..9f5865874 100644
--- a/qemu/hw/cris/axis_dev88.c
+++ b/qemu/hw/cris/axis_dev88.c
@@ -22,6 +22,10 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/sysbus.h"
#include "net/net.h"
#include "hw/block/flash.h"
@@ -138,7 +142,7 @@ static void tempsensor_clkedge(struct tempsensor_t *s,
s->count = 16;
if ((s->regs[0] & 0xff) == 0) {
- /* 25 degrees celcius. */
+ /* 25 degrees celsius. */
s->shiftreg = 0x0b9f;
} else if ((s->regs[0] & 0xff) == 0xff) {
/* Sensor ID, 0x8100 LM70. */
@@ -277,7 +281,7 @@ void axisdev88_init(MachineState *machine)
/* The ETRAX-FS has 128Kb on chip ram, the docs refer to it as the
internal memory. */
memory_region_init_ram(phys_intmem, NULL, "axisdev88.chipram", INTMEM_SIZE,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(phys_intmem);
memory_region_add_subregion(address_space_mem, 0x38000000, phys_intmem);
@@ -351,16 +355,11 @@ void axisdev88_init(MachineState *machine)
}
}
-static QEMUMachine axisdev88_machine = {
- .name = "axis-dev88",
- .desc = "AXIS devboard 88",
- .init = axisdev88_init,
- .is_default = 1,
-};
-
-static void axisdev88_machine_init(void)
+static void axisdev88_machine_init(MachineClass *mc)
{
- qemu_register_machine(&axisdev88_machine);
+ mc->desc = "AXIS devboard 88";
+ mc->init = axisdev88_init;
+ mc->is_default = 1;
}
-machine_init(axisdev88_machine_init);
+DEFINE_MACHINE("axis-dev88", axisdev88_machine_init)
diff --git a/qemu/hw/cris/boot.c b/qemu/hw/cris/boot.c
index 622f353c9..f896ed7f8 100644
--- a/qemu/hw/cris/boot.c
+++ b/qemu/hw/cris/boot.c
@@ -22,10 +22,14 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "hw/loader.h"
#include "elf.h"
#include "boot.h"
+#include "qemu/cutils.h"
static void main_cpu_reset(void *opaque)
{
@@ -72,7 +76,7 @@ void cris_load_image(CRISCPU *cpu, struct cris_load_info *li)
/* Boots a kernel elf binary, os/linux-2.6/vmlinux from the axis
devboard SDK. */
image_size = load_elf(li->image_filename, translate_kernel_address, NULL,
- &entry, NULL, &high, 0, ELF_MACHINE, 0);
+ &entry, NULL, &high, 0, EM_CRIS, 0, 0);
li->entry = entry;
if (image_size < 0) {
/* Takes a kimage from the axis devboard SDK. */
diff --git a/qemu/hw/display/Makefile.objs b/qemu/hw/display/Makefile.objs
index dd8ea76d1..d99780eeb 100644
--- a/qemu/hw/display/Makefile.objs
+++ b/qemu/hw/display/Makefile.objs
@@ -27,6 +27,7 @@ endif
obj-$(CONFIG_OMAP) += omap_dss.o
obj-$(CONFIG_OMAP) += omap_lcdc.o
obj-$(CONFIG_PXA2XX) += pxa2xx_lcd.o
+obj-$(CONFIG_RASPI) += bcm2835_fb.o
obj-$(CONFIG_SM501) += sm501.o
obj-$(CONFIG_TCX) += tcx.o
obj-$(CONFIG_CG3) += cg3.o
@@ -35,6 +36,10 @@ obj-$(CONFIG_VGA) += vga.o
common-obj-$(CONFIG_QXL) += qxl.o qxl-logger.o qxl-render.o
-obj-$(CONFIG_VIRTIO) += virtio-gpu.o
+obj-$(CONFIG_VIRTIO) += virtio-gpu.o virtio-gpu-3d.o
obj-$(CONFIG_VIRTIO_PCI) += virtio-gpu-pci.o
obj-$(CONFIG_VIRTIO_VGA) += virtio-vga.o
+virtio-gpu.o-cflags := $(VIRGL_CFLAGS)
+virtio-gpu.o-libs += $(VIRGL_LIBS)
+virtio-gpu-3d.o-cflags := $(VIRGL_CFLAGS)
+virtio-gpu-3d.o-libs += $(VIRGL_LIBS)
diff --git a/qemu/hw/display/ads7846.c b/qemu/hw/display/ads7846.c
index 3f35369bb..05aa2d1e6 100644
--- a/qemu/hw/display/ads7846.c
+++ b/qemu/hw/display/ads7846.c
@@ -10,7 +10,8 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
-#include "hw/ssi.h"
+#include "qemu/osdep.h"
+#include "hw/ssi/ssi.h"
#include "ui/console.h"
typedef struct {
diff --git a/qemu/hw/display/bcm2835_fb.c b/qemu/hw/display/bcm2835_fb.c
new file mode 100644
index 000000000..506f1d3d9
--- /dev/null
+++ b/qemu/hw/display/bcm2835_fb.c
@@ -0,0 +1,425 @@
+/*
+ * Raspberry Pi emulation (c) 2012 Gregory Estrade
+ * Refactoring for Pi2 Copyright (c) 2015, Microsoft. Written by Andrew Baumann.
+ * This code is licensed under the GNU GPLv2 and later.
+ *
+ * Heavily based on milkymist-vgafb.c, copyright terms below:
+ * QEMU model of the Milkymist VGA framebuffer.
+ *
+ * Copyright (c) 2010-2012 Michael Walle <michael@walle.cc>
+ *
+ * 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 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/display/bcm2835_fb.h"
+#include "hw/display/framebuffer.h"
+#include "ui/pixel_ops.h"
+#include "hw/misc/bcm2835_mbox_defs.h"
+
+#define DEFAULT_VCRAM_SIZE 0x4000000
+#define BCM2835_FB_OFFSET 0x00100000
+
+static void fb_invalidate_display(void *opaque)
+{
+ BCM2835FBState *s = BCM2835_FB(opaque);
+
+ s->invalidate = true;
+}
+
+static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src,
+ int width, int deststep)
+{
+ BCM2835FBState *s = opaque;
+ uint16_t rgb565;
+ uint32_t rgb888;
+ uint8_t r, g, b;
+ DisplaySurface *surface = qemu_console_surface(s->con);
+ int bpp = surface_bits_per_pixel(surface);
+
+ while (width--) {
+ switch (s->bpp) {
+ case 8:
+ /* lookup palette starting at video ram base
+ * TODO: cache translation, rather than doing this each time!
+ */
+ rgb888 = ldl_le_phys(&s->dma_as, s->vcram_base + (*src << 2));
+ r = (rgb888 >> 0) & 0xff;
+ g = (rgb888 >> 8) & 0xff;
+ b = (rgb888 >> 16) & 0xff;
+ src++;
+ break;
+ case 16:
+ rgb565 = lduw_le_p(src);
+ r = ((rgb565 >> 11) & 0x1f) << 3;
+ g = ((rgb565 >> 5) & 0x3f) << 2;
+ b = ((rgb565 >> 0) & 0x1f) << 3;
+ src += 2;
+ break;
+ case 24:
+ rgb888 = ldl_le_p(src);
+ r = (rgb888 >> 0) & 0xff;
+ g = (rgb888 >> 8) & 0xff;
+ b = (rgb888 >> 16) & 0xff;
+ src += 3;
+ break;
+ case 32:
+ rgb888 = ldl_le_p(src);
+ r = (rgb888 >> 0) & 0xff;
+ g = (rgb888 >> 8) & 0xff;
+ b = (rgb888 >> 16) & 0xff;
+ src += 4;
+ break;
+ default:
+ r = 0;
+ g = 0;
+ b = 0;
+ break;
+ }
+
+ if (s->pixo == 0) {
+ /* swap to BGR pixel format */
+ uint8_t tmp = r;
+ r = b;
+ b = tmp;
+ }
+
+ switch (bpp) {
+ case 8:
+ *dst++ = rgb_to_pixel8(r, g, b);
+ break;
+ case 15:
+ *(uint16_t *)dst = rgb_to_pixel15(r, g, b);
+ dst += 2;
+ break;
+ case 16:
+ *(uint16_t *)dst = rgb_to_pixel16(r, g, b);
+ dst += 2;
+ break;
+ case 24:
+ rgb888 = rgb_to_pixel24(r, g, b);
+ *dst++ = rgb888 & 0xff;
+ *dst++ = (rgb888 >> 8) & 0xff;
+ *dst++ = (rgb888 >> 16) & 0xff;
+ break;
+ case 32:
+ *(uint32_t *)dst = rgb_to_pixel32(r, g, b);
+ dst += 4;
+ break;
+ default:
+ return;
+ }
+ }
+}
+
+static void fb_update_display(void *opaque)
+{
+ BCM2835FBState *s = opaque;
+ DisplaySurface *surface = qemu_console_surface(s->con);
+ int first = 0;
+ int last = 0;
+ int src_width = 0;
+ int dest_width = 0;
+
+ if (s->lock || !s->xres) {
+ return;
+ }
+
+ src_width = s->xres * (s->bpp >> 3);
+ dest_width = s->xres;
+
+ switch (surface_bits_per_pixel(surface)) {
+ case 0:
+ return;
+ case 8:
+ break;
+ case 15:
+ dest_width *= 2;
+ break;
+ case 16:
+ dest_width *= 2;
+ break;
+ case 24:
+ dest_width *= 3;
+ break;
+ case 32:
+ dest_width *= 4;
+ break;
+ default:
+ hw_error("bcm2835_fb: bad color depth\n");
+ break;
+ }
+
+ if (s->invalidate) {
+ framebuffer_update_memory_section(&s->fbsection, s->dma_mr, s->base,
+ s->yres, src_width);
+ }
+
+ framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres,
+ src_width, dest_width, 0, s->invalidate,
+ draw_line_src16, s, &first, &last);
+
+ if (first >= 0) {
+ dpy_gfx_update(s->con, 0, first, s->xres, last - first + 1);
+ }
+
+ s->invalidate = false;
+}
+
+static void bcm2835_fb_mbox_push(BCM2835FBState *s, uint32_t value)
+{
+ value &= ~0xf;
+
+ s->lock = true;
+
+ s->xres = ldl_le_phys(&s->dma_as, value);
+ s->yres = ldl_le_phys(&s->dma_as, value + 4);
+ s->xres_virtual = ldl_le_phys(&s->dma_as, value + 8);
+ s->yres_virtual = ldl_le_phys(&s->dma_as, value + 12);
+ s->bpp = ldl_le_phys(&s->dma_as, value + 20);
+ s->xoffset = ldl_le_phys(&s->dma_as, value + 24);
+ s->yoffset = ldl_le_phys(&s->dma_as, value + 28);
+
+ s->base = s->vcram_base | (value & 0xc0000000);
+ s->base += BCM2835_FB_OFFSET;
+
+ /* TODO - Manage properly virtual resolution */
+
+ s->pitch = s->xres * (s->bpp >> 3);
+ s->size = s->yres * s->pitch;
+
+ stl_le_phys(&s->dma_as, value + 16, s->pitch);
+ stl_le_phys(&s->dma_as, value + 32, s->base);
+ stl_le_phys(&s->dma_as, value + 36, s->size);
+
+ s->invalidate = true;
+ qemu_console_resize(s->con, s->xres, s->yres);
+ s->lock = false;
+}
+
+void bcm2835_fb_reconfigure(BCM2835FBState *s, uint32_t *xres, uint32_t *yres,
+ uint32_t *xoffset, uint32_t *yoffset, uint32_t *bpp,
+ uint32_t *pixo, uint32_t *alpha)
+{
+ s->lock = true;
+
+ /* TODO: input validation! */
+ if (xres) {
+ s->xres = *xres;
+ }
+ if (yres) {
+ s->yres = *yres;
+ }
+ if (xoffset) {
+ s->xoffset = *xoffset;
+ }
+ if (yoffset) {
+ s->yoffset = *yoffset;
+ }
+ if (bpp) {
+ s->bpp = *bpp;
+ }
+ if (pixo) {
+ s->pixo = *pixo;
+ }
+ if (alpha) {
+ s->alpha = *alpha;
+ }
+
+ /* TODO - Manage properly virtual resolution */
+
+ s->pitch = s->xres * (s->bpp >> 3);
+ s->size = s->yres * s->pitch;
+
+ s->invalidate = true;
+ qemu_console_resize(s->con, s->xres, s->yres);
+ s->lock = false;
+}
+
+static uint64_t bcm2835_fb_read(void *opaque, hwaddr offset, unsigned size)
+{
+ BCM2835FBState *s = opaque;
+ uint32_t res = 0;
+
+ switch (offset) {
+ case MBOX_AS_DATA:
+ res = MBOX_CHAN_FB;
+ s->pending = false;
+ qemu_set_irq(s->mbox_irq, 0);
+ break;
+
+ case MBOX_AS_PENDING:
+ res = s->pending;
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return 0;
+ }
+
+ return res;
+}
+
+static void bcm2835_fb_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ BCM2835FBState *s = opaque;
+
+ switch (offset) {
+ case MBOX_AS_DATA:
+ /* bcm2835_mbox should check our pending status before pushing */
+ assert(!s->pending);
+ s->pending = true;
+ bcm2835_fb_mbox_push(s, value);
+ qemu_set_irq(s->mbox_irq, 1);
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return;
+ }
+}
+
+static const MemoryRegionOps bcm2835_fb_ops = {
+ .read = bcm2835_fb_read,
+ .write = bcm2835_fb_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+static const VMStateDescription vmstate_bcm2835_fb = {
+ .name = TYPE_BCM2835_FB,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_BOOL(lock, BCM2835FBState),
+ VMSTATE_BOOL(invalidate, BCM2835FBState),
+ VMSTATE_BOOL(pending, BCM2835FBState),
+ VMSTATE_UINT32(xres, BCM2835FBState),
+ VMSTATE_UINT32(yres, BCM2835FBState),
+ VMSTATE_UINT32(xres_virtual, BCM2835FBState),
+ VMSTATE_UINT32(yres_virtual, BCM2835FBState),
+ VMSTATE_UINT32(xoffset, BCM2835FBState),
+ VMSTATE_UINT32(yoffset, BCM2835FBState),
+ VMSTATE_UINT32(bpp, BCM2835FBState),
+ VMSTATE_UINT32(base, BCM2835FBState),
+ VMSTATE_UINT32(pitch, BCM2835FBState),
+ VMSTATE_UINT32(size, BCM2835FBState),
+ VMSTATE_UINT32(pixo, BCM2835FBState),
+ VMSTATE_UINT32(alpha, BCM2835FBState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const GraphicHwOps vgafb_ops = {
+ .invalidate = fb_invalidate_display,
+ .gfx_update = fb_update_display,
+};
+
+static void bcm2835_fb_init(Object *obj)
+{
+ BCM2835FBState *s = BCM2835_FB(obj);
+
+ memory_region_init_io(&s->iomem, obj, &bcm2835_fb_ops, s, TYPE_BCM2835_FB,
+ 0x10);
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
+ sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq);
+}
+
+static void bcm2835_fb_reset(DeviceState *dev)
+{
+ BCM2835FBState *s = BCM2835_FB(dev);
+
+ s->pending = false;
+
+ s->xres_virtual = s->xres;
+ s->yres_virtual = s->yres;
+ s->xoffset = 0;
+ s->yoffset = 0;
+ s->base = s->vcram_base + BCM2835_FB_OFFSET;
+ s->pitch = s->xres * (s->bpp >> 3);
+ s->size = s->yres * s->pitch;
+
+ s->invalidate = true;
+ s->lock = false;
+}
+
+static void bcm2835_fb_realize(DeviceState *dev, Error **errp)
+{
+ BCM2835FBState *s = BCM2835_FB(dev);
+ Error *err = NULL;
+ Object *obj;
+
+ if (s->vcram_base == 0) {
+ error_setg(errp, "%s: required vcram-base property not set", __func__);
+ return;
+ }
+
+ obj = object_property_get_link(OBJECT(dev), "dma-mr", &err);
+ if (obj == NULL) {
+ error_setg(errp, "%s: required dma-mr link not found: %s",
+ __func__, error_get_pretty(err));
+ return;
+ }
+
+ s->dma_mr = MEMORY_REGION(obj);
+ address_space_init(&s->dma_as, s->dma_mr, NULL);
+
+ bcm2835_fb_reset(dev);
+
+ s->con = graphic_console_init(dev, 0, &vgafb_ops, s);
+ qemu_console_resize(s->con, s->xres, s->yres);
+}
+
+static Property bcm2835_fb_props[] = {
+ DEFINE_PROP_UINT32("vcram-base", BCM2835FBState, vcram_base, 0),/*required*/
+ DEFINE_PROP_UINT32("vcram-size", BCM2835FBState, vcram_size,
+ DEFAULT_VCRAM_SIZE),
+ DEFINE_PROP_UINT32("xres", BCM2835FBState, xres, 640),
+ DEFINE_PROP_UINT32("yres", BCM2835FBState, yres, 480),
+ DEFINE_PROP_UINT32("bpp", BCM2835FBState, bpp, 16),
+ DEFINE_PROP_UINT32("pixo", BCM2835FBState, pixo, 1), /* 1=RGB, 0=BGR */
+ DEFINE_PROP_UINT32("alpha", BCM2835FBState, alpha, 2), /* alpha ignored */
+ DEFINE_PROP_END_OF_LIST()
+};
+
+static void bcm2835_fb_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->props = bcm2835_fb_props;
+ dc->realize = bcm2835_fb_realize;
+ dc->reset = bcm2835_fb_reset;
+ dc->vmsd = &vmstate_bcm2835_fb;
+}
+
+static TypeInfo bcm2835_fb_info = {
+ .name = TYPE_BCM2835_FB,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BCM2835FBState),
+ .class_init = bcm2835_fb_class_init,
+ .instance_init = bcm2835_fb_init,
+};
+
+static void bcm2835_fb_register_types(void)
+{
+ type_register_static(&bcm2835_fb_info);
+}
+
+type_init(bcm2835_fb_register_types)
diff --git a/qemu/hw/display/blizzard.c b/qemu/hw/display/blizzard.c
index 5019bbbef..c231960d9 100644
--- a/qemu/hw/display/blizzard.c
+++ b/qemu/hw/display/blizzard.c
@@ -18,6 +18,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "ui/console.h"
#include "hw/devices.h"
diff --git a/qemu/hw/display/cg3.c b/qemu/hw/display/cg3.c
index 34dcbc311..fc0d97fa4 100644
--- a/qemu/hw/display/cg3.c
+++ b/qemu/hw/display/cg3.c
@@ -23,6 +23,8 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu-common.h"
#include "qemu/error-report.h"
#include "ui/console.h"
@@ -280,12 +282,12 @@ static void cg3_initfn(Object *obj)
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
CG3State *s = CG3(obj);
- memory_region_init_ram(&s->rom, NULL, "cg3.prom", FCODE_MAX_ROM_SIZE,
- &error_abort);
+ memory_region_init_ram(&s->rom, obj, "cg3.prom", FCODE_MAX_ROM_SIZE,
+ &error_fatal);
memory_region_set_readonly(&s->rom, true);
sysbus_init_mmio(sbd, &s->rom);
- memory_region_init_io(&s->reg, NULL, &cg3_reg_ops, s, "cg3.reg",
+ memory_region_init_io(&s->reg, obj, &cg3_reg_ops, s, "cg3.reg",
CG3_REG_SIZE);
sysbus_init_mmio(sbd, &s->reg);
}
@@ -310,7 +312,7 @@ static void cg3_realizefn(DeviceState *dev, Error **errp)
}
memory_region_init_ram(&s->vram_mem, NULL, "cg3.vram", s->vram_size,
- &error_abort);
+ &error_fatal);
memory_region_set_log(&s->vram_mem, true, DIRTY_MEMORY_VGA);
vmstate_register_ram_global(&s->vram_mem);
sysbus_init_mmio(sbd, &s->vram_mem);
diff --git a/qemu/hw/display/cirrus_vga.c b/qemu/hw/display/cirrus_vga.c
index 5198037d8..3d712d592 100644
--- a/qemu/hw/display/cirrus_vga.c
+++ b/qemu/hw/display/cirrus_vga.c
@@ -26,6 +26,8 @@
* Reference: Finn Thogersons' VGADOC4b
* available at http://home.worldonline.dk/~finth/
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "ui/console.h"
@@ -275,14 +277,14 @@ static bool blit_region_is_unsafe(struct CirrusVGAState *s,
+ ((int64_t)s->cirrus_blt_height-1) * pitch;
int32_t max = addr
+ s->cirrus_blt_width;
- if (min < 0 || max >= s->vga.vram_size) {
+ if (min < 0 || max > s->vga.vram_size) {
return true;
}
} else {
int64_t max = addr
+ ((int64_t)s->cirrus_blt_height-1) * pitch
+ s->cirrus_blt_width;
- if (max >= s->vga.vram_size) {
+ if (max > s->vga.vram_size) {
return true;
}
}
diff --git a/qemu/hw/display/exynos4210_fimd.c b/qemu/hw/display/exynos4210_fimd.c
index 603ef5056..728eb214a 100644
--- a/qemu/hw/display/exynos4210_fimd.c
+++ b/qemu/hw/display/exynos4210_fimd.c
@@ -22,6 +22,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "hw/sysbus.h"
#include "ui/console.h"
@@ -1354,9 +1355,7 @@ static void exynos4210_fimd_reset(DeviceState *d)
fimd_update_get_alpha(s, w);
}
- if (s->ifb != NULL) {
- g_free(s->ifb);
- }
+ g_free(s->ifb);
s->ifb = NULL;
exynos4210_fimd_invalidate(s);
diff --git a/qemu/hw/display/framebuffer.c b/qemu/hw/display/framebuffer.c
index 7f075ce77..df51358e7 100644
--- a/qemu/hw/display/framebuffer.c
+++ b/qemu/hw/display/framebuffer.c
@@ -17,6 +17,7 @@
- Remove all DisplayState knowledge from devices.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "ui/console.h"
#include "framebuffer.h"
diff --git a/qemu/hw/display/g364fb.c b/qemu/hw/display/g364fb.c
index 7f83a007b..70ef2c745 100644
--- a/qemu/hw/display/g364fb.c
+++ b/qemu/hw/display/g364fb.c
@@ -17,6 +17,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "qemu/error-report.h"
#include "ui/console.h"
diff --git a/qemu/hw/display/jazz_led.c b/qemu/hw/display/jazz_led.c
index 12b1707cb..09dcdb46a 100644
--- a/qemu/hw/display/jazz_led.c
+++ b/qemu/hw/display/jazz_led.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "ui/console.h"
#include "ui/pixel_ops.h"
@@ -233,8 +234,10 @@ static void jazz_led_text_update(void *opaque, console_ch_t *chardata)
/* TODO: draw the segments */
snprintf(buf, 2, "%02hhx\n", s->segments);
- console_write_ch(chardata++, 0x00200100 | buf[0]);
- console_write_ch(chardata++, 0x00200100 | buf[1]);
+ console_write_ch(chardata++, ATTR2CHTYPE(buf[0], QEMU_COLOR_BLUE,
+ QEMU_COLOR_BLACK, 1));
+ console_write_ch(chardata++, ATTR2CHTYPE(buf[1], QEMU_COLOR_BLUE,
+ QEMU_COLOR_BLACK, 1));
dpy_text_update(s->con, 0, 0, 2, 1);
}
diff --git a/qemu/hw/display/milkymist-tmu2.c b/qemu/hw/display/milkymist-tmu2.c
index 3e1d0b9c2..9bc88f93b 100644
--- a/qemu/hw/display/milkymist-tmu2.c
+++ b/qemu/hw/display/milkymist-tmu2.c
@@ -24,14 +24,15 @@
*
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "trace.h"
#include "qemu/error-report.h"
#include <X11/Xlib.h>
-#include <GL/gl.h>
-#include <GL/glx.h>
+#include <epoxy/gl.h>
+#include <epoxy/glx.h>
enum {
R_CTL = 0,
diff --git a/qemu/hw/display/milkymist-vgafb.c b/qemu/hw/display/milkymist-vgafb.c
index ab3074fad..19ca25647 100644
--- a/qemu/hw/display/milkymist-vgafb.c
+++ b/qemu/hw/display/milkymist-vgafb.c
@@ -22,6 +22,7 @@
* http://www.milkymist.org/socdoc/vgafb.pdf
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "trace.h"
diff --git a/qemu/hw/display/omap_dss.c b/qemu/hw/display/omap_dss.c
index f1fef2767..783e9e131 100644
--- a/qemu/hw/display/omap_dss.c
+++ b/qemu/hw/display/omap_dss.c
@@ -17,6 +17,7 @@
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "ui/console.h"
#include "hw/arm/omap.h"
@@ -1051,8 +1052,7 @@ struct omap_dss_s *omap_dss_init(struct omap_target_agent_s *ta,
omap_clk fck1, omap_clk fck2, omap_clk ck54m,
omap_clk ick1, omap_clk ick2)
{
- struct omap_dss_s *s = (struct omap_dss_s *)
- g_malloc0(sizeof(struct omap_dss_s));
+ struct omap_dss_s *s = g_new0(struct omap_dss_s, 1);
s->irq = irq;
s->drq = drq;
diff --git a/qemu/hw/display/omap_lcd_template.h b/qemu/hw/display/omap_lcd_template.h
index e5dd44716..f0ce71fd6 100644
--- a/qemu/hw/display/omap_lcd_template.h
+++ b/qemu/hw/display/omap_lcd_template.h
@@ -136,7 +136,7 @@ static void glue(draw_line12_, DEPTH)(void *opaque,
uint8_t r, g, b;
do {
- v = lduw_p((void *) s);
+ v = lduw_le_p((void *) s);
r = (v >> 4) & 0xf0;
g = v & 0xf0;
b = (v << 4) & 0xf0;
@@ -159,7 +159,7 @@ static void glue(draw_line16_, DEPTH)(void *opaque,
uint8_t r, g, b;
do {
- v = lduw_p((void *) s);
+ v = lduw_le_p((void *) s);
r = (v >> 8) & 0xf8;
g = (v >> 3) & 0xfc;
b = (v << 3) & 0xf8;
diff --git a/qemu/hw/display/omap_lcdc.c b/qemu/hw/display/omap_lcdc.c
index a7c6cd79b..ce1058bf8 100644
--- a/qemu/hw/display/omap_lcdc.c
+++ b/qemu/hw/display/omap_lcdc.c
@@ -16,6 +16,7 @@
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "ui/console.h"
#include "hw/arm/omap.h"
@@ -403,8 +404,7 @@ struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem,
struct omap_dma_lcd_channel_s *dma,
omap_clk clk)
{
- struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *)
- g_malloc0(sizeof(struct omap_lcd_panel_s));
+ struct omap_lcd_panel_s *s = g_new0(struct omap_lcd_panel_s, 1);
s->irq = irq;
s->dma = dma;
diff --git a/qemu/hw/display/pl110.c b/qemu/hw/display/pl110.c
index ef1a7b1a5..d589959f1 100644
--- a/qemu/hw/display/pl110.c
+++ b/qemu/hw/display/pl110.c
@@ -7,6 +7,7 @@
* This code is licensed under the GNU LGPL
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "ui/console.h"
#include "framebuffer.h"
diff --git a/qemu/hw/display/pxa2xx_lcd.c b/qemu/hw/display/pxa2xx_lcd.c
index 494700d07..845521c5b 100644
--- a/qemu/hw/display/pxa2xx_lcd.c
+++ b/qemu/hw/display/pxa2xx_lcd.c
@@ -10,6 +10,7 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "ui/console.h"
#include "hw/arm/pxa.h"
@@ -309,10 +310,10 @@ static void pxa2xx_descriptor_load(PXA2xxLCDState *s)
}
cpu_physical_memory_read(descptr, &desc, sizeof(desc));
- s->dma_ch[i].descriptor = tswap32(desc.fdaddr);
- s->dma_ch[i].source = tswap32(desc.fsaddr);
- s->dma_ch[i].id = tswap32(desc.fidr);
- s->dma_ch[i].command = tswap32(desc.ldcmd);
+ s->dma_ch[i].descriptor = le32_to_cpu(desc.fdaddr);
+ s->dma_ch[i].source = le32_to_cpu(desc.fsaddr);
+ s->dma_ch[i].id = le32_to_cpu(desc.fidr);
+ s->dma_ch[i].command = le32_to_cpu(desc.ldcmd);
}
}
diff --git a/qemu/hw/display/qxl-logger.c b/qemu/hw/display/qxl-logger.c
index d944d3fdb..2ec6d8fa3 100644
--- a/qemu/hw/display/qxl-logger.c
+++ b/qemu/hw/display/qxl-logger.c
@@ -19,6 +19,7 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "qemu/timer.h"
#include "qxl.h"
diff --git a/qemu/hw/display/qxl-render.c b/qemu/hw/display/qxl-render.c
index a542087fc..9ad9d9e0f 100644
--- a/qemu/hw/display/qxl-render.c
+++ b/qemu/hw/display/qxl-render.c
@@ -19,6 +19,7 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "qxl.h"
#include "trace.h"
@@ -159,7 +160,7 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl)
/*
* use ssd.lock to protect render_update_cookie_num.
* qxl_render_update is called by io thread or vcpu thread, and the completion
- * callbacks are called by spice_server thread, defering to bh called from the
+ * callbacks are called by spice_server thread, deferring to bh called from the
* io thread.
*/
void qxl_render_update(PCIQXLDevice *qxl)
diff --git a/qemu/hw/display/qxl.c b/qemu/hw/display/qxl.c
index 2288238d0..919dc5cd3 100644
--- a/qemu/hw/display/qxl.c
+++ b/qemu/hw/display/qxl.c
@@ -18,8 +18,8 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include <zlib.h>
-#include <stdint.h>
#include "qemu-common.h"
#include "qemu/timer.h"
@@ -1156,7 +1156,9 @@ static void qxl_soft_reset(PCIQXLDevice *d)
trace_qxl_soft_reset(d->id);
qxl_check_state(d);
qxl_clear_guest_bug(d);
+ qemu_mutex_lock(&d->async_lock);
d->current_async = QXL_UNDEFINED_IO;
+ qemu_mutex_unlock(&d->async_lock);
if (d->id == 0) {
qxl_enter_vga_mode(d);
@@ -1970,14 +1972,14 @@ static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp)
qxl->rom_size = qxl_rom_size();
memory_region_init_ram(&qxl->rom_bar, OBJECT(qxl), "qxl.vrom",
- qxl->rom_size, &error_abort);
+ qxl->rom_size, &error_fatal);
vmstate_register_ram(&qxl->rom_bar, &qxl->pci.qdev);
init_qxl_rom(qxl);
init_qxl_ram(qxl);
qxl->guest_surfaces.cmds = g_new0(QXLPHYSICAL, qxl->ssd.num_surfaces);
memory_region_init_ram(&qxl->vram_bar, OBJECT(qxl), "qxl.vram",
- qxl->vram_size, &error_abort);
+ qxl->vram_size, &error_fatal);
vmstate_register_ram(&qxl->vram_bar, &qxl->pci.qdev);
memory_region_init_alias(&qxl->vram32_bar, OBJECT(qxl), "qxl.vram32",
&qxl->vram_bar, 0, qxl->vram32_size);
@@ -2079,7 +2081,7 @@ static void qxl_realize_secondary(PCIDevice *dev, Error **errp)
qxl->id = device_id++;
qxl_init_ramsize(qxl);
memory_region_init_ram(&qxl->vga.vram, OBJECT(dev), "qxl.vgavram",
- qxl->vga.vram_size, &error_abort);
+ qxl->vga.vram_size, &error_fatal);
vmstate_register_ram(&qxl->vga.vram, &qxl->pci.qdev);
qxl->vga.vram_ptr = memory_region_get_ram_ptr(&qxl->vga.vram);
qxl->vga.con = graphic_console_init(DEVICE(dev), 0, &qxl_ops, qxl);
@@ -2156,7 +2158,7 @@ static int qxl_post_load(void *opaque, int version)
qxl_create_guest_primary(d, 1, QXL_SYNC);
/* replay surface-create and cursor-set commands */
- cmds = g_malloc0(sizeof(QXLCommandExt) * (d->ssd.num_surfaces + 1));
+ cmds = g_new0(QXLCommandExt, d->ssd.num_surfaces + 1);
for (in = 0, out = 0; in < d->ssd.num_surfaces; in++) {
if (d->guest_surfaces.cmds[in] == 0) {
continue;
diff --git a/qemu/hw/display/sm501.c b/qemu/hw/display/sm501.c
index 15a5ba800..5f7101210 100644
--- a/qemu/hw/display/sm501.c
+++ b/qemu/hw/display/sm501.c
@@ -22,7 +22,10 @@
* THE SOFTWARE.
*/
-#include <stdio.h>
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "hw/char/serial.h"
#include "ui/console.h"
@@ -1411,7 +1414,7 @@ void sm501_init(MemoryRegion *address_space_mem, uint32_t base,
/* allocate local memory */
memory_region_init_ram(&s->local_mem_region, NULL, "sm501.local",
- local_mem_bytes, &error_abort);
+ local_mem_bytes, &error_fatal);
vmstate_register_ram_global(&s->local_mem_region);
memory_region_set_log(&s->local_mem_region, true, DIRTY_MEMORY_VGA);
s->local_mem = memory_region_get_ram_ptr(&s->local_mem_region);
diff --git a/qemu/hw/display/ssd0303.c b/qemu/hw/display/ssd0303.c
index f6804fb51..d3017563f 100644
--- a/qemu/hw/display/ssd0303.c
+++ b/qemu/hw/display/ssd0303.c
@@ -10,6 +10,7 @@
/* The controller can support a variety of different displays, but we only
implement one. Most of the commends relating to brightness and geometry
setup are ignored. */
+#include "qemu/osdep.h"
#include "hw/i2c/i2c.h"
#include "ui/console.h"
diff --git a/qemu/hw/display/ssd0323.c b/qemu/hw/display/ssd0323.c
index 97270077e..14c1bf339 100644
--- a/qemu/hw/display/ssd0323.c
+++ b/qemu/hw/display/ssd0323.c
@@ -10,7 +10,8 @@
/* The controller can support a variety of different displays, but we only
implement one. Most of the commends relating to brightness and geometry
setup are ignored. */
-#include "hw/ssi.h"
+#include "qemu/osdep.h"
+#include "hw/ssi/ssi.h"
#include "ui/console.h"
//#define DEBUG_SSD0323 1
diff --git a/qemu/hw/display/tc6393xb.c b/qemu/hw/display/tc6393xb.c
index f5f3f3e69..da3ceceb0 100644
--- a/qemu/hw/display/tc6393xb.c
+++ b/qemu/hw/display/tc6393xb.c
@@ -10,6 +10,8 @@
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/devices.h"
#include "hw/block/flash.h"
@@ -584,7 +586,7 @@ TC6393xbState *tc6393xb_init(MemoryRegion *sysmem, uint32_t base, qemu_irq irq)
memory_region_add_subregion(sysmem, base, &s->iomem);
memory_region_init_ram(&s->vram, NULL, "tc6393xb.vram", 0x100000,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&s->vram);
s->vram_ptr = memory_region_get_ram_ptr(&s->vram);
memory_region_add_subregion(sysmem, base + 0x100000, &s->vram);
diff --git a/qemu/hw/display/tcx.c b/qemu/hw/display/tcx.c
index 6acdc2d28..8e26aae80 100644
--- a/qemu/hw/display/tcx.c
+++ b/qemu/hw/display/tcx.c
@@ -22,7 +22,10 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu-common.h"
+#include "cpu.h" /* FIXME shouldn't use TARGET_PAGE_SIZE */
#include "ui/console.h"
#include "ui/pixel_ops.h"
#include "hw/loader.h"
@@ -944,57 +947,55 @@ static void tcx_initfn(Object *obj)
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
TCXState *s = TCX(obj);
- memory_region_init_ram(&s->rom, NULL, "tcx.prom", FCODE_MAX_ROM_SIZE,
- &error_abort);
+ memory_region_init_ram(&s->rom, obj, "tcx.prom", FCODE_MAX_ROM_SIZE,
+ &error_fatal);
memory_region_set_readonly(&s->rom, true);
sysbus_init_mmio(sbd, &s->rom);
/* 2/STIP : Stippler */
- memory_region_init_io(&s->stip, OBJECT(s), &tcx_stip_ops, s, "tcx.stip",
+ memory_region_init_io(&s->stip, obj, &tcx_stip_ops, s, "tcx.stip",
TCX_STIP_NREGS);
sysbus_init_mmio(sbd, &s->stip);
/* 3/BLIT : Blitter */
- memory_region_init_io(&s->blit, OBJECT(s), &tcx_blit_ops, s, "tcx.blit",
+ memory_region_init_io(&s->blit, obj, &tcx_blit_ops, s, "tcx.blit",
TCX_BLIT_NREGS);
sysbus_init_mmio(sbd, &s->blit);
/* 5/RSTIP : Raw Stippler */
- memory_region_init_io(&s->rstip, OBJECT(s), &tcx_rstip_ops, s, "tcx.rstip",
+ memory_region_init_io(&s->rstip, obj, &tcx_rstip_ops, s, "tcx.rstip",
TCX_RSTIP_NREGS);
sysbus_init_mmio(sbd, &s->rstip);
/* 6/RBLIT : Raw Blitter */
- memory_region_init_io(&s->rblit, OBJECT(s), &tcx_rblit_ops, s, "tcx.rblit",
+ memory_region_init_io(&s->rblit, obj, &tcx_rblit_ops, s, "tcx.rblit",
TCX_RBLIT_NREGS);
sysbus_init_mmio(sbd, &s->rblit);
/* 7/TEC : ??? */
- memory_region_init_io(&s->tec, OBJECT(s), &tcx_dummy_ops, s,
- "tcx.tec", TCX_TEC_NREGS);
+ memory_region_init_io(&s->tec, obj, &tcx_dummy_ops, s, "tcx.tec",
+ TCX_TEC_NREGS);
sysbus_init_mmio(sbd, &s->tec);
/* 8/CMAP : DAC */
- memory_region_init_io(&s->dac, OBJECT(s), &tcx_dac_ops, s,
- "tcx.dac", TCX_DAC_NREGS);
+ memory_region_init_io(&s->dac, obj, &tcx_dac_ops, s, "tcx.dac",
+ TCX_DAC_NREGS);
sysbus_init_mmio(sbd, &s->dac);
/* 9/THC : Cursor */
- memory_region_init_io(&s->thc, OBJECT(s), &tcx_thc_ops, s, "tcx.thc",
+ memory_region_init_io(&s->thc, obj, &tcx_thc_ops, s, "tcx.thc",
TCX_THC_NREGS);
sysbus_init_mmio(sbd, &s->thc);
/* 11/DHC : ??? */
- memory_region_init_io(&s->dhc, OBJECT(s), &tcx_dummy_ops, s, "tcx.dhc",
+ memory_region_init_io(&s->dhc, obj, &tcx_dummy_ops, s, "tcx.dhc",
TCX_DHC_NREGS);
sysbus_init_mmio(sbd, &s->dhc);
/* 12/ALT : ??? */
- memory_region_init_io(&s->alt, OBJECT(s), &tcx_dummy_ops, s, "tcx.alt",
+ memory_region_init_io(&s->alt, obj, &tcx_dummy_ops, s, "tcx.alt",
TCX_ALT_NREGS);
sysbus_init_mmio(sbd, &s->alt);
-
- return;
}
static void tcx_realizefn(DeviceState *dev, Error **errp)
@@ -1007,7 +1008,7 @@ static void tcx_realizefn(DeviceState *dev, Error **errp)
char *fcode_filename;
memory_region_init_ram(&s->vram_mem, OBJECT(s), "tcx.vram",
- s->vram_size * (1 + 4 + 4), &error_abort);
+ s->vram_size * (1 + 4 + 4), &error_fatal);
vmstate_register_ram_global(&s->vram_mem);
memory_region_set_log(&s->vram_mem, true, DIRTY_MEMORY_VGA);
vram_base = memory_region_get_ram_ptr(&s->vram_mem);
diff --git a/qemu/hw/display/vga-isa-mm.c b/qemu/hw/display/vga-isa-mm.c
index 4efc22278..51ccbccc4 100644
--- a/qemu/hw/display/vga-isa-mm.c
+++ b/qemu/hw/display/vga-isa-mm.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "ui/console.h"
#include "hw/i386/pc.h"
diff --git a/qemu/hw/display/vga-isa.c b/qemu/hw/display/vga-isa.c
index 7f3c98941..f5aff1cbe 100644
--- a/qemu/hw/display/vga-isa.c
+++ b/qemu/hw/display/vga-isa.c
@@ -23,6 +23,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "ui/console.h"
#include "hw/i386/pc.h"
diff --git a/qemu/hw/display/vga-pci.c b/qemu/hw/display/vga-pci.c
index 1dfa331e6..ac9a76499 100644
--- a/qemu/hw/display/vga-pci.c
+++ b/qemu/hw/display/vga-pci.c
@@ -23,6 +23,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "ui/console.h"
#include "hw/pci/pci.h"
diff --git a/qemu/hw/display/vga.c b/qemu/hw/display/vga.c
index b35d523e6..4a55ec6db 100644
--- a/qemu/hw/display/vga.c
+++ b/qemu/hw/display/vga.c
@@ -21,6 +21,8 @@
* 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/hw.h"
#include "vga.h"
#include "ui/console.h"
@@ -140,6 +142,13 @@ static uint32_t expand4[256];
static uint16_t expand2[256];
static uint8_t expand4to8[16];
+static void vbe_update_vgaregs(VGACommonState *s);
+
+static inline bool vbe_enabled(VGACommonState *s)
+{
+ return s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED;
+}
+
static void vga_update_memory_access(VGACommonState *s)
{
hwaddr base, offset, size;
@@ -177,6 +186,7 @@ static void vga_update_memory_access(VGACommonState *s)
size = 0x8000;
break;
}
+ assert(offset + size <= s->vram_size);
memory_region_init_alias(&s->chain4_alias, memory_region_owner(&s->vram),
"vga.chain4", &s->vram, offset, size);
memory_region_add_subregion_overlap(s->legacy_address_space, base,
@@ -234,9 +244,9 @@ static void vga_precise_update_retrace_info(VGACommonState *s)
r->total_chars = vtotal_lines * htotal_chars;
if (r->freq) {
- r->ticks_per_char = get_ticks_per_sec() / (r->total_chars * r->freq);
+ r->ticks_per_char = NANOSECONDS_PER_SECOND / (r->total_chars * r->freq);
} else {
- r->ticks_per_char = get_ticks_per_sec() / chars_per_sec;
+ r->ticks_per_char = NANOSECONDS_PER_SECOND / chars_per_sec;
}
r->vstart = vretr_start_line;
@@ -264,7 +274,7 @@ static void vga_precise_update_retrace_info(VGACommonState *s)
"dots = %d\n"
"ticks/char = %" PRId64 "\n"
"\n",
- (double) get_ticks_per_sec() / (r->ticks_per_char * r->total_chars),
+ (double) NANOSECONDS_PER_SECOND / (r->ticks_per_char * r->total_chars),
htotal_chars,
hretr_start_char,
hretr_skew_chars,
@@ -476,6 +486,7 @@ void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
printf("vga: write SR%x = 0x%02x\n", s->sr_index, val);
#endif
s->sr[s->sr_index] = val & sr_mask[s->sr_index];
+ vbe_update_vgaregs(s);
if (s->sr_index == VGA_SEQ_CLOCK_MODE) {
s->update_retrace_info(s);
}
@@ -507,6 +518,7 @@ void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
printf("vga: write GR%x = 0x%02x\n", s->gr_index, val);
#endif
s->gr[s->gr_index] = val & gr_mask[s->gr_index];
+ vbe_update_vgaregs(s);
vga_update_memory_access(s);
break;
case VGA_CRT_IM:
@@ -525,10 +537,12 @@ void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
if (s->cr_index == VGA_CRTC_OVERFLOW) {
s->cr[VGA_CRTC_OVERFLOW] = (s->cr[VGA_CRTC_OVERFLOW] & ~0x10) |
(val & 0x10);
+ vbe_update_vgaregs(s);
}
return;
}
s->cr[s->cr_index] = val;
+ vbe_update_vgaregs(s);
switch(s->cr_index) {
case VGA_CRTC_H_TOTAL:
@@ -561,7 +575,7 @@ static void vbe_fixup_regs(VGACommonState *s)
uint16_t *r = s->vbe_regs;
uint32_t bits, linelength, maxy, offset;
- if (!(r[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) {
+ if (!vbe_enabled(s)) {
/* vbe is turned off -- nothing to do */
return;
}
@@ -636,6 +650,49 @@ static void vbe_fixup_regs(VGACommonState *s)
s->vbe_start_addr = offset / 4;
}
+/* we initialize the VGA graphic mode */
+static void vbe_update_vgaregs(VGACommonState *s)
+{
+ int h, shift_control;
+
+ if (!vbe_enabled(s)) {
+ /* vbe is turned off -- nothing to do */
+ return;
+ }
+
+ /* graphic mode + memory map 1 */
+ s->gr[VGA_GFX_MISC] = (s->gr[VGA_GFX_MISC] & ~0x0c) | 0x04 |
+ VGA_GR06_GRAPHICS_MODE;
+ s->cr[VGA_CRTC_MODE] |= 3; /* no CGA modes */
+ s->cr[VGA_CRTC_OFFSET] = s->vbe_line_offset >> 3;
+ /* width */
+ s->cr[VGA_CRTC_H_DISP] =
+ (s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 3) - 1;
+ /* height (only meaningful if < 1024) */
+ h = s->vbe_regs[VBE_DISPI_INDEX_YRES] - 1;
+ s->cr[VGA_CRTC_V_DISP_END] = h;
+ s->cr[VGA_CRTC_OVERFLOW] = (s->cr[VGA_CRTC_OVERFLOW] & ~0x42) |
+ ((h >> 7) & 0x02) | ((h >> 3) & 0x40);
+ /* line compare to 1023 */
+ s->cr[VGA_CRTC_LINE_COMPARE] = 0xff;
+ s->cr[VGA_CRTC_OVERFLOW] |= 0x10;
+ s->cr[VGA_CRTC_MAX_SCAN] |= 0x40;
+
+ if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) {
+ shift_control = 0;
+ s->sr[VGA_SEQ_CLOCK_MODE] &= ~8; /* no double line */
+ } else {
+ shift_control = 2;
+ /* set chain 4 mode */
+ s->sr[VGA_SEQ_MEMORY_MODE] |= VGA_SR04_CHN_4M;
+ /* activate all planes */
+ s->sr[VGA_SEQ_PLANE_WRITE] |= VGA_SR02_ALL_PLANES;
+ }
+ s->gr[VGA_GFX_MODE] = (s->gr[VGA_GFX_MODE] & ~0x60) |
+ (shift_control << 5);
+ s->cr[VGA_CRTC_MAX_SCAN] &= ~0x9f; /* no double scan */
+}
+
static uint32_t vbe_ioport_read_index(void *opaque, uint32_t addr)
{
VGACommonState *s = opaque;
@@ -712,13 +769,10 @@ void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val)
case VBE_DISPI_INDEX_Y_OFFSET:
s->vbe_regs[s->vbe_index] = val;
vbe_fixup_regs(s);
+ vbe_update_vgaregs(s);
break;
case VBE_DISPI_INDEX_BANK:
- if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) {
- val &= (s->vbe_bank_mask >> 2);
- } else {
- val &= s->vbe_bank_mask;
- }
+ val &= s->vbe_bank_mask;
s->vbe_regs[s->vbe_index] = val;
s->bank_offset = (val << 16);
vga_update_memory_access(s);
@@ -726,52 +780,19 @@ void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val)
case VBE_DISPI_INDEX_ENABLE:
if ((val & VBE_DISPI_ENABLED) &&
!(s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) {
- int h, shift_control;
s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = 0;
s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
s->vbe_regs[VBE_DISPI_INDEX_ENABLE] |= VBE_DISPI_ENABLED;
vbe_fixup_regs(s);
+ vbe_update_vgaregs(s);
/* clear the screen */
if (!(val & VBE_DISPI_NOCLEARMEM)) {
memset(s->vram_ptr, 0,
s->vbe_regs[VBE_DISPI_INDEX_YRES] * s->vbe_line_offset);
}
-
- /* we initialize the VGA graphic mode */
- /* graphic mode + memory map 1 */
- s->gr[VGA_GFX_MISC] = (s->gr[VGA_GFX_MISC] & ~0x0c) | 0x04 |
- VGA_GR06_GRAPHICS_MODE;
- s->cr[VGA_CRTC_MODE] |= 3; /* no CGA modes */
- s->cr[VGA_CRTC_OFFSET] = s->vbe_line_offset >> 3;
- /* width */
- s->cr[VGA_CRTC_H_DISP] =
- (s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 3) - 1;
- /* height (only meaningful if < 1024) */
- h = s->vbe_regs[VBE_DISPI_INDEX_YRES] - 1;
- s->cr[VGA_CRTC_V_DISP_END] = h;
- s->cr[VGA_CRTC_OVERFLOW] = (s->cr[VGA_CRTC_OVERFLOW] & ~0x42) |
- ((h >> 7) & 0x02) | ((h >> 3) & 0x40);
- /* line compare to 1023 */
- s->cr[VGA_CRTC_LINE_COMPARE] = 0xff;
- s->cr[VGA_CRTC_OVERFLOW] |= 0x10;
- s->cr[VGA_CRTC_MAX_SCAN] |= 0x40;
-
- if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) {
- shift_control = 0;
- s->sr[VGA_SEQ_CLOCK_MODE] &= ~8; /* no double line */
- } else {
- shift_control = 2;
- /* set chain 4 mode */
- s->sr[VGA_SEQ_MEMORY_MODE] |= VGA_SR04_CHN_4M;
- /* activate all planes */
- s->sr[VGA_SEQ_PLANE_WRITE] |= VGA_SR02_ALL_PLANES;
- }
- s->gr[VGA_GFX_MODE] = (s->gr[VGA_GFX_MODE] & ~0x60) |
- (shift_control << 5);
- s->cr[VGA_CRTC_MAX_SCAN] &= ~0x9f; /* no double scan */
} else {
s->bank_offset = 0;
}
@@ -817,13 +838,21 @@ uint32_t vga_mem_readb(VGACommonState *s, hwaddr addr)
if (s->sr[VGA_SEQ_MEMORY_MODE] & VGA_SR04_CHN_4M) {
/* chain 4 mode : simplest access */
+ assert(addr < s->vram_size);
ret = s->vram_ptr[addr];
} else if (s->gr[VGA_GFX_MODE] & 0x10) {
/* odd/even mode (aka text mode mapping) */
plane = (s->gr[VGA_GFX_PLANE_READ] & 2) | (addr & 1);
- ret = s->vram_ptr[((addr & ~1) << 1) | plane];
+ addr = ((addr & ~1) << 1) | plane;
+ if (addr >= s->vram_size) {
+ return 0xff;
+ }
+ ret = s->vram_ptr[addr];
} else {
/* standard VGA latched access */
+ if (addr * sizeof(uint32_t) >= s->vram_size) {
+ return 0xff;
+ }
s->latch = ((uint32_t *)s->vram_ptr)[addr];
if (!(s->gr[VGA_GFX_MODE] & 0x08)) {
@@ -880,6 +909,7 @@ void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val)
plane = addr & 3;
mask = (1 << plane);
if (s->sr[VGA_SEQ_PLANE_WRITE] & mask) {
+ assert(addr < s->vram_size);
s->vram_ptr[addr] = val;
#ifdef DEBUG_VGA_MEM
printf("vga: chain4: [0x" TARGET_FMT_plx "]\n", addr);
@@ -893,6 +923,9 @@ void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val)
mask = (1 << plane);
if (s->sr[VGA_SEQ_PLANE_WRITE] & mask) {
addr = ((addr & ~1) << 1) | plane;
+ if (addr >= s->vram_size) {
+ return;
+ }
s->vram_ptr[addr] = val;
#ifdef DEBUG_VGA_MEM
printf("vga: odd/even: [0x" TARGET_FMT_plx "]\n", addr);
@@ -966,6 +999,9 @@ void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val)
mask = s->sr[VGA_SEQ_PLANE_WRITE];
s->plane_updated |= mask; /* only used to detect font change */
write_mask = mask16[mask];
+ if (addr * sizeof(uint32_t) >= s->vram_size) {
+ return;
+ }
((uint32_t *)s->vram_ptr)[addr] =
(((uint32_t *)s->vram_ptr)[addr] & ~write_mask) |
(val & write_mask);
@@ -1044,7 +1080,7 @@ static void vga_get_offsets(VGACommonState *s,
{
uint32_t start_addr, line_offset, line_compare;
- if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
+ if (vbe_enabled(s)) {
line_offset = s->vbe_line_offset;
start_addr = s->vbe_start_addr;
line_compare = 65535;
@@ -1369,7 +1405,7 @@ static int vga_get_bpp(VGACommonState *s)
{
int ret;
- if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
+ if (vbe_enabled(s)) {
ret = s->vbe_regs[VBE_DISPI_INDEX_BPP];
} else {
ret = 0;
@@ -1381,7 +1417,7 @@ static void vga_get_resolution(VGACommonState *s, int *pwidth, int *pheight)
{
int width, height;
- if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
+ if (vbe_enabled(s)) {
width = s->vbe_regs[VBE_DISPI_INDEX_XRES];
height = s->vbe_regs[VBE_DISPI_INDEX_YRES];
} else {
@@ -1979,7 +2015,8 @@ static void vga_update_text(void *opaque, console_ch_t *chardata)
width = (s->last_width - size) / 2;
dst = chardata + s->last_width + width;
for (i = 0; i < size; i ++)
- console_write_ch(dst ++, 0x00200100 | msg_buffer[i]);
+ console_write_ch(dst ++, ATTR2CHTYPE(msg_buffer[i], QEMU_COLOR_BLUE,
+ QEMU_COLOR_BLACK, 1));
dpy_text_update(s->con, 0, 0, s->last_width, height);
}
@@ -2139,7 +2176,7 @@ void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate)
s->is_vbe_vmstate = 1;
memory_region_init_ram(&s->vram, obj, "vga.vram", s->vram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram(&s->vram, global_vmstate ? NULL : DEVICE(obj));
xen_register_framebuffer(&s->vram);
s->vram_ptr = memory_region_get_ram_ptr(&s->vram);
diff --git a/qemu/hw/display/vga_int.h b/qemu/hw/display/vga_int.h
index 40ba6a420..bdb43a5a3 100644
--- a/qemu/hw/display/vga_int.h
+++ b/qemu/hw/display/vga_int.h
@@ -25,7 +25,6 @@
#define HW_VGA_INT_H 1
#include <hw/hw.h>
-#include "qapi/error.h"
#include "exec/memory.h"
#define ST01_V_RETRACE 0x08
diff --git a/qemu/hw/display/virtio-gpu-3d.c b/qemu/hw/display/virtio-gpu-3d.c
new file mode 100644
index 000000000..fa192946a
--- /dev/null
+++ b/qemu/hw/display/virtio-gpu-3d.c
@@ -0,0 +1,606 @@
+/*
+ * Virtio GPU Device
+ *
+ * Copyright Red Hat, Inc. 2013-2014
+ *
+ * Authors:
+ * Dave Airlie <airlied@redhat.com>
+ * Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * 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 "qemu/iov.h"
+#include "trace.h"
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/virtio-gpu.h"
+
+#ifdef CONFIG_VIRGL
+
+#include "virglrenderer.h"
+
+static struct virgl_renderer_callbacks virtio_gpu_3d_cbs;
+
+static void virgl_cmd_create_resource_2d(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_resource_create_2d c2d;
+ struct virgl_renderer_resource_create_args args;
+
+ VIRTIO_GPU_FILL_CMD(c2d);
+ trace_virtio_gpu_cmd_res_create_2d(c2d.resource_id, c2d.format,
+ c2d.width, c2d.height);
+
+ args.handle = c2d.resource_id;
+ args.target = 2;
+ args.format = c2d.format;
+ args.bind = (1 << 1);
+ args.width = c2d.width;
+ args.height = c2d.height;
+ args.depth = 1;
+ args.array_size = 1;
+ args.last_level = 0;
+ args.nr_samples = 0;
+ args.flags = VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP;
+ virgl_renderer_resource_create(&args, NULL, 0);
+}
+
+static void virgl_cmd_create_resource_3d(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_resource_create_3d c3d;
+ struct virgl_renderer_resource_create_args args;
+
+ VIRTIO_GPU_FILL_CMD(c3d);
+ trace_virtio_gpu_cmd_res_create_3d(c3d.resource_id, c3d.format,
+ c3d.width, c3d.height, c3d.depth);
+
+ args.handle = c3d.resource_id;
+ args.target = c3d.target;
+ args.format = c3d.format;
+ args.bind = c3d.bind;
+ args.width = c3d.width;
+ args.height = c3d.height;
+ args.depth = c3d.depth;
+ args.array_size = c3d.array_size;
+ args.last_level = c3d.last_level;
+ args.nr_samples = c3d.nr_samples;
+ args.flags = c3d.flags;
+ virgl_renderer_resource_create(&args, NULL, 0);
+}
+
+static void virgl_cmd_resource_unref(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_resource_unref unref;
+
+ VIRTIO_GPU_FILL_CMD(unref);
+ trace_virtio_gpu_cmd_res_unref(unref.resource_id);
+
+ virgl_renderer_resource_unref(unref.resource_id);
+}
+
+static void virgl_cmd_context_create(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_ctx_create cc;
+
+ VIRTIO_GPU_FILL_CMD(cc);
+ trace_virtio_gpu_cmd_ctx_create(cc.hdr.ctx_id,
+ cc.debug_name);
+
+ virgl_renderer_context_create(cc.hdr.ctx_id, cc.nlen,
+ cc.debug_name);
+}
+
+static void virgl_cmd_context_destroy(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_ctx_destroy cd;
+
+ VIRTIO_GPU_FILL_CMD(cd);
+ trace_virtio_gpu_cmd_ctx_destroy(cd.hdr.ctx_id);
+
+ virgl_renderer_context_destroy(cd.hdr.ctx_id);
+}
+
+static void virtio_gpu_rect_update(VirtIOGPU *g, int idx, int x, int y,
+ int width, int height)
+{
+ if (!g->scanout[idx].con) {
+ return;
+ }
+
+ dpy_gl_update(g->scanout[idx].con, x, y, width, height);
+}
+
+static void virgl_cmd_resource_flush(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_resource_flush rf;
+ int i;
+
+ VIRTIO_GPU_FILL_CMD(rf);
+ trace_virtio_gpu_cmd_res_flush(rf.resource_id,
+ rf.r.width, rf.r.height, rf.r.x, rf.r.y);
+
+ for (i = 0; i < VIRTIO_GPU_MAX_SCANOUT; i++) {
+ if (g->scanout[i].resource_id != rf.resource_id) {
+ continue;
+ }
+ virtio_gpu_rect_update(g, i, rf.r.x, rf.r.y, rf.r.width, rf.r.height);
+ }
+}
+
+static void virgl_cmd_set_scanout(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_set_scanout ss;
+ struct virgl_renderer_resource_info info;
+ int ret;
+
+ VIRTIO_GPU_FILL_CMD(ss);
+ trace_virtio_gpu_cmd_set_scanout(ss.scanout_id, ss.resource_id,
+ ss.r.width, ss.r.height, ss.r.x, ss.r.y);
+
+ if (ss.scanout_id >= VIRTIO_GPU_MAX_SCANOUT) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout id specified %d",
+ __func__, ss.scanout_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID;
+ return;
+ }
+ g->enable = 1;
+
+ memset(&info, 0, sizeof(info));
+
+ if (ss.resource_id && ss.r.width && ss.r.height) {
+ ret = virgl_renderer_resource_get_info(ss.resource_id, &info);
+ if (ret == -1) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: illegal resource specified %d\n",
+ __func__, ss.resource_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+ qemu_console_resize(g->scanout[ss.scanout_id].con,
+ ss.r.width, ss.r.height);
+ virgl_renderer_force_ctx_0();
+ dpy_gl_scanout(g->scanout[ss.scanout_id].con, info.tex_id,
+ info.flags & 1 /* FIXME: Y_0_TOP */,
+ ss.r.x, ss.r.y, ss.r.width, ss.r.height);
+ } else {
+ if (ss.scanout_id != 0) {
+ dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, NULL);
+ }
+ dpy_gl_scanout(g->scanout[ss.scanout_id].con, 0, false,
+ 0, 0, 0, 0);
+ }
+ g->scanout[ss.scanout_id].resource_id = ss.resource_id;
+}
+
+static void virgl_cmd_submit_3d(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_cmd_submit cs;
+ void *buf;
+ size_t s;
+
+ VIRTIO_GPU_FILL_CMD(cs);
+ trace_virtio_gpu_cmd_ctx_submit(cs.hdr.ctx_id, cs.size);
+
+ buf = g_malloc(cs.size);
+ s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num,
+ sizeof(cs), buf, cs.size);
+ if (s != cs.size) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: size mismatch (%zd/%d)",
+ __func__, s, cs.size);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if (virtio_gpu_stats_enabled(g->conf)) {
+ g->stats.req_3d++;
+ g->stats.bytes_3d += cs.size;
+ }
+
+ virgl_renderer_submit_cmd(buf, cs.hdr.ctx_id, cs.size / 4);
+
+out:
+ g_free(buf);
+}
+
+static void virgl_cmd_transfer_to_host_2d(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_transfer_to_host_2d t2d;
+ struct virtio_gpu_box box;
+
+ VIRTIO_GPU_FILL_CMD(t2d);
+ trace_virtio_gpu_cmd_res_xfer_toh_2d(t2d.resource_id);
+
+ box.x = t2d.r.x;
+ box.y = t2d.r.y;
+ box.z = 0;
+ box.w = t2d.r.width;
+ box.h = t2d.r.height;
+ box.d = 1;
+
+ virgl_renderer_transfer_write_iov(t2d.resource_id,
+ 0,
+ 0,
+ 0,
+ 0,
+ (struct virgl_box *)&box,
+ t2d.offset, NULL, 0);
+}
+
+static void virgl_cmd_transfer_to_host_3d(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_transfer_host_3d t3d;
+
+ VIRTIO_GPU_FILL_CMD(t3d);
+ trace_virtio_gpu_cmd_res_xfer_toh_3d(t3d.resource_id);
+
+ virgl_renderer_transfer_write_iov(t3d.resource_id,
+ t3d.hdr.ctx_id,
+ t3d.level,
+ t3d.stride,
+ t3d.layer_stride,
+ (struct virgl_box *)&t3d.box,
+ t3d.offset, NULL, 0);
+}
+
+static void
+virgl_cmd_transfer_from_host_3d(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_transfer_host_3d tf3d;
+
+ VIRTIO_GPU_FILL_CMD(tf3d);
+ trace_virtio_gpu_cmd_res_xfer_fromh_3d(tf3d.resource_id);
+
+ virgl_renderer_transfer_read_iov(tf3d.resource_id,
+ tf3d.hdr.ctx_id,
+ tf3d.level,
+ tf3d.stride,
+ tf3d.layer_stride,
+ (struct virgl_box *)&tf3d.box,
+ tf3d.offset, NULL, 0);
+}
+
+
+static void virgl_resource_attach_backing(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_resource_attach_backing att_rb;
+ struct iovec *res_iovs;
+ int ret;
+
+ VIRTIO_GPU_FILL_CMD(att_rb);
+ trace_virtio_gpu_cmd_res_back_attach(att_rb.resource_id);
+
+ ret = virtio_gpu_create_mapping_iov(&att_rb, cmd, &res_iovs);
+ if (ret != 0) {
+ cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+ return;
+ }
+
+ virgl_renderer_resource_attach_iov(att_rb.resource_id,
+ res_iovs, att_rb.nr_entries);
+}
+
+static void virgl_resource_detach_backing(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_resource_detach_backing detach_rb;
+ struct iovec *res_iovs = NULL;
+ int num_iovs = 0;
+
+ VIRTIO_GPU_FILL_CMD(detach_rb);
+ trace_virtio_gpu_cmd_res_back_detach(detach_rb.resource_id);
+
+ virgl_renderer_resource_detach_iov(detach_rb.resource_id,
+ &res_iovs,
+ &num_iovs);
+ if (res_iovs == NULL || num_iovs == 0) {
+ return;
+ }
+ virtio_gpu_cleanup_mapping_iov(res_iovs, num_iovs);
+}
+
+
+static void virgl_cmd_ctx_attach_resource(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_ctx_resource att_res;
+
+ VIRTIO_GPU_FILL_CMD(att_res);
+ trace_virtio_gpu_cmd_ctx_res_attach(att_res.hdr.ctx_id,
+ att_res.resource_id);
+
+ virgl_renderer_ctx_attach_resource(att_res.hdr.ctx_id, att_res.resource_id);
+}
+
+static void virgl_cmd_ctx_detach_resource(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_ctx_resource det_res;
+
+ VIRTIO_GPU_FILL_CMD(det_res);
+ trace_virtio_gpu_cmd_ctx_res_detach(det_res.hdr.ctx_id,
+ det_res.resource_id);
+
+ virgl_renderer_ctx_detach_resource(det_res.hdr.ctx_id, det_res.resource_id);
+}
+
+static void virgl_cmd_get_capset_info(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_get_capset_info info;
+ struct virtio_gpu_resp_capset_info resp;
+
+ VIRTIO_GPU_FILL_CMD(info);
+
+ if (info.capset_index == 0) {
+ resp.capset_id = VIRTIO_GPU_CAPSET_VIRGL;
+ virgl_renderer_get_cap_set(resp.capset_id,
+ &resp.capset_max_version,
+ &resp.capset_max_size);
+ } else {
+ resp.capset_max_version = 0;
+ resp.capset_max_size = 0;
+ }
+ resp.hdr.type = VIRTIO_GPU_RESP_OK_CAPSET_INFO;
+ virtio_gpu_ctrl_response(g, cmd, &resp.hdr, sizeof(resp));
+}
+
+static void virgl_cmd_get_capset(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_get_capset gc;
+ struct virtio_gpu_resp_capset *resp;
+ uint32_t max_ver, max_size;
+ VIRTIO_GPU_FILL_CMD(gc);
+
+ virgl_renderer_get_cap_set(gc.capset_id, &max_ver,
+ &max_size);
+ resp = g_malloc(sizeof(*resp) + max_size);
+
+ resp->hdr.type = VIRTIO_GPU_RESP_OK_CAPSET;
+ virgl_renderer_fill_caps(gc.capset_id,
+ gc.capset_version,
+ (void *)resp->capset_data);
+ virtio_gpu_ctrl_response(g, cmd, &resp->hdr, sizeof(*resp) + max_size);
+ g_free(resp);
+}
+
+void virtio_gpu_virgl_process_cmd(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ VIRTIO_GPU_FILL_CMD(cmd->cmd_hdr);
+
+ cmd->waiting = g->renderer_blocked;
+ if (cmd->waiting) {
+ return;
+ }
+
+ virgl_renderer_force_ctx_0();
+ switch (cmd->cmd_hdr.type) {
+ case VIRTIO_GPU_CMD_CTX_CREATE:
+ virgl_cmd_context_create(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_CTX_DESTROY:
+ virgl_cmd_context_destroy(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_CREATE_2D:
+ virgl_cmd_create_resource_2d(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_CREATE_3D:
+ virgl_cmd_create_resource_3d(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_SUBMIT_3D:
+ virgl_cmd_submit_3d(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D:
+ virgl_cmd_transfer_to_host_2d(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D:
+ virgl_cmd_transfer_to_host_3d(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D:
+ virgl_cmd_transfer_from_host_3d(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING:
+ virgl_resource_attach_backing(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING:
+ virgl_resource_detach_backing(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_SET_SCANOUT:
+ virgl_cmd_set_scanout(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_FLUSH:
+ virgl_cmd_resource_flush(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_UNREF:
+ virgl_cmd_resource_unref(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE:
+ /* TODO add security */
+ virgl_cmd_ctx_attach_resource(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE:
+ /* TODO add security */
+ virgl_cmd_ctx_detach_resource(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_GET_CAPSET_INFO:
+ virgl_cmd_get_capset_info(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_GET_CAPSET:
+ virgl_cmd_get_capset(g, cmd);
+ break;
+
+ case VIRTIO_GPU_CMD_GET_DISPLAY_INFO:
+ virtio_gpu_get_display_info(g, cmd);
+ break;
+ default:
+ cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+ break;
+ }
+
+ if (cmd->finished) {
+ return;
+ }
+ if (cmd->error) {
+ fprintf(stderr, "%s: ctrl 0x%x, error 0x%x\n", __func__,
+ cmd->cmd_hdr.type, cmd->error);
+ virtio_gpu_ctrl_response_nodata(g, cmd, cmd->error);
+ return;
+ }
+ if (!(cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_FENCE)) {
+ virtio_gpu_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA);
+ return;
+ }
+
+ trace_virtio_gpu_fence_ctrl(cmd->cmd_hdr.fence_id, cmd->cmd_hdr.type);
+ virgl_renderer_create_fence(cmd->cmd_hdr.fence_id, cmd->cmd_hdr.type);
+}
+
+static void virgl_write_fence(void *opaque, uint32_t fence)
+{
+ VirtIOGPU *g = opaque;
+ struct virtio_gpu_ctrl_command *cmd, *tmp;
+
+ QTAILQ_FOREACH_SAFE(cmd, &g->fenceq, next, tmp) {
+ /*
+ * the guest can end up emitting fences out of order
+ * so we should check all fenced cmds not just the first one.
+ */
+ if (cmd->cmd_hdr.fence_id > fence) {
+ continue;
+ }
+ trace_virtio_gpu_fence_resp(cmd->cmd_hdr.fence_id);
+ virtio_gpu_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA);
+ QTAILQ_REMOVE(&g->fenceq, cmd, next);
+ g_free(cmd);
+ g->inflight--;
+ if (virtio_gpu_stats_enabled(g->conf)) {
+ fprintf(stderr, "inflight: %3d (-)\r", g->inflight);
+ }
+ }
+}
+
+static virgl_renderer_gl_context
+virgl_create_context(void *opaque, int scanout_idx,
+ struct virgl_renderer_gl_ctx_param *params)
+{
+ VirtIOGPU *g = opaque;
+ QEMUGLContext ctx;
+ QEMUGLParams qparams;
+
+ qparams.major_ver = params->major_ver;
+ qparams.minor_ver = params->minor_ver;
+
+ ctx = dpy_gl_ctx_create(g->scanout[scanout_idx].con, &qparams);
+ return (virgl_renderer_gl_context)ctx;
+}
+
+static void virgl_destroy_context(void *opaque, virgl_renderer_gl_context ctx)
+{
+ VirtIOGPU *g = opaque;
+ QEMUGLContext qctx = (QEMUGLContext)ctx;
+
+ dpy_gl_ctx_destroy(g->scanout[0].con, qctx);
+}
+
+static int virgl_make_context_current(void *opaque, int scanout_idx,
+ virgl_renderer_gl_context ctx)
+{
+ VirtIOGPU *g = opaque;
+ QEMUGLContext qctx = (QEMUGLContext)ctx;
+
+ return dpy_gl_ctx_make_current(g->scanout[scanout_idx].con, qctx);
+}
+
+static struct virgl_renderer_callbacks virtio_gpu_3d_cbs = {
+ .version = 1,
+ .write_fence = virgl_write_fence,
+ .create_gl_context = virgl_create_context,
+ .destroy_gl_context = virgl_destroy_context,
+ .make_current = virgl_make_context_current,
+};
+
+static void virtio_gpu_print_stats(void *opaque)
+{
+ VirtIOGPU *g = opaque;
+
+ if (g->stats.requests) {
+ fprintf(stderr, "stats: vq req %4d, %3d -- 3D %4d (%5d)\n",
+ g->stats.requests,
+ g->stats.max_inflight,
+ g->stats.req_3d,
+ g->stats.bytes_3d);
+ g->stats.requests = 0;
+ g->stats.max_inflight = 0;
+ g->stats.req_3d = 0;
+ g->stats.bytes_3d = 0;
+ } else {
+ fprintf(stderr, "stats: idle\r");
+ }
+ timer_mod(g->print_stats, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000);
+}
+
+static void virtio_gpu_fence_poll(void *opaque)
+{
+ VirtIOGPU *g = opaque;
+
+ virgl_renderer_poll();
+ virtio_gpu_process_cmdq(g);
+ if (!QTAILQ_EMPTY(&g->cmdq) || !QTAILQ_EMPTY(&g->fenceq)) {
+ timer_mod(g->fence_poll, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 10);
+ }
+}
+
+void virtio_gpu_virgl_fence_poll(VirtIOGPU *g)
+{
+ virtio_gpu_fence_poll(g);
+}
+
+void virtio_gpu_virgl_reset(VirtIOGPU *g)
+{
+ int i;
+
+ /* virgl_renderer_reset() ??? */
+ for (i = 0; i < g->conf.max_outputs; i++) {
+ if (i != 0) {
+ dpy_gfx_replace_surface(g->scanout[i].con, NULL);
+ }
+ dpy_gl_scanout(g->scanout[i].con, 0, false, 0, 0, 0, 0);
+ }
+}
+
+int virtio_gpu_virgl_init(VirtIOGPU *g)
+{
+ int ret;
+
+ ret = virgl_renderer_init(g, 0, &virtio_gpu_3d_cbs);
+ if (ret != 0) {
+ return ret;
+ }
+
+ g->fence_poll = timer_new_ms(QEMU_CLOCK_VIRTUAL,
+ virtio_gpu_fence_poll, g);
+
+ if (virtio_gpu_stats_enabled(g->conf)) {
+ g->print_stats = timer_new_ms(QEMU_CLOCK_VIRTUAL,
+ virtio_gpu_print_stats, g);
+ timer_mod(g->print_stats, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000);
+ }
+ return 0;
+}
+
+#endif /* CONFIG_VIRGL */
diff --git a/qemu/hw/display/virtio-gpu-pci.c b/qemu/hw/display/virtio-gpu-pci.c
index 5bc62cf34..a71b230d3 100644
--- a/qemu/hw/display/virtio-gpu-pci.c
+++ b/qemu/hw/display/virtio-gpu-pci.c
@@ -6,10 +6,11 @@
* Authors:
* Dave Airlie
*
- * This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
+ * 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 "hw/pci/pci.h"
#include "hw/virtio/virtio.h"
#include "hw/virtio/virtio-bus.h"
diff --git a/qemu/hw/display/virtio-gpu.c b/qemu/hw/display/virtio-gpu.c
index a67d927f5..c181fb364 100644
--- a/qemu/hw/display/virtio-gpu.c
+++ b/qemu/hw/display/virtio-gpu.c
@@ -7,10 +7,11 @@
* Dave Airlie <airlied@redhat.com>
* Gerd Hoffmann <kraxel@redhat.com>
*
- * This work is licensed under the terms of the GNU GPL, version 2.
+ * 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 "qemu/iov.h"
#include "ui/console.h"
@@ -22,6 +23,23 @@
static struct virtio_gpu_simple_resource*
virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id);
+#ifdef CONFIG_VIRGL
+#include "virglrenderer.h"
+#define VIRGL(_g, _virgl, _simple, ...) \
+ do { \
+ if (_g->use_virgl_renderer) { \
+ _virgl(__VA_ARGS__); \
+ } else { \
+ _simple(__VA_ARGS__); \
+ } \
+ } while (0)
+#else
+#define VIRGL(_g, _virgl, _simple, ...) \
+ do { \
+ _simple(__VA_ARGS__); \
+ } while (0)
+#endif
+
static void update_cursor_data_simple(VirtIOGPU *g,
struct virtio_gpu_scanout *s,
uint32_t resource_id)
@@ -45,16 +63,49 @@ static void update_cursor_data_simple(VirtIOGPU *g,
pixels * sizeof(uint32_t));
}
+#ifdef CONFIG_VIRGL
+
+static void update_cursor_data_virgl(VirtIOGPU *g,
+ struct virtio_gpu_scanout *s,
+ uint32_t resource_id)
+{
+ uint32_t width, height;
+ uint32_t pixels, *data;
+
+ data = virgl_renderer_get_cursor_data(resource_id, &width, &height);
+ if (!data) {
+ return;
+ }
+
+ if (width != s->current_cursor->width ||
+ height != s->current_cursor->height) {
+ return;
+ }
+
+ pixels = s->current_cursor->width * s->current_cursor->height;
+ memcpy(s->current_cursor->data, data, pixels * sizeof(uint32_t));
+ free(data);
+}
+
+#endif
+
static void update_cursor(VirtIOGPU *g, struct virtio_gpu_update_cursor *cursor)
{
struct virtio_gpu_scanout *s;
+ bool move = cursor->hdr.type != VIRTIO_GPU_CMD_MOVE_CURSOR;
if (cursor->pos.scanout_id >= g->conf.max_outputs) {
return;
}
s = &g->scanout[cursor->pos.scanout_id];
- if (cursor->hdr.type != VIRTIO_GPU_CMD_MOVE_CURSOR) {
+ trace_virtio_gpu_update_cursor(cursor->pos.scanout_id,
+ cursor->pos.x,
+ cursor->pos.y,
+ move ? "move" : "update",
+ cursor->resource_id);
+
+ if (move) {
if (!s->current_cursor) {
s->current_cursor = cursor_alloc(64, 64);
}
@@ -63,7 +114,8 @@ static void update_cursor(VirtIOGPU *g, struct virtio_gpu_update_cursor *cursor)
s->current_cursor->hot_y = cursor->hot_y;
if (cursor->resource_id > 0) {
- update_cursor_data_simple(g, s, cursor->resource_id);
+ VIRGL(g, update_cursor_data_virgl, update_cursor_data_simple,
+ g, s, cursor->resource_id);
}
dpy_cursor_define(s->con, s->current_cursor);
}
@@ -92,9 +144,23 @@ static void virtio_gpu_set_config(VirtIODevice *vdev, const uint8_t *config)
static uint64_t virtio_gpu_get_features(VirtIODevice *vdev, uint64_t features,
Error **errp)
{
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+
+ if (virtio_gpu_virgl_enabled(g->conf)) {
+ features |= (1 << VIRTIO_GPU_F_VIRGL);
+ }
return features;
}
+static void virtio_gpu_set_features(VirtIODevice *vdev, uint64_t features)
+{
+ static const uint32_t virgl = (1 << VIRTIO_GPU_F_VIRGL);
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+
+ g->use_virgl_renderer = ((features & virgl) == virgl);
+ trace_virtio_gpu_features(g->use_virgl_renderer);
+}
+
static void virtio_gpu_notify_event(VirtIOGPU *g, uint32_t event_type)
{
g->virtio_config.events_read |= event_type;
@@ -506,10 +572,7 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g,
scanout->width != ss.r.width ||
scanout->height != ss.r.height) {
/* realloc the surface ptr */
- scanout->ds = qemu_create_displaysurface_from
- (ss.r.width, ss.r.height, format,
- pixman_image_get_stride(res->image),
- (uint8_t *)pixman_image_get_data(res->image) + offset);
+ scanout->ds = qemu_create_displaysurface_pixman(res->image);
if (!scanout->ds) {
cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
return;
@@ -563,7 +626,6 @@ int virtio_gpu_create_mapping_iov(struct virtio_gpu_resource_attach_backing *ab,
__func__, ab->resource_id, i);
virtio_gpu_cleanup_mapping_iov(*iov, i);
g_free(ents);
- g_free(*iov);
*iov = NULL;
return -1;
}
@@ -580,12 +642,12 @@ void virtio_gpu_cleanup_mapping_iov(struct iovec *iov, uint32_t count)
cpu_physical_memory_unmap(iov[i].iov_base, iov[i].iov_len, 1,
iov[i].iov_len);
}
+ g_free(iov);
}
static void virtio_gpu_cleanup_mapping(struct virtio_gpu_simple_resource *res)
{
virtio_gpu_cleanup_mapping_iov(res->iov, res->iov_cnt);
- g_free(res->iov);
res->iov = NULL;
res->iov_cnt = 0;
}
@@ -690,6 +752,39 @@ static void virtio_gpu_handle_cursor_cb(VirtIODevice *vdev, VirtQueue *vq)
qemu_bh_schedule(g->cursor_bh);
}
+void virtio_gpu_process_cmdq(VirtIOGPU *g)
+{
+ struct virtio_gpu_ctrl_command *cmd;
+
+ while (!QTAILQ_EMPTY(&g->cmdq)) {
+ cmd = QTAILQ_FIRST(&g->cmdq);
+
+ /* process command */
+ VIRGL(g, virtio_gpu_virgl_process_cmd, virtio_gpu_simple_process_cmd,
+ g, cmd);
+ if (cmd->waiting) {
+ break;
+ }
+ QTAILQ_REMOVE(&g->cmdq, cmd, next);
+ if (virtio_gpu_stats_enabled(g->conf)) {
+ g->stats.requests++;
+ }
+
+ if (!cmd->finished) {
+ QTAILQ_INSERT_TAIL(&g->fenceq, cmd, next);
+ g->inflight++;
+ if (virtio_gpu_stats_enabled(g->conf)) {
+ if (g->stats.max_inflight < g->inflight) {
+ g->stats.max_inflight = g->inflight;
+ }
+ fprintf(stderr, "inflight: %3d (+)\r", g->inflight);
+ }
+ } else {
+ g_free(cmd);
+ }
+ }
+}
+
static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
{
VirtIOGPU *g = VIRTIO_GPU(vdev);
@@ -699,25 +794,30 @@ static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
return;
}
- cmd = g_new(struct virtio_gpu_ctrl_command, 1);
- while (virtqueue_pop(vq, &cmd->elem)) {
+#ifdef CONFIG_VIRGL
+ if (!g->renderer_inited && g->use_virgl_renderer) {
+ virtio_gpu_virgl_init(g);
+ g->renderer_inited = true;
+ }
+#endif
+
+ cmd = virtqueue_pop(vq, sizeof(struct virtio_gpu_ctrl_command));
+ while (cmd) {
cmd->vq = vq;
cmd->error = 0;
cmd->finished = false;
- g->stats.requests++;
+ cmd->waiting = false;
+ QTAILQ_INSERT_TAIL(&g->cmdq, cmd, next);
+ cmd = virtqueue_pop(vq, sizeof(struct virtio_gpu_ctrl_command));
+ }
- virtio_gpu_simple_process_cmd(g, cmd);
- if (!cmd->finished) {
- QTAILQ_INSERT_TAIL(&g->fenceq, cmd, next);
- g->stats.inflight++;
- if (g->stats.max_inflight < g->stats.inflight) {
- g->stats.max_inflight = g->stats.inflight;
- }
- fprintf(stderr, "inflight: %3d (+)\r", g->stats.inflight);
- cmd = g_new(struct virtio_gpu_ctrl_command, 1);
- }
+ virtio_gpu_process_cmdq(g);
+
+#ifdef CONFIG_VIRGL
+ if (g->use_virgl_renderer) {
+ virtio_gpu_virgl_fence_poll(g);
}
- g_free(cmd);
+#endif
}
static void virtio_gpu_ctrl_bh(void *opaque)
@@ -729,15 +829,20 @@ static void virtio_gpu_ctrl_bh(void *opaque)
static void virtio_gpu_handle_cursor(VirtIODevice *vdev, VirtQueue *vq)
{
VirtIOGPU *g = VIRTIO_GPU(vdev);
- VirtQueueElement elem;
+ VirtQueueElement *elem;
size_t s;
struct virtio_gpu_update_cursor cursor_info;
if (!virtio_queue_ready(vq)) {
return;
}
- while (virtqueue_pop(vq, &elem)) {
- s = iov_to_buf(elem.out_sg, elem.out_num, 0,
+ for (;;) {
+ elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
+ if (!elem) {
+ break;
+ }
+
+ s = iov_to_buf(elem->out_sg, elem->out_num, 0,
&cursor_info, sizeof(cursor_info));
if (s != sizeof(cursor_info)) {
qemu_log_mask(LOG_GUEST_ERROR,
@@ -746,8 +851,9 @@ static void virtio_gpu_handle_cursor(VirtIODevice *vdev, VirtQueue *vq)
} else {
update_cursor(g, &cursor_info);
}
- virtqueue_push(vq, &elem, 0);
+ virtqueue_push(vq, elem, 0);
virtio_notify(vdev, vq);
+ g_free(elem);
}
}
@@ -793,17 +899,34 @@ static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
return 0;
}
+static void virtio_gpu_gl_block(void *opaque, bool block)
+{
+ VirtIOGPU *g = opaque;
+
+ g->renderer_blocked = block;
+ if (!block) {
+ virtio_gpu_process_cmdq(g);
+ }
+}
+
const GraphicHwOps virtio_gpu_ops = {
.invalidate = virtio_gpu_invalidate_display,
.gfx_update = virtio_gpu_update_display,
.text_update = virtio_gpu_text_update,
.ui_info = virtio_gpu_ui_info,
+ .gl_block = virtio_gpu_gl_block,
+};
+
+static const VMStateDescription vmstate_virtio_gpu_unmigratable = {
+ .name = "virtio-gpu",
+ .unmigratable = 1,
};
static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
VirtIOGPU *g = VIRTIO_GPU(qdev);
+ bool have_virgl;
int i;
g->config_size = sizeof(struct virtio_gpu_config);
@@ -814,12 +937,30 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
g->req_state[0].width = 1024;
g->req_state[0].height = 768;
- g->ctrl_vq = virtio_add_queue(vdev, 64, virtio_gpu_handle_ctrl_cb);
- g->cursor_vq = virtio_add_queue(vdev, 16, virtio_gpu_handle_cursor_cb);
+ g->use_virgl_renderer = false;
+#if !defined(CONFIG_VIRGL) || defined(HOST_WORDS_BIGENDIAN)
+ have_virgl = false;
+#else
+ have_virgl = display_opengl;
+#endif
+ if (!have_virgl) {
+ g->conf.flags &= ~(1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED);
+ }
+
+ if (virtio_gpu_virgl_enabled(g->conf)) {
+ /* use larger control queue in 3d mode */
+ g->ctrl_vq = virtio_add_queue(vdev, 256, virtio_gpu_handle_ctrl_cb);
+ g->cursor_vq = virtio_add_queue(vdev, 16, virtio_gpu_handle_cursor_cb);
+ g->virtio_config.num_capsets = 1;
+ } else {
+ g->ctrl_vq = virtio_add_queue(vdev, 64, virtio_gpu_handle_ctrl_cb);
+ g->cursor_vq = virtio_add_queue(vdev, 16, virtio_gpu_handle_cursor_cb);
+ }
g->ctrl_bh = qemu_bh_new(virtio_gpu_ctrl_bh, g);
g->cursor_bh = qemu_bh_new(virtio_gpu_cursor_bh, g);
QTAILQ_INIT(&g->reslist);
+ QTAILQ_INIT(&g->cmdq);
QTAILQ_INIT(&g->fenceq);
g->enabled_output_bitmask = 1;
@@ -832,6 +973,8 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
dpy_gfx_replace_surface(g->scanout[i].con, NULL);
}
}
+
+ vmstate_register(qdev, -1, &vmstate_virtio_gpu_unmigratable, g);
}
static void virtio_gpu_instance_init(Object *obj)
@@ -869,10 +1012,23 @@ static void virtio_gpu_reset(VirtIODevice *vdev)
g->scanout[i].ds = NULL;
}
g->enabled_output_bitmask = 1;
+
+#ifdef CONFIG_VIRGL
+ if (g->use_virgl_renderer) {
+ virtio_gpu_virgl_reset(g);
+ g->use_virgl_renderer = 0;
+ }
+#endif
}
static Property virtio_gpu_properties[] = {
DEFINE_PROP_UINT32("max_outputs", VirtIOGPU, conf.max_outputs, 1),
+#ifdef CONFIG_VIRGL
+ DEFINE_PROP_BIT("virgl", VirtIOGPU, conf.flags,
+ VIRTIO_GPU_FLAG_VIRGL_ENABLED, true),
+ DEFINE_PROP_BIT("stats", VirtIOGPU, conf.flags,
+ VIRTIO_GPU_FLAG_STATS_ENABLED, false),
+#endif
DEFINE_PROP_END_OF_LIST(),
};
@@ -885,6 +1041,7 @@ static void virtio_gpu_class_init(ObjectClass *klass, void *data)
vdc->get_config = virtio_gpu_get_config;
vdc->set_config = virtio_gpu_set_config;
vdc->get_features = virtio_gpu_get_features;
+ vdc->set_features = virtio_gpu_set_features;
vdc->reset = virtio_gpu_reset;
@@ -917,3 +1074,14 @@ QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_mem_entry) != 16);
QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_attach_backing) != 32);
QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_detach_backing) != 32);
QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_display_info) != 408);
+
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_transfer_host_3d) != 72);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_create_3d) != 72);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_create) != 96);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_destroy) != 24);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_resource) != 32);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_cmd_submit) != 32);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_get_capset_info) != 32);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_capset_info) != 40);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_get_capset) != 32);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_capset) != 24);
diff --git a/qemu/hw/display/virtio-vga.c b/qemu/hw/display/virtio-vga.c
index f7e539fe9..e58b165ae 100644
--- a/qemu/hw/display/virtio-vga.c
+++ b/qemu/hw/display/virtio-vga.c
@@ -1,3 +1,4 @@
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "ui/console.h"
@@ -65,11 +66,21 @@ static int virtio_vga_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
return -1;
}
+static void virtio_vga_gl_block(void *opaque, bool block)
+{
+ VirtIOVGA *vvga = opaque;
+
+ if (virtio_gpu_ops.gl_block) {
+ virtio_gpu_ops.gl_block(&vvga->vdev, block);
+ }
+}
+
static const GraphicHwOps virtio_vga_ops = {
.invalidate = virtio_vga_invalidate_display,
.gfx_update = virtio_vga_update_display,
.text_update = virtio_vga_text_update,
.ui_info = virtio_vga_ui_info,
+ .gl_block = virtio_vga_gl_block,
};
/* VGA device wrapper around PCI device around virtio GPU */
diff --git a/qemu/hw/display/vmware_vga.c b/qemu/hw/display/vmware_vga.c
index 7f397d3c2..0c63fa851 100644
--- a/qemu/hw/display/vmware_vga.c
+++ b/qemu/hw/display/vmware_vga.c
@@ -21,6 +21,8 @@
* 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/hw.h"
#include "hw/loader.h"
#include "trace.h"
@@ -488,10 +490,10 @@ static inline int vmsvga_fill_rect(struct vmsvga_state_s *s,
#endif
struct vmsvga_cursor_definition_s {
- int width;
- int height;
+ uint32_t width;
+ uint32_t height;
int id;
- int bpp;
+ uint32_t bpp;
int hot_x;
int hot_y;
uint32_t mask[1024];
@@ -658,7 +660,10 @@ static void vmsvga_fifo_run(struct vmsvga_state_s *s)
cursor.bpp = vmsvga_fifo_read(s);
args = SVGA_BITMAP_SIZE(x, y) + SVGA_PIXMAP_SIZE(x, y, cursor.bpp);
- if (SVGA_BITMAP_SIZE(x, y) > sizeof cursor.mask ||
+ if (cursor.width > 256 ||
+ cursor.height > 256 ||
+ cursor.bpp > 32 ||
+ SVGA_BITMAP_SIZE(x, y) > sizeof cursor.mask ||
SVGA_PIXMAP_SIZE(x, y, cursor.bpp) > sizeof cursor.image) {
goto badcmd;
}
@@ -1244,7 +1249,7 @@ static void vmsvga_init(DeviceState *dev, struct vmsvga_state_s *s,
s->fifo_size = SVGA_FIFO_SIZE;
memory_region_init_ram(&s->fifo_ram, NULL, "vmsvga.fifo", s->fifo_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&s->fifo_ram);
s->fifo_ptr = memory_region_get_ram_ptr(&s->fifo_ram);
diff --git a/qemu/hw/display/xenfb.c b/qemu/hw/display/xenfb.c
index 5e324ef62..9866dfda5 100644
--- a/qemu/hw/display/xenfb.c
+++ b/qemu/hw/display/xenfb.c
@@ -24,16 +24,8 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
-#include <stdarg.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <unistd.h>
+#include "qemu/osdep.h"
#include <sys/mman.h>
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <time.h>
#include "hw/hw.h"
#include "ui/console.h"
@@ -95,23 +87,24 @@ struct XenFB {
static int common_bind(struct common *c)
{
- uint64_t mfn;
+ uint64_t val;
+ xen_pfn_t mfn;
- if (xenstore_read_fe_uint64(&c->xendev, "page-ref", &mfn) == -1)
+ if (xenstore_read_fe_uint64(&c->xendev, "page-ref", &val) == -1)
return -1;
- assert(mfn == (xen_pfn_t)mfn);
+ mfn = (xen_pfn_t)val;
+ assert(val == mfn);
if (xenstore_read_fe_int(&c->xendev, "event-channel", &c->xendev.remote_port) == -1)
return -1;
- c->page = xc_map_foreign_range(xen_xc, c->xendev.dom,
- XC_PAGE_SIZE,
- PROT_READ | PROT_WRITE, mfn);
+ c->page = xenforeignmemory_map(xen_fmem, c->xendev.dom,
+ PROT_READ | PROT_WRITE, 1, &mfn, NULL);
if (c->page == NULL)
return -1;
xen_be_bind_evtchn(&c->xendev);
- xen_be_printf(&c->xendev, 1, "ring mfn %"PRIx64", remote-port %d, local-port %d\n",
+ xen_be_printf(&c->xendev, 1, "ring mfn %"PRI_xen_pfn", remote-port %d, local-port %d\n",
mfn, c->xendev.remote_port, c->xendev.local_port);
return 0;
@@ -121,7 +114,7 @@ static void common_unbind(struct common *c)
{
xen_be_unbind_evtchn(&c->xendev);
if (c->page) {
- munmap(c->page, XC_PAGE_SIZE);
+ xenforeignmemory_unmap(xen_fmem, c->page, 1);
c->page = NULL;
}
}
@@ -248,9 +241,7 @@ static int xenfb_send_motion(struct XenInput *xenfb,
event.type = XENKBD_TYPE_MOTION;
event.motion.rel_x = rel_x;
event.motion.rel_y = rel_y;
-#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030207
event.motion.rel_z = rel_z;
-#endif
return xenfb_kbd_event(xenfb, &event);
}
@@ -265,12 +256,7 @@ static int xenfb_send_position(struct XenInput *xenfb,
event.type = XENKBD_TYPE_POS;
event.pos.abs_x = abs_x;
event.pos.abs_y = abs_y;
-#if __XEN_LATEST_INTERFACE_VERSION__ == 0x00030207
- event.pos.abs_z = z;
-#endif
-#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030208
event.pos.rel_z = z;
-#endif
return xenfb_kbd_event(xenfb, &event);
}
@@ -494,15 +480,15 @@ static int xenfb_map_fb(struct XenFB *xenfb)
fbmfns = g_malloc0(sizeof(xen_pfn_t) * xenfb->fbpages);
xenfb_copy_mfns(mode, n_fbdirs, pgmfns, pd);
- map = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom,
- PROT_READ, pgmfns, n_fbdirs);
+ map = xenforeignmemory_map(xen_fmem, xenfb->c.xendev.dom,
+ PROT_READ, n_fbdirs, pgmfns, NULL);
if (map == NULL)
goto out;
xenfb_copy_mfns(mode, xenfb->fbpages, fbmfns, map);
- munmap(map, n_fbdirs * XC_PAGE_SIZE);
+ xenforeignmemory_unmap(xen_fmem, map, n_fbdirs);
- xenfb->pixels = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom,
- PROT_READ, fbmfns, xenfb->fbpages);
+ xenfb->pixels = xenforeignmemory_map(xen_fmem, xenfb->c.xendev.dom,
+ PROT_READ, xenfb->fbpages, fbmfns, NULL);
if (xenfb->pixels == NULL)
goto out;
@@ -784,18 +770,21 @@ static void xenfb_invalidate(void *opaque)
static void xenfb_handle_events(struct XenFB *xenfb)
{
- uint32_t prod, cons;
+ uint32_t prod, cons, out_cons;
struct xenfb_page *page = xenfb->c.page;
prod = page->out_prod;
- if (prod == page->out_cons)
- return;
+ out_cons = page->out_cons;
+ if (prod - out_cons > XENFB_OUT_RING_LEN) {
+ return;
+ }
xen_rmb(); /* ensure we see ring contents up to prod */
- for (cons = page->out_cons; cons != prod; cons++) {
+ for (cons = out_cons; cons != prod; cons++) {
union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons);
+ uint8_t type = event->type;
int x, y, w, h;
- switch (event->type) {
+ switch (type) {
case XENFB_TYPE_UPDATE:
if (xenfb->up_count == UP_QUEUE)
xenfb->up_fullscreen = 1;
@@ -909,6 +898,7 @@ static void fb_disconnect(struct XenDevice *xendev)
* Replacing the framebuffer with anonymous shared memory
* instead. This releases the guest pages and keeps qemu happy.
*/
+ xenforeignmemory_unmap(xen_fmem, fb->pixels, fb->fbpages);
fb->pixels = mmap(fb->pixels, fb->fbpages * XC_PAGE_SIZE,
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON,
-1, 0);
diff --git a/qemu/hw/dma/Makefile.objs b/qemu/hw/dma/Makefile.objs
index 0e65ed0d7..a1abbcf74 100644
--- a/qemu/hw/dma/Makefile.objs
+++ b/qemu/hw/dma/Makefile.objs
@@ -11,3 +11,4 @@ common-obj-$(CONFIG_SUN4M) += sun4m_iommu.o
obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o
obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o
+obj-$(CONFIG_RASPI) += bcm2835_dma.o
diff --git a/qemu/hw/dma/bcm2835_dma.c b/qemu/hw/dma/bcm2835_dma.c
new file mode 100644
index 000000000..542117599
--- /dev/null
+++ b/qemu/hw/dma/bcm2835_dma.c
@@ -0,0 +1,409 @@
+/*
+ * Raspberry Pi emulation (c) 2012 Gregory Estrade
+ * This code is licensed under the GNU GPLv2 and later.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/dma/bcm2835_dma.h"
+
+/* DMA CS Control and Status bits */
+#define BCM2708_DMA_ACTIVE (1 << 0)
+#define BCM2708_DMA_END (1 << 1) /* GE */
+#define BCM2708_DMA_INT (1 << 2)
+#define BCM2708_DMA_ISPAUSED (1 << 4) /* Pause requested or not active */
+#define BCM2708_DMA_ISHELD (1 << 5) /* Is held by DREQ flow control */
+#define BCM2708_DMA_ERR (1 << 8)
+#define BCM2708_DMA_ABORT (1 << 30) /* stop current CB, go to next, WO */
+#define BCM2708_DMA_RESET (1 << 31) /* WO, self clearing */
+
+/* DMA control block "info" field bits */
+#define BCM2708_DMA_INT_EN (1 << 0)
+#define BCM2708_DMA_TDMODE (1 << 1)
+#define BCM2708_DMA_WAIT_RESP (1 << 3)
+#define BCM2708_DMA_D_INC (1 << 4)
+#define BCM2708_DMA_D_WIDTH (1 << 5)
+#define BCM2708_DMA_D_DREQ (1 << 6)
+#define BCM2708_DMA_D_IGNORE (1 << 7)
+#define BCM2708_DMA_S_INC (1 << 8)
+#define BCM2708_DMA_S_WIDTH (1 << 9)
+#define BCM2708_DMA_S_DREQ (1 << 10)
+#define BCM2708_DMA_S_IGNORE (1 << 11)
+
+/* Register offsets */
+#define BCM2708_DMA_CS 0x00 /* Control and Status */
+#define BCM2708_DMA_ADDR 0x04 /* Control block address */
+/* the current control block appears in the following registers - read only */
+#define BCM2708_DMA_INFO 0x08
+#define BCM2708_DMA_SOURCE_AD 0x0c
+#define BCM2708_DMA_DEST_AD 0x10
+#define BCM2708_DMA_TXFR_LEN 0x14
+#define BCM2708_DMA_STRIDE 0x18
+#define BCM2708_DMA_NEXTCB 0x1C
+#define BCM2708_DMA_DEBUG 0x20
+
+#define BCM2708_DMA_INT_STATUS 0xfe0 /* Interrupt status of each channel */
+#define BCM2708_DMA_ENABLE 0xff0 /* Global enable bits for each channel */
+
+#define BCM2708_DMA_CS_RW_MASK 0x30ff0001 /* All RW bits in DMA_CS */
+
+static void bcm2835_dma_update(BCM2835DMAState *s, unsigned c)
+{
+ BCM2835DMAChan *ch = &s->chan[c];
+ uint32_t data, xlen, ylen;
+ int16_t dst_stride, src_stride;
+
+ if (!(s->enable & (1 << c))) {
+ return;
+ }
+
+ while ((s->enable & (1 << c)) && (ch->conblk_ad != 0)) {
+ /* CB fetch */
+ ch->ti = ldl_le_phys(&s->dma_as, ch->conblk_ad);
+ ch->source_ad = ldl_le_phys(&s->dma_as, ch->conblk_ad + 4);
+ ch->dest_ad = ldl_le_phys(&s->dma_as, ch->conblk_ad + 8);
+ ch->txfr_len = ldl_le_phys(&s->dma_as, ch->conblk_ad + 12);
+ ch->stride = ldl_le_phys(&s->dma_as, ch->conblk_ad + 16);
+ ch->nextconbk = ldl_le_phys(&s->dma_as, ch->conblk_ad + 20);
+
+ if (ch->ti & BCM2708_DMA_TDMODE) {
+ /* 2D transfer mode */
+ ylen = (ch->txfr_len >> 16) & 0x3fff;
+ xlen = ch->txfr_len & 0xffff;
+ dst_stride = ch->stride >> 16;
+ src_stride = ch->stride & 0xffff;
+ } else {
+ ylen = 1;
+ xlen = ch->txfr_len;
+ dst_stride = 0;
+ src_stride = 0;
+ }
+
+ while (ylen != 0) {
+ /* Normal transfer mode */
+ while (xlen != 0) {
+ if (ch->ti & BCM2708_DMA_S_IGNORE) {
+ /* Ignore reads */
+ data = 0;
+ } else {
+ data = ldl_le_phys(&s->dma_as, ch->source_ad);
+ }
+ if (ch->ti & BCM2708_DMA_S_INC) {
+ ch->source_ad += 4;
+ }
+
+ if (ch->ti & BCM2708_DMA_D_IGNORE) {
+ /* Ignore writes */
+ } else {
+ stl_le_phys(&s->dma_as, ch->dest_ad, data);
+ }
+ if (ch->ti & BCM2708_DMA_D_INC) {
+ ch->dest_ad += 4;
+ }
+
+ /* update remaining transfer length */
+ xlen -= 4;
+ if (ch->ti & BCM2708_DMA_TDMODE) {
+ ch->txfr_len = (ylen << 16) | xlen;
+ } else {
+ ch->txfr_len = xlen;
+ }
+ }
+
+ if (--ylen != 0) {
+ ch->source_ad += src_stride;
+ ch->dest_ad += dst_stride;
+ }
+ }
+ ch->cs |= BCM2708_DMA_END;
+ if (ch->ti & BCM2708_DMA_INT_EN) {
+ ch->cs |= BCM2708_DMA_INT;
+ s->int_status |= (1 << c);
+ qemu_set_irq(ch->irq, 1);
+ }
+
+ /* Process next CB */
+ ch->conblk_ad = ch->nextconbk;
+ }
+
+ ch->cs &= ~BCM2708_DMA_ACTIVE;
+ ch->cs |= BCM2708_DMA_ISPAUSED;
+}
+
+static void bcm2835_dma_chan_reset(BCM2835DMAChan *ch)
+{
+ ch->cs = 0;
+ ch->conblk_ad = 0;
+}
+
+static uint64_t bcm2835_dma_read(BCM2835DMAState *s, hwaddr offset,
+ unsigned size, unsigned c)
+{
+ BCM2835DMAChan *ch;
+ uint32_t res = 0;
+
+ assert(size == 4);
+ assert(c < BCM2835_DMA_NCHANS);
+
+ ch = &s->chan[c];
+
+ switch (offset) {
+ case BCM2708_DMA_CS:
+ res = ch->cs;
+ break;
+ case BCM2708_DMA_ADDR:
+ res = ch->conblk_ad;
+ break;
+ case BCM2708_DMA_INFO:
+ res = ch->ti;
+ break;
+ case BCM2708_DMA_SOURCE_AD:
+ res = ch->source_ad;
+ break;
+ case BCM2708_DMA_DEST_AD:
+ res = ch->dest_ad;
+ break;
+ case BCM2708_DMA_TXFR_LEN:
+ res = ch->txfr_len;
+ break;
+ case BCM2708_DMA_STRIDE:
+ res = ch->stride;
+ break;
+ case BCM2708_DMA_NEXTCB:
+ res = ch->nextconbk;
+ break;
+ case BCM2708_DMA_DEBUG:
+ res = ch->debug;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ break;
+ }
+ return res;
+}
+
+static void bcm2835_dma_write(BCM2835DMAState *s, hwaddr offset,
+ uint64_t value, unsigned size, unsigned c)
+{
+ BCM2835DMAChan *ch;
+ uint32_t oldcs;
+
+ assert(size == 4);
+ assert(c < BCM2835_DMA_NCHANS);
+
+ ch = &s->chan[c];
+
+ switch (offset) {
+ case BCM2708_DMA_CS:
+ oldcs = ch->cs;
+ if (value & BCM2708_DMA_RESET) {
+ bcm2835_dma_chan_reset(ch);
+ }
+ if (value & BCM2708_DMA_ABORT) {
+ /* abort is a no-op, since we always run to completion */
+ }
+ if (value & BCM2708_DMA_END) {
+ ch->cs &= ~BCM2708_DMA_END;
+ }
+ if (value & BCM2708_DMA_INT) {
+ ch->cs &= ~BCM2708_DMA_INT;
+ s->int_status &= ~(1 << c);
+ qemu_set_irq(ch->irq, 0);
+ }
+ ch->cs &= ~BCM2708_DMA_CS_RW_MASK;
+ ch->cs |= (value & BCM2708_DMA_CS_RW_MASK);
+ if (!(oldcs & BCM2708_DMA_ACTIVE) && (ch->cs & BCM2708_DMA_ACTIVE)) {
+ bcm2835_dma_update(s, c);
+ }
+ break;
+ case BCM2708_DMA_ADDR:
+ ch->conblk_ad = value;
+ break;
+ case BCM2708_DMA_DEBUG:
+ ch->debug = value;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ break;
+ }
+}
+
+static uint64_t bcm2835_dma0_read(void *opaque, hwaddr offset, unsigned size)
+{
+ BCM2835DMAState *s = opaque;
+
+ if (offset < 0xf00) {
+ return bcm2835_dma_read(s, (offset & 0xff), size, (offset >> 8) & 0xf);
+ } else {
+ switch (offset) {
+ case BCM2708_DMA_INT_STATUS:
+ return s->int_status;
+ case BCM2708_DMA_ENABLE:
+ return s->enable;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return 0;
+ }
+ }
+}
+
+static uint64_t bcm2835_dma15_read(void *opaque, hwaddr offset, unsigned size)
+{
+ return bcm2835_dma_read(opaque, (offset & 0xff), size, 15);
+}
+
+static void bcm2835_dma0_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ BCM2835DMAState *s = opaque;
+
+ if (offset < 0xf00) {
+ bcm2835_dma_write(s, (offset & 0xff), value, size, (offset >> 8) & 0xf);
+ } else {
+ switch (offset) {
+ case BCM2708_DMA_INT_STATUS:
+ break;
+ case BCM2708_DMA_ENABLE:
+ s->enable = (value & 0xffff);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ }
+ }
+
+}
+
+static void bcm2835_dma15_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ bcm2835_dma_write(opaque, (offset & 0xff), value, size, 15);
+}
+
+static const MemoryRegionOps bcm2835_dma0_ops = {
+ .read = bcm2835_dma0_read,
+ .write = bcm2835_dma0_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+static const MemoryRegionOps bcm2835_dma15_ops = {
+ .read = bcm2835_dma15_read,
+ .write = bcm2835_dma15_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+static const VMStateDescription vmstate_bcm2835_dma_chan = {
+ .name = TYPE_BCM2835_DMA "-chan",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(cs, BCM2835DMAChan),
+ VMSTATE_UINT32(conblk_ad, BCM2835DMAChan),
+ VMSTATE_UINT32(ti, BCM2835DMAChan),
+ VMSTATE_UINT32(source_ad, BCM2835DMAChan),
+ VMSTATE_UINT32(dest_ad, BCM2835DMAChan),
+ VMSTATE_UINT32(txfr_len, BCM2835DMAChan),
+ VMSTATE_UINT32(stride, BCM2835DMAChan),
+ VMSTATE_UINT32(nextconbk, BCM2835DMAChan),
+ VMSTATE_UINT32(debug, BCM2835DMAChan),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_bcm2835_dma = {
+ .name = TYPE_BCM2835_DMA,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT_ARRAY(chan, BCM2835DMAState, BCM2835_DMA_NCHANS, 1,
+ vmstate_bcm2835_dma_chan, BCM2835DMAChan),
+ VMSTATE_UINT32(int_status, BCM2835DMAState),
+ VMSTATE_UINT32(enable, BCM2835DMAState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void bcm2835_dma_init(Object *obj)
+{
+ BCM2835DMAState *s = BCM2835_DMA(obj);
+ int n;
+
+ /* DMA channels 0-14 occupy a contiguous block of IO memory, along
+ * with the global enable and interrupt status bits. Channel 15
+ * has the same register map, but is mapped at a discontiguous
+ * address in a separate IO block.
+ */
+ memory_region_init_io(&s->iomem0, OBJECT(s), &bcm2835_dma0_ops, s,
+ TYPE_BCM2835_DMA, 0x1000);
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem0);
+
+ memory_region_init_io(&s->iomem15, OBJECT(s), &bcm2835_dma15_ops, s,
+ TYPE_BCM2835_DMA "-chan15", 0x100);
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem15);
+
+ for (n = 0; n < 16; n++) {
+ sysbus_init_irq(SYS_BUS_DEVICE(s), &s->chan[n].irq);
+ }
+}
+
+static void bcm2835_dma_reset(DeviceState *dev)
+{
+ BCM2835DMAState *s = BCM2835_DMA(dev);
+ int n;
+
+ s->enable = 0xffff;
+ s->int_status = 0;
+ for (n = 0; n < BCM2835_DMA_NCHANS; n++) {
+ bcm2835_dma_chan_reset(&s->chan[n]);
+ }
+}
+
+static void bcm2835_dma_realize(DeviceState *dev, Error **errp)
+{
+ BCM2835DMAState *s = BCM2835_DMA(dev);
+ Error *err = NULL;
+ Object *obj;
+
+ obj = object_property_get_link(OBJECT(dev), "dma-mr", &err);
+ if (obj == NULL) {
+ error_setg(errp, "%s: required dma-mr link not found: %s",
+ __func__, error_get_pretty(err));
+ return;
+ }
+
+ s->dma_mr = MEMORY_REGION(obj);
+ address_space_init(&s->dma_as, s->dma_mr, NULL);
+
+ bcm2835_dma_reset(dev);
+}
+
+static void bcm2835_dma_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = bcm2835_dma_realize;
+ dc->reset = bcm2835_dma_reset;
+ dc->vmsd = &vmstate_bcm2835_dma;
+}
+
+static TypeInfo bcm2835_dma_info = {
+ .name = TYPE_BCM2835_DMA,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BCM2835DMAState),
+ .class_init = bcm2835_dma_class_init,
+ .instance_init = bcm2835_dma_init,
+};
+
+static void bcm2835_dma_register_types(void)
+{
+ type_register_static(&bcm2835_dma_info);
+}
+
+type_init(bcm2835_dma_register_types)
diff --git a/qemu/hw/dma/etraxfs_dma.c b/qemu/hw/dma/etraxfs_dma.c
index 359951341..d5650eb88 100644
--- a/qemu/hw/dma/etraxfs_dma.c
+++ b/qemu/hw/dma/etraxfs_dma.c
@@ -21,8 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-#include <stdio.h>
-#include <sys/time.h>
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "exec/address-spaces.h"
#include "qemu-common.h"
@@ -441,13 +440,16 @@ static int channel_out_run(struct fs_dma_ctrl *ctrl, int c)
D(printf("channel %d pushes %x %u bytes eop=%u\n", c,
saved_data_buf, len, out_eop));
- if (ctrl->channels[c].client->client.push)
- ctrl->channels[c].client->client.push(
- ctrl->channels[c].client->client.opaque,
- buf, len, out_eop);
- else
+ if (ctrl->channels[c].client->client.push) {
+ if (len > 0) {
+ ctrl->channels[c].client->client.push(
+ ctrl->channels[c].client->client.opaque,
+ buf, len, out_eop);
+ }
+ } else {
printf("WARNING: DMA ch%d dataloss,"
" no attached client.\n", c);
+ }
saved_data_buf += len;
diff --git a/qemu/hw/dma/i82374.c b/qemu/hw/dma/i82374.c
index b8ad2e64e..6c0f975df 100644
--- a/qemu/hw/dma/i82374.c
+++ b/qemu/hw/dma/i82374.c
@@ -22,8 +22,12 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/isa/isa.h"
+#define TYPE_I82374 "i82374"
+#define I82374(obj) OBJECT_CHECK(I82374State, (obj), TYPE_I82374)
+
//#define DEBUG_I82374
#ifdef DEBUG_I82374
@@ -37,8 +41,10 @@ do {} while (0)
do { fprintf(stderr, "i82374 ERROR: " fmt , ## __VA_ARGS__); } while (0)
typedef struct I82374State {
+ ISADevice parent_obj;
+
+ uint32_t iobase;
uint8_t commands[8];
- qemu_irq out;
PortioList port_list;
} I82374State;
@@ -99,32 +105,6 @@ static uint32_t i82374_read_descriptor(void *opaque, uint32_t nport)
return val;
}
-static void i82374_realize(I82374State *s, Error **errp)
-{
- DMA_init(1, &s->out);
- memset(s->commands, 0, sizeof(s->commands));
-}
-
-#define TYPE_I82374 "i82374"
-#define I82374(obj) OBJECT_CHECK(ISAi82374State, (obj), TYPE_I82374)
-
-typedef struct ISAi82374State {
- ISADevice parent_obj;
-
- uint32_t iobase;
- I82374State state;
-} ISAi82374State;
-
-static const VMStateDescription vmstate_isa_i82374 = {
- .name = "isa-i82374",
- .version_id = 0,
- .minimum_version_id = 0,
- .fields = (VMStateField[]) {
- VMSTATE_STRUCT(state, ISAi82374State, 0, vmstate_i82374, I82374State),
- VMSTATE_END_OF_LIST()
- },
-};
-
static const MemoryRegionPortio i82374_portio_list[] = {
{ 0x0A, 1, 1, .read = i82374_read_isr, },
{ 0x10, 8, 1, .write = i82374_write_command, },
@@ -134,23 +114,21 @@ static const MemoryRegionPortio i82374_portio_list[] = {
PORTIO_END_OF_LIST(),
};
-static void i82374_isa_realize(DeviceState *dev, Error **errp)
+static void i82374_realize(DeviceState *dev, Error **errp)
{
- ISAi82374State *isa = I82374(dev);
- I82374State *s = &isa->state;
+ I82374State *s = I82374(dev);
- portio_list_init(&s->port_list, OBJECT(isa), i82374_portio_list, s,
+ portio_list_init(&s->port_list, OBJECT(s), i82374_portio_list, s,
"i82374");
- portio_list_add(&s->port_list, isa_address_space_io(&isa->parent_obj),
- isa->iobase);
-
- i82374_realize(s, errp);
+ portio_list_add(&s->port_list, isa_address_space_io(&s->parent_obj),
+ s->iobase);
- qdev_init_gpio_out(dev, &s->out, 1);
+ DMA_init(isa_bus_from_device(ISA_DEVICE(dev)), 1);
+ memset(s->commands, 0, sizeof(s->commands));
}
static Property i82374_properties[] = {
- DEFINE_PROP_UINT32("iobase", ISAi82374State, iobase, 0x400),
+ DEFINE_PROP_UINT32("iobase", I82374State, iobase, 0x400),
DEFINE_PROP_END_OF_LIST()
};
@@ -158,21 +136,21 @@ static void i82374_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- dc->realize = i82374_isa_realize;
- dc->vmsd = &vmstate_isa_i82374;
+ dc->realize = i82374_realize;
+ dc->vmsd = &vmstate_i82374;
dc->props = i82374_properties;
}
-static const TypeInfo i82374_isa_info = {
+static const TypeInfo i82374_info = {
.name = TYPE_I82374,
.parent = TYPE_ISA_DEVICE,
- .instance_size = sizeof(ISAi82374State),
+ .instance_size = sizeof(I82374State),
.class_init = i82374_class_init,
};
static void i82374_register_types(void)
{
- type_register_static(&i82374_isa_info);
+ type_register_static(&i82374_info);
}
type_init(i82374_register_types)
diff --git a/qemu/hw/dma/i8257.c b/qemu/hw/dma/i8257.c
index a414029be..f345c5476 100644
--- a/qemu/hw/dma/i8257.c
+++ b/qemu/hw/dma/i8257.c
@@ -21,11 +21,16 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/isa/isa.h"
+#include "hw/isa/i8257.h"
#include "qemu/main-loop.h"
#include "trace.h"
+#define I8257(obj) \
+ OBJECT_CHECK(I8257State, (obj), TYPE_I8257)
+
/* #define DEBUG_DMA */
#define dolog(...) fprintf (stderr, "dma: " __VA_ARGS__)
@@ -37,33 +42,9 @@
#define ldebug(...)
#endif
-struct dma_regs {
- int now[2];
- uint16_t base[2];
- uint8_t mode;
- uint8_t page;
- uint8_t pageh;
- uint8_t dack;
- uint8_t eop;
- DMA_transfer_handler transfer_handler;
- void *opaque;
-};
-
#define ADDR 0
#define COUNT 1
-static struct dma_cont {
- uint8_t status;
- uint8_t command;
- uint8_t mask;
- uint8_t flip_flop;
- int dshift;
- struct dma_regs regs[4];
- qemu_irq *cpu_request_exit;
- MemoryRegion channel_io;
- MemoryRegion cont_io;
-} dma_controllers[2];
-
enum {
CMD_MEMORY_TO_MEMORY = 0x01,
CMD_FIXED_ADDRESS = 0x02,
@@ -79,13 +60,13 @@ enum {
};
-static void DMA_run (void);
+static void i8257_dma_run(void *opaque);
-static int channels[8] = {-1, 2, 3, 1, -1, -1, -1, 0};
+static const int channels[8] = {-1, 2, 3, 1, -1, -1, -1, 0};
-static void write_page (void *opaque, uint32_t nport, uint32_t data)
+static void i8257_write_page(void *opaque, uint32_t nport, uint32_t data)
{
- struct dma_cont *d = opaque;
+ I8257State *d = opaque;
int ichan;
ichan = channels[nport & 7];
@@ -96,9 +77,9 @@ static void write_page (void *opaque, uint32_t nport, uint32_t data)
d->regs[ichan].page = data;
}
-static void write_pageh (void *opaque, uint32_t nport, uint32_t data)
+static void i8257_write_pageh(void *opaque, uint32_t nport, uint32_t data)
{
- struct dma_cont *d = opaque;
+ I8257State *d = opaque;
int ichan;
ichan = channels[nport & 7];
@@ -109,9 +90,9 @@ static void write_pageh (void *opaque, uint32_t nport, uint32_t data)
d->regs[ichan].pageh = data;
}
-static uint32_t read_page (void *opaque, uint32_t nport)
+static uint32_t i8257_read_page(void *opaque, uint32_t nport)
{
- struct dma_cont *d = opaque;
+ I8257State *d = opaque;
int ichan;
ichan = channels[nport & 7];
@@ -122,9 +103,9 @@ static uint32_t read_page (void *opaque, uint32_t nport)
return d->regs[ichan].page;
}
-static uint32_t read_pageh (void *opaque, uint32_t nport)
+static uint32_t i8257_read_pageh(void *opaque, uint32_t nport)
{
- struct dma_cont *d = opaque;
+ I8257State *d = opaque;
int ichan;
ichan = channels[nport & 7];
@@ -135,16 +116,16 @@ static uint32_t read_pageh (void *opaque, uint32_t nport)
return d->regs[ichan].pageh;
}
-static inline void init_chan (struct dma_cont *d, int ichan)
+static inline void i8257_init_chan(I8257State *d, int ichan)
{
- struct dma_regs *r;
+ I8257Regs *r;
r = d->regs + ichan;
r->now[ADDR] = r->base[ADDR] << d->dshift;
r->now[COUNT] = 0;
}
-static inline int getff (struct dma_cont *d)
+static inline int i8257_getff(I8257State *d)
{
int ff;
@@ -153,11 +134,11 @@ static inline int getff (struct dma_cont *d)
return ff;
}
-static uint64_t read_chan(void *opaque, hwaddr nport, unsigned size)
+static uint64_t i8257_read_chan(void *opaque, hwaddr nport, unsigned size)
{
- struct dma_cont *d = opaque;
+ I8257State *d = opaque;
int ichan, nreg, iport, ff, val, dir;
- struct dma_regs *r;
+ I8257Regs *r;
iport = (nport >> d->dshift) & 0x0f;
ichan = iport >> 1;
@@ -165,7 +146,7 @@ static uint64_t read_chan(void *opaque, hwaddr nport, unsigned size)
r = d->regs + ichan;
dir = ((r->mode >> 5) & 1) ? -1 : 1;
- ff = getff (d);
+ ff = i8257_getff(d);
if (nreg)
val = (r->base[COUNT] << d->dshift) - r->now[COUNT];
else
@@ -175,29 +156,29 @@ static uint64_t read_chan(void *opaque, hwaddr nport, unsigned size)
return (val >> (d->dshift + (ff << 3))) & 0xff;
}
-static void write_chan(void *opaque, hwaddr nport, uint64_t data,
- unsigned size)
+static void i8257_write_chan(void *opaque, hwaddr nport, uint64_t data,
+ unsigned int size)
{
- struct dma_cont *d = opaque;
+ I8257State *d = opaque;
int iport, ichan, nreg;
- struct dma_regs *r;
+ I8257Regs *r;
iport = (nport >> d->dshift) & 0x0f;
ichan = iport >> 1;
nreg = iport & 1;
r = d->regs + ichan;
- if (getff (d)) {
+ if (i8257_getff(d)) {
r->base[nreg] = (r->base[nreg] & 0xff) | ((data << 8) & 0xff00);
- init_chan (d, ichan);
+ i8257_init_chan(d, ichan);
} else {
r->base[nreg] = (r->base[nreg] & 0xff00) | (data & 0xff);
}
}
-static void write_cont(void *opaque, hwaddr nport, uint64_t data,
- unsigned size)
+static void i8257_write_cont(void *opaque, hwaddr nport, uint64_t data,
+ unsigned int size)
{
- struct dma_cont *d = opaque;
+ I8257State *d = opaque;
int iport, ichan = 0;
iport = (nport >> d->dshift) & 0x0f;
@@ -219,7 +200,7 @@ static void write_cont(void *opaque, hwaddr nport, uint64_t data,
d->status &= ~(1 << (ichan + 4));
}
d->status &= ~(1 << ichan);
- DMA_run();
+ i8257_dma_run(d);
break;
case 0x02: /* single mask */
@@ -227,7 +208,7 @@ static void write_cont(void *opaque, hwaddr nport, uint64_t data,
d->mask |= 1 << (data & 3);
else
d->mask &= ~(1 << (data & 3));
- DMA_run();
+ i8257_dma_run(d);
break;
case 0x03: /* mode */
@@ -262,12 +243,12 @@ static void write_cont(void *opaque, hwaddr nport, uint64_t data,
case 0x06: /* clear mask for all channels */
d->mask = 0;
- DMA_run();
+ i8257_dma_run(d);
break;
case 0x07: /* write mask for all channels */
d->mask = data;
- DMA_run();
+ i8257_dma_run(d);
break;
default:
@@ -283,9 +264,9 @@ static void write_cont(void *opaque, hwaddr nport, uint64_t data,
#endif
}
-static uint64_t read_cont(void *opaque, hwaddr nport, unsigned size)
+static uint64_t i8257_read_cont(void *opaque, hwaddr nport, unsigned size)
{
- struct dma_cont *d = opaque;
+ I8257State *d = opaque;
int iport, val;
iport = (nport >> d->dshift) & 0x0f;
@@ -306,37 +287,43 @@ static uint64_t read_cont(void *opaque, hwaddr nport, unsigned size)
return val;
}
-int DMA_get_channel_mode (int nchan)
+static IsaDmaTransferMode i8257_dma_get_transfer_mode(IsaDma *obj, int nchan)
{
- return dma_controllers[nchan > 3].regs[nchan & 3].mode;
+ I8257State *d = I8257(obj);
+ return (d->regs[nchan & 3].mode >> 2) & 3;
}
-void DMA_hold_DREQ (int nchan)
+static bool i8257_dma_has_autoinitialization(IsaDma *obj, int nchan)
{
- int ncont, ichan;
+ I8257State *d = I8257(obj);
+ return (d->regs[nchan & 3].mode >> 4) & 1;
+}
+
+static void i8257_dma_hold_DREQ(IsaDma *obj, int nchan)
+{
+ I8257State *d = I8257(obj);
+ int ichan;
- ncont = nchan > 3;
ichan = nchan & 3;
- linfo ("held cont=%d chan=%d\n", ncont, ichan);
- dma_controllers[ncont].status |= 1 << (ichan + 4);
- DMA_run();
+ d->status |= 1 << (ichan + 4);
+ i8257_dma_run(d);
}
-void DMA_release_DREQ (int nchan)
+static void i8257_dma_release_DREQ(IsaDma *obj, int nchan)
{
- int ncont, ichan;
+ I8257State *d = I8257(obj);
+ int ichan;
- ncont = nchan > 3;
ichan = nchan & 3;
- linfo ("released cont=%d chan=%d\n", ncont, ichan);
- dma_controllers[ncont].status &= ~(1 << (ichan + 4));
- DMA_run();
+ d->status &= ~(1 << (ichan + 4));
+ i8257_dma_run(d);
}
-static void channel_run (int ncont, int ichan)
+static void i8257_channel_run(I8257State *d, int ichan)
{
+ int ncont = d->dshift;
int n;
- struct dma_regs *r = &dma_controllers[ncont].regs[ichan];
+ I8257Regs *r = &d->regs[ichan];
#ifdef DEBUG_DMA
int dir, opmode;
@@ -355,68 +342,64 @@ static void channel_run (int ncont, int ichan)
r->now[COUNT], (r->base[COUNT] + 1) << ncont);
r->now[COUNT] = n;
ldebug ("dma_pos %d size %d\n", n, (r->base[COUNT] + 1) << ncont);
+ if (n == (r->base[COUNT] + 1) << ncont) {
+ ldebug("transfer done\n");
+ d->status |= (1 << ichan);
+ }
}
-static QEMUBH *dma_bh;
-
-static void DMA_run (void)
+static void i8257_dma_run(void *opaque)
{
- struct dma_cont *d;
- int icont, ichan;
+ I8257State *d = opaque;
+ int ichan;
int rearm = 0;
- static int running = 0;
- if (running) {
+ if (d->running) {
rearm = 1;
goto out;
} else {
- running = 1;
+ d->running = 1;
}
- d = dma_controllers;
+ for (ichan = 0; ichan < 4; ichan++) {
+ int mask;
- for (icont = 0; icont < 2; icont++, d++) {
- for (ichan = 0; ichan < 4; ichan++) {
- int mask;
+ mask = 1 << ichan;
- mask = 1 << ichan;
-
- if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4)))) {
- channel_run (icont, ichan);
- rearm = 1;
- }
+ if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4)))) {
+ i8257_channel_run(d, ichan);
+ rearm = 1;
}
}
- running = 0;
+ d->running = 0;
out:
- if (rearm)
- qemu_bh_schedule_idle(dma_bh);
-}
-
-static void DMA_run_bh(void *unused)
-{
- DMA_run();
+ if (rearm) {
+ qemu_bh_schedule_idle(d->dma_bh);
+ d->dma_bh_scheduled = true;
+ }
}
-void DMA_register_channel (int nchan,
- DMA_transfer_handler transfer_handler,
- void *opaque)
+static void i8257_dma_register_channel(IsaDma *obj, int nchan,
+ IsaDmaTransferHandler transfer_handler,
+ void *opaque)
{
- struct dma_regs *r;
- int ichan, ncont;
+ I8257State *d = I8257(obj);
+ I8257Regs *r;
+ int ichan;
- ncont = nchan > 3;
ichan = nchan & 3;
- r = dma_controllers[ncont].regs + ichan;
+ r = d->regs + ichan;
r->transfer_handler = transfer_handler;
r->opaque = opaque;
}
-int DMA_read_memory (int nchan, void *buf, int pos, int len)
+static int i8257_dma_read_memory(IsaDma *obj, int nchan, void *buf, int pos,
+ int len)
{
- struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3];
+ I8257State *d = I8257(obj);
+ I8257Regs *r = &d->regs[nchan & 3];
hwaddr addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
if (r->mode & 0x20) {
@@ -436,9 +419,11 @@ int DMA_read_memory (int nchan, void *buf, int pos, int len)
return len;
}
-int DMA_write_memory (int nchan, void *buf, int pos, int len)
+static int i8257_dma_write_memory(IsaDma *obj, int nchan, void *buf, int pos,
+ int len)
{
- struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3];
+ I8257State *s = I8257(obj);
+ I8257Regs *r = &s->regs[nchan & 3];
hwaddr addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
if (r->mode & 0x20) {
@@ -458,21 +443,25 @@ int DMA_write_memory (int nchan, void *buf, int pos, int len)
return len;
}
-/* request the emulator to transfer a new DMA memory block ASAP */
-void DMA_schedule(int nchan)
+/* request the emulator to transfer a new DMA memory block ASAP (even
+ * if the idle bottom half would not have exited the iothread yet).
+ */
+static void i8257_dma_schedule(IsaDma *obj)
{
- struct dma_cont *d = &dma_controllers[nchan > 3];
-
- qemu_irq_pulse(*d->cpu_request_exit);
+ I8257State *d = I8257(obj);
+ if (d->dma_bh_scheduled) {
+ qemu_notify_event();
+ }
}
-static void dma_reset(void *opaque)
+static void i8257_reset(DeviceState *dev)
{
- struct dma_cont *d = opaque;
- write_cont(d, (0x05 << d->dshift), 0, 1);
+ I8257State *d = I8257(dev);
+ i8257_write_cont(d, (0x05 << d->dshift), 0, 1);
}
-static int dma_phony_handler (void *opaque, int nchan, int dma_pos, int dma_len)
+static int i8257_phony_handler(void *opaque, int nchan, int dma_pos,
+ int dma_len)
{
trace_i8257_unregistered_dma(nchan, dma_pos, dma_len);
return dma_pos;
@@ -480,8 +469,8 @@ static int dma_phony_handler (void *opaque, int nchan, int dma_pos, int dma_len)
static const MemoryRegionOps channel_io_ops = {
- .read = read_chan,
- .write = write_chan,
+ .read = i8257_read_chan,
+ .write = i8257_write_chan,
.endianness = DEVICE_NATIVE_ENDIAN,
.impl = {
.min_access_size = 1,
@@ -491,21 +480,21 @@ static const MemoryRegionOps channel_io_ops = {
/* IOport from page_base */
static const MemoryRegionPortio page_portio_list[] = {
- { 0x01, 3, 1, .write = write_page, .read = read_page, },
- { 0x07, 1, 1, .write = write_page, .read = read_page, },
+ { 0x01, 3, 1, .write = i8257_write_page, .read = i8257_read_page, },
+ { 0x07, 1, 1, .write = i8257_write_page, .read = i8257_read_page, },
PORTIO_END_OF_LIST(),
};
/* IOport from pageh_base */
static const MemoryRegionPortio pageh_portio_list[] = {
- { 0x01, 3, 1, .write = write_pageh, .read = read_pageh, },
- { 0x07, 3, 1, .write = write_pageh, .read = read_pageh, },
+ { 0x01, 3, 1, .write = i8257_write_pageh, .read = i8257_read_pageh, },
+ { 0x07, 3, 1, .write = i8257_write_pageh, .read = i8257_read_pageh, },
PORTIO_END_OF_LIST(),
};
static const MemoryRegionOps cont_io_ops = {
- .read = read_cont,
- .write = write_cont,
+ .read = i8257_read_cont,
+ .write = i8257_write_cont,
.endianness = DEVICE_NATIVE_ENDIAN,
.impl = {
.min_access_size = 1,
@@ -513,86 +502,142 @@ static const MemoryRegionOps cont_io_ops = {
},
};
-/* dshift = 0: 8 bit DMA, 1 = 16 bit DMA */
-static void dma_init2(struct dma_cont *d, int base, int dshift,
- int page_base, int pageh_base,
- qemu_irq *cpu_request_exit)
+static const VMStateDescription vmstate_i8257_regs = {
+ .name = "dma_regs",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32_ARRAY(now, I8257Regs, 2),
+ VMSTATE_UINT16_ARRAY(base, I8257Regs, 2),
+ VMSTATE_UINT8(mode, I8257Regs),
+ VMSTATE_UINT8(page, I8257Regs),
+ VMSTATE_UINT8(pageh, I8257Regs),
+ VMSTATE_UINT8(dack, I8257Regs),
+ VMSTATE_UINT8(eop, I8257Regs),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int i8257_post_load(void *opaque, int version_id)
{
- int i;
+ I8257State *d = opaque;
+ i8257_dma_run(d);
- d->dshift = dshift;
- d->cpu_request_exit = cpu_request_exit;
+ return 0;
+}
+
+static const VMStateDescription vmstate_i8257 = {
+ .name = "dma",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .post_load = i8257_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(command, I8257State),
+ VMSTATE_UINT8(mask, I8257State),
+ VMSTATE_UINT8(flip_flop, I8257State),
+ VMSTATE_INT32(dshift, I8257State),
+ VMSTATE_STRUCT_ARRAY(regs, I8257State, 4, 1, vmstate_i8257_regs,
+ I8257Regs),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void i8257_realize(DeviceState *dev, Error **errp)
+{
+ ISADevice *isa = ISA_DEVICE(dev);
+ I8257State *d = I8257(dev);
+ int i;
memory_region_init_io(&d->channel_io, NULL, &channel_io_ops, d,
"dma-chan", 8 << d->dshift);
- memory_region_add_subregion(isa_address_space_io(NULL),
- base, &d->channel_io);
+ memory_region_add_subregion(isa_address_space_io(isa),
+ d->base, &d->channel_io);
- isa_register_portio_list(NULL, page_base, page_portio_list, d,
+ isa_register_portio_list(isa, d->page_base, page_portio_list, d,
"dma-page");
- if (pageh_base >= 0) {
- isa_register_portio_list(NULL, pageh_base, pageh_portio_list, d,
+ if (d->pageh_base >= 0) {
+ isa_register_portio_list(isa, d->pageh_base, pageh_portio_list, d,
"dma-pageh");
}
- memory_region_init_io(&d->cont_io, NULL, &cont_io_ops, d, "dma-cont",
- 8 << d->dshift);
- memory_region_add_subregion(isa_address_space_io(NULL),
- base + (8 << d->dshift), &d->cont_io);
+ memory_region_init_io(&d->cont_io, OBJECT(isa), &cont_io_ops, d,
+ "dma-cont", 8 << d->dshift);
+ memory_region_add_subregion(isa_address_space_io(isa),
+ d->base + (8 << d->dshift), &d->cont_io);
- qemu_register_reset(dma_reset, d);
- dma_reset(d);
- for (i = 0; i < ARRAY_SIZE (d->regs); ++i) {
- d->regs[i].transfer_handler = dma_phony_handler;
+ for (i = 0; i < ARRAY_SIZE(d->regs); ++i) {
+ d->regs[i].transfer_handler = i8257_phony_handler;
}
+
+ d->dma_bh = qemu_bh_new(i8257_dma_run, d);
}
-static const VMStateDescription vmstate_dma_regs = {
- .name = "dma_regs",
- .version_id = 1,
- .minimum_version_id = 1,
- .fields = (VMStateField[]) {
- VMSTATE_INT32_ARRAY(now, struct dma_regs, 2),
- VMSTATE_UINT16_ARRAY(base, struct dma_regs, 2),
- VMSTATE_UINT8(mode, struct dma_regs),
- VMSTATE_UINT8(page, struct dma_regs),
- VMSTATE_UINT8(pageh, struct dma_regs),
- VMSTATE_UINT8(dack, struct dma_regs),
- VMSTATE_UINT8(eop, struct dma_regs),
- VMSTATE_END_OF_LIST()
- }
+static Property i8257_properties[] = {
+ DEFINE_PROP_INT32("base", I8257State, base, 0x00),
+ DEFINE_PROP_INT32("page-base", I8257State, page_base, 0x80),
+ DEFINE_PROP_INT32("pageh-base", I8257State, pageh_base, 0x480),
+ DEFINE_PROP_INT32("dshift", I8257State, dshift, 0),
+ DEFINE_PROP_END_OF_LIST()
};
-static int dma_post_load(void *opaque, int version_id)
+static void i8257_class_init(ObjectClass *klass, void *data)
{
- DMA_run();
-
- return 0;
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ IsaDmaClass *idc = ISADMA_CLASS(klass);
+
+ dc->realize = i8257_realize;
+ dc->reset = i8257_reset;
+ dc->vmsd = &vmstate_i8257;
+ dc->props = i8257_properties;
+
+ idc->get_transfer_mode = i8257_dma_get_transfer_mode;
+ idc->has_autoinitialization = i8257_dma_has_autoinitialization;
+ idc->read_memory = i8257_dma_read_memory;
+ idc->write_memory = i8257_dma_write_memory;
+ idc->hold_DREQ = i8257_dma_hold_DREQ;
+ idc->release_DREQ = i8257_dma_release_DREQ;
+ idc->schedule = i8257_dma_schedule;
+ idc->register_channel = i8257_dma_register_channel;
}
-static const VMStateDescription vmstate_dma = {
- .name = "dma",
- .version_id = 1,
- .minimum_version_id = 1,
- .post_load = dma_post_load,
- .fields = (VMStateField[]) {
- VMSTATE_UINT8(command, struct dma_cont),
- VMSTATE_UINT8(mask, struct dma_cont),
- VMSTATE_UINT8(flip_flop, struct dma_cont),
- VMSTATE_INT32(dshift, struct dma_cont),
- VMSTATE_STRUCT_ARRAY(regs, struct dma_cont, 4, 1, vmstate_dma_regs, struct dma_regs),
- VMSTATE_END_OF_LIST()
+static const TypeInfo i8257_info = {
+ .name = TYPE_I8257,
+ .parent = TYPE_ISA_DEVICE,
+ .instance_size = sizeof(I8257State),
+ .class_init = i8257_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_ISADMA },
+ { }
}
};
-void DMA_init(int high_page_enable, qemu_irq *cpu_request_exit)
+static void i8257_register_types(void)
+{
+ type_register_static(&i8257_info);
+}
+
+type_init(i8257_register_types)
+
+void DMA_init(ISABus *bus, int high_page_enable)
{
- dma_init2(&dma_controllers[0], 0x00, 0, 0x80,
- high_page_enable ? 0x480 : -1, cpu_request_exit);
- dma_init2(&dma_controllers[1], 0xc0, 1, 0x88,
- high_page_enable ? 0x488 : -1, cpu_request_exit);
- vmstate_register (NULL, 0, &vmstate_dma, &dma_controllers[0]);
- vmstate_register (NULL, 1, &vmstate_dma, &dma_controllers[1]);
-
- dma_bh = qemu_bh_new(DMA_run_bh, NULL);
+ ISADevice *isa1, *isa2;
+ DeviceState *d;
+
+ isa1 = isa_create(bus, TYPE_I8257);
+ d = DEVICE(isa1);
+ qdev_prop_set_int32(d, "base", 0x00);
+ qdev_prop_set_int32(d, "page-base", 0x80);
+ qdev_prop_set_int32(d, "pageh-base", high_page_enable ? 0x480 : -1);
+ qdev_prop_set_int32(d, "dshift", 0);
+ qdev_init_nofail(d);
+
+ isa2 = isa_create(bus, TYPE_I8257);
+ d = DEVICE(isa2);
+ qdev_prop_set_int32(d, "base", 0xc0);
+ qdev_prop_set_int32(d, "page-base", 0x88);
+ qdev_prop_set_int32(d, "pageh-base", high_page_enable ? 0x488 : -1);
+ qdev_prop_set_int32(d, "dshift", 1);
+ qdev_init_nofail(d);
+
+ isa_bus_dma(bus, ISADMA(isa1), ISADMA(isa2));
}
diff --git a/qemu/hw/dma/omap_dma.c b/qemu/hw/dma/omap_dma.c
index 97c57a03c..700cd6b43 100644
--- a/qemu/hw/dma/omap_dma.c
+++ b/qemu/hw/dma/omap_dma.c
@@ -17,6 +17,7 @@
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/timer.h"
#include "hw/arm/omap.h"
@@ -1626,8 +1627,7 @@ struct soc_dma_s *omap_dma_init(hwaddr base, qemu_irq *irqs,
enum omap_dma_model model)
{
int num_irqs, memsize, i;
- struct omap_dma_s *s = (struct omap_dma_s *)
- g_malloc0(sizeof(struct omap_dma_s));
+ struct omap_dma_s *s = g_new0(struct omap_dma_s, 1);
if (model <= omap_dma_3_1) {
num_irqs = 6;
@@ -2061,8 +2061,7 @@ struct soc_dma_s *omap_dma4_init(hwaddr base, qemu_irq *irqs,
int chans, omap_clk iclk, omap_clk fclk)
{
int i;
- struct omap_dma_s *s = (struct omap_dma_s *)
- g_malloc0(sizeof(struct omap_dma_s));
+ struct omap_dma_s *s = g_new0(struct omap_dma_s, 1);
s->model = omap_dma_4;
s->chans = chans;
diff --git a/qemu/hw/dma/pl080.c b/qemu/hw/dma/pl080.c
index b89b4744f..9318108b8 100644
--- a/qemu/hw/dma/pl080.c
+++ b/qemu/hw/dma/pl080.c
@@ -7,6 +7,7 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "exec/address-spaces.h"
diff --git a/qemu/hw/dma/pl330.c b/qemu/hw/dma/pl330.c
index 5be3df521..ea89ecb00 100644
--- a/qemu/hw/dma/pl330.c
+++ b/qemu/hw/dma/pl330.c
@@ -14,7 +14,9 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
+#include "qapi/error.h"
#include "qemu/timer.h"
#include "sysemu/dma.h"
diff --git a/qemu/hw/dma/puv3_dma.c b/qemu/hw/dma/puv3_dma.c
index 101bd7f8a..b97a6c176 100644
--- a/qemu/hw/dma/puv3_dma.c
+++ b/qemu/hw/dma/puv3_dma.c
@@ -8,6 +8,7 @@
* published by the Free Software Foundation, or any later version.
* See the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
diff --git a/qemu/hw/dma/pxa2xx_dma.c b/qemu/hw/dma/pxa2xx_dma.c
index d4501fb4c..2306abc35 100644
--- a/qemu/hw/dma/pxa2xx_dma.c
+++ b/qemu/hw/dma/pxa2xx_dma.c
@@ -8,6 +8,7 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/arm/pxa.h"
#include "hw/sysbus.h"
@@ -459,9 +460,8 @@ static int pxa2xx_dma_init(SysBusDevice *sbd)
return -1;
}
- s->chan = g_malloc0(sizeof(PXA2xxDMAChannel) * s->channels);
+ s->chan = g_new0(PXA2xxDMAChannel, s->channels);
- memset(s->chan, 0, sizeof(PXA2xxDMAChannel) * s->channels);
for (i = 0; i < s->channels; i ++)
s->chan[i].state = DCSR_STOPINTR;
diff --git a/qemu/hw/dma/rc4030.c b/qemu/hw/dma/rc4030.c
index 3efa6de35..a06c2359a 100644
--- a/qemu/hw/dma/rc4030.c
+++ b/qemu/hw/dma/rc4030.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/mips/mips.h"
#include "hw/sysbus.h"
@@ -111,7 +112,7 @@ static void set_next_tick(rc4030State *s)
tm_hz = 1000 / (s->itr + 1);
timer_mod(s->periodic_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
- get_ticks_per_sec() / tm_hz);
+ NANOSECONDS_PER_SECOND / tm_hz);
}
/* called for accesses to rc4030 */
diff --git a/qemu/hw/dma/soc_dma.c b/qemu/hw/dma/soc_dma.c
index c06aabb40..9bb499bf9 100644
--- a/qemu/hw/dma/soc_dma.c
+++ b/qemu/hw/dma/soc_dma.c
@@ -17,6 +17,7 @@
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/timer.h"
#include "hw/arm/soc_dma.h"
@@ -269,11 +270,10 @@ void soc_dma_port_add_fifo(struct soc_dma_s *soc, hwaddr virt_base,
if (entry->type == soc_dma_port_mem) {
if (entry->addr <= virt_base &&
entry->addr + entry->u.mem.size > virt_base) {
- fprintf(stderr, "%s: FIFO at " TARGET_FMT_lx
- " collides with RAM region at " TARGET_FMT_lx
- "-" TARGET_FMT_lx "\n", __FUNCTION__,
- (target_ulong) virt_base,
- (target_ulong) entry->addr, (target_ulong)
+ fprintf(stderr, "%s: FIFO at %"PRIx64
+ " collides with RAM region at %"PRIx64
+ "-%"PRIx64 "\n", __func__,
+ virt_base, entry->addr,
(entry->addr + entry->u.mem.size));
exit(-1);
}
@@ -284,10 +284,9 @@ void soc_dma_port_add_fifo(struct soc_dma_s *soc, hwaddr virt_base,
while (entry < dma->memmap + dma->memmap_size &&
entry->addr <= virt_base) {
if (entry->addr == virt_base && entry->u.fifo.out == out) {
- fprintf(stderr, "%s: FIFO at " TARGET_FMT_lx
- " collides FIFO at " TARGET_FMT_lx "\n",
- __FUNCTION__, (target_ulong) virt_base,
- (target_ulong) entry->addr);
+ fprintf(stderr, "%s: FIFO at %"PRIx64
+ " collides FIFO at %"PRIx64 "\n",
+ __func__, virt_base, entry->addr);
exit(-1);
}
@@ -322,13 +321,11 @@ void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base,
if ((entry->addr >= virt_base && entry->addr < virt_base + size) ||
(entry->addr <= virt_base &&
entry->addr + entry->u.mem.size > virt_base)) {
- fprintf(stderr, "%s: RAM at " TARGET_FMT_lx "-" TARGET_FMT_lx
- " collides with RAM region at " TARGET_FMT_lx
- "-" TARGET_FMT_lx "\n", __FUNCTION__,
- (target_ulong) virt_base,
- (target_ulong) (virt_base + size),
- (target_ulong) entry->addr, (target_ulong)
- (entry->addr + entry->u.mem.size));
+ fprintf(stderr, "%s: RAM at %"PRIx64 "-%"PRIx64
+ " collides with RAM region at %"PRIx64
+ "-%"PRIx64 "\n", __func__,
+ virt_base, virt_base + size,
+ entry->addr, entry->addr + entry->u.mem.size);
exit(-1);
}
@@ -337,12 +334,11 @@ void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base,
} else {
if (entry->addr >= virt_base &&
entry->addr < virt_base + size) {
- fprintf(stderr, "%s: RAM at " TARGET_FMT_lx "-" TARGET_FMT_lx
- " collides with FIFO at " TARGET_FMT_lx
- "\n", __FUNCTION__,
- (target_ulong) virt_base,
- (target_ulong) (virt_base + size),
- (target_ulong) entry->addr);
+ fprintf(stderr, "%s: RAM at %"PRIx64 "-%"PRIx64
+ " collides with FIFO at %"PRIx64
+ "\n", __func__,
+ virt_base, virt_base + size,
+ entry->addr);
exit(-1);
}
diff --git a/qemu/hw/dma/sparc32_dma.c b/qemu/hw/dma/sparc32_dma.c
index e6a453ce5..9d545e412 100644
--- a/qemu/hw/dma/sparc32_dma.c
+++ b/qemu/hw/dma/sparc32_dma.c
@@ -25,6 +25,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sparc/sparc32_dma.h"
#include "hw/sparc/sun4m.h"
diff --git a/qemu/hw/dma/sun4m_iommu.c b/qemu/hw/dma/sun4m_iommu.c
index 9a488bc9b..b3cbc54c2 100644
--- a/qemu/hw/dma/sun4m_iommu.c
+++ b/qemu/hw/dma/sun4m_iommu.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sparc/sun4m.h"
#include "hw/sysbus.h"
#include "exec/address-spaces.h"
diff --git a/qemu/hw/dma/xilinx_axidma.c b/qemu/hw/dma/xilinx_axidma.c
index cf842a3cc..a4753e55a 100644
--- a/qemu/hw/dma/xilinx_axidma.c
+++ b/qemu/hw/dma/xilinx_axidma.c
@@ -22,7 +22,9 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
+#include "qapi/error.h"
#include "qemu/timer.h"
#include "hw/ptimer.h"
#include "qemu/log.h"
@@ -133,7 +135,7 @@ struct XilinxAXIDMA {
};
/*
- * Helper calls to extract info from desriptors and other trivial
+ * Helper calls to extract info from descriptors and other trivial
* state from regs.
*/
static inline int stream_desc_sof(struct SDesc *d)
@@ -177,16 +179,6 @@ static inline int streamid_from_addr(hwaddr addr)
return sid;
}
-#ifdef DEBUG_ENET
-static void stream_desc_show(struct SDesc *d)
-{
- qemu_log("buffer_addr = " PRIx64 "\n", d->buffer_address);
- qemu_log("nxtdesc = " PRIx64 "\n", d->nxtdesc);
- qemu_log("control = %x\n", d->control);
- qemu_log("status = %x\n", d->status);
-}
-#endif
-
static void stream_desc_load(struct Stream *s, hwaddr addr)
{
struct SDesc *d = &s->desc;
diff --git a/qemu/hw/gpio/Makefile.objs b/qemu/hw/gpio/Makefile.objs
index 1abcf1798..a43c7cf44 100644
--- a/qemu/hw/gpio/Makefile.objs
+++ b/qemu/hw/gpio/Makefile.objs
@@ -3,5 +3,7 @@ common-obj-$(CONFIG_PL061) += pl061.o
common-obj-$(CONFIG_PUV3) += puv3_gpio.o
common-obj-$(CONFIG_ZAURUS) += zaurus.o
common-obj-$(CONFIG_E500) += mpc8xxx.o
+common-obj-$(CONFIG_GPIO_KEY) += gpio_key.o
obj-$(CONFIG_OMAP) += omap_gpio.o
+obj-$(CONFIG_IMX) += imx_gpio.o
diff --git a/qemu/hw/gpio/gpio_key.c b/qemu/hw/gpio/gpio_key.c
new file mode 100644
index 000000000..ef287727b
--- /dev/null
+++ b/qemu/hw/gpio/gpio_key.c
@@ -0,0 +1,104 @@
+/*
+ * GPIO key
+ *
+ * Copyright (c) 2016 Linaro Limited
+ *
+ * Author: Shannon Zhao <shannon.zhao@linaro.org>
+ *
+ * Emulate a (human) keypress -- when the key is triggered by
+ * setting the incoming gpio line, the outbound irq line is
+ * raised for 100ms before being dropped again.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License; 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/sysbus.h"
+
+#define TYPE_GPIOKEY "gpio-key"
+#define GPIOKEY(obj) OBJECT_CHECK(GPIOKEYState, (obj), TYPE_GPIOKEY)
+#define GPIO_KEY_LATENCY 100 /* 100ms */
+
+typedef struct GPIOKEYState {
+ SysBusDevice parent_obj;
+
+ QEMUTimer *timer;
+ qemu_irq irq;
+} GPIOKEYState;
+
+static const VMStateDescription vmstate_gpio_key = {
+ .name = "gpio-key",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_TIMER_PTR(timer, GPIOKEYState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void gpio_key_reset(DeviceState *dev)
+{
+ GPIOKEYState *s = GPIOKEY(dev);
+
+ timer_del(s->timer);
+}
+
+static void gpio_key_timer_expired(void *opaque)
+{
+ GPIOKEYState *s = (GPIOKEYState *)opaque;
+
+ qemu_set_irq(s->irq, 0);
+ timer_del(s->timer);
+}
+
+static void gpio_key_set_irq(void *opaque, int irq, int level)
+{
+ GPIOKEYState *s = (GPIOKEYState *)opaque;
+
+ qemu_set_irq(s->irq, 1);
+ timer_mod(s->timer,
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + GPIO_KEY_LATENCY);
+}
+
+static void gpio_key_realize(DeviceState *dev, Error **errp)
+{
+ GPIOKEYState *s = GPIOKEY(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+ sysbus_init_irq(sbd, &s->irq);
+ qdev_init_gpio_in(dev, gpio_key_set_irq, 1);
+ s->timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, gpio_key_timer_expired, s);
+}
+
+static void gpio_key_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = gpio_key_realize;
+ dc->vmsd = &vmstate_gpio_key;
+ dc->reset = &gpio_key_reset;
+}
+
+static const TypeInfo gpio_key_info = {
+ .name = TYPE_GPIOKEY,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(GPIOKEYState),
+ .class_init = gpio_key_class_init,
+};
+
+static void gpio_key_register_types(void)
+{
+ type_register_static(&gpio_key_info);
+}
+
+type_init(gpio_key_register_types)
diff --git a/qemu/hw/gpio/imx_gpio.c b/qemu/hw/gpio/imx_gpio.c
new file mode 100644
index 000000000..ed7e247f5
--- /dev/null
+++ b/qemu/hw/gpio/imx_gpio.c
@@ -0,0 +1,350 @@
+/*
+ * i.MX processors GPIO emulation.
+ *
+ * Copyright (C) 2015 Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/gpio/imx_gpio.h"
+
+#ifndef DEBUG_IMX_GPIO
+#define DEBUG_IMX_GPIO 0
+#endif
+
+typedef enum IMXGPIOLevel {
+ IMX_GPIO_LEVEL_LOW = 0,
+ IMX_GPIO_LEVEL_HIGH = 1,
+} IMXGPIOLevel;
+
+#define DPRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX_GPIO) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_GPIO, \
+ __func__, ##args); \
+ } \
+ } while (0)
+
+static const char *imx_gpio_reg_name(uint32_t reg)
+{
+ switch (reg) {
+ case DR_ADDR:
+ return "DR";
+ case GDIR_ADDR:
+ return "GDIR";
+ case PSR_ADDR:
+ return "PSR";
+ case ICR1_ADDR:
+ return "ICR1";
+ case ICR2_ADDR:
+ return "ICR2";
+ case IMR_ADDR:
+ return "IMR";
+ case ISR_ADDR:
+ return "ISR";
+ case EDGE_SEL_ADDR:
+ return "EDGE_SEL";
+ default:
+ return "[?]";
+ }
+}
+
+static void imx_gpio_update_int(IMXGPIOState *s)
+{
+ if (s->has_upper_pin_irq) {
+ qemu_set_irq(s->irq[0], (s->isr & s->imr & 0x0000FFFF) ? 1 : 0);
+ qemu_set_irq(s->irq[1], (s->isr & s->imr & 0xFFFF0000) ? 1 : 0);
+ } else {
+ qemu_set_irq(s->irq[0], (s->isr & s->imr) ? 1 : 0);
+ }
+}
+
+static void imx_gpio_set_int_line(IMXGPIOState *s, int line, IMXGPIOLevel level)
+{
+ /* if this signal isn't configured as an input signal, nothing to do */
+ if (!extract32(s->gdir, line, 1)) {
+ return;
+ }
+
+ /* When set, EDGE_SEL overrides the ICR config */
+ if (extract32(s->edge_sel, line, 1)) {
+ /* we detect interrupt on rising and falling edge */
+ if (extract32(s->psr, line, 1) != level) {
+ /* level changed */
+ s->isr = deposit32(s->isr, line, 1, 1);
+ }
+ } else if (extract64(s->icr, 2*line + 1, 1)) {
+ /* interrupt is edge sensitive */
+ if (extract32(s->psr, line, 1) != level) {
+ /* level changed */
+ if (extract64(s->icr, 2*line, 1) != level) {
+ s->isr = deposit32(s->isr, line, 1, 1);
+ }
+ }
+ } else {
+ /* interrupt is level sensitive */
+ if (extract64(s->icr, 2*line, 1) == level) {
+ s->isr = deposit32(s->isr, line, 1, 1);
+ }
+ }
+}
+
+static void imx_gpio_set(void *opaque, int line, int level)
+{
+ IMXGPIOState *s = IMX_GPIO(opaque);
+ IMXGPIOLevel imx_level = level ? IMX_GPIO_LEVEL_HIGH : IMX_GPIO_LEVEL_LOW;
+
+ imx_gpio_set_int_line(s, line, imx_level);
+
+ /* this is an input signal, so set PSR */
+ s->psr = deposit32(s->psr, line, 1, imx_level);
+
+ imx_gpio_update_int(s);
+}
+
+static void imx_gpio_set_all_int_lines(IMXGPIOState *s)
+{
+ int i;
+
+ for (i = 0; i < IMX_GPIO_PIN_COUNT; i++) {
+ IMXGPIOLevel imx_level = extract32(s->psr, i, 1);
+ imx_gpio_set_int_line(s, i, imx_level);
+ }
+
+ imx_gpio_update_int(s);
+}
+
+static inline void imx_gpio_set_all_output_lines(IMXGPIOState *s)
+{
+ int i;
+
+ for (i = 0; i < IMX_GPIO_PIN_COUNT; i++) {
+ /*
+ * if the line is set as output, then forward the line
+ * level to its user.
+ */
+ if (extract32(s->gdir, i, 1) && s->output[i]) {
+ qemu_set_irq(s->output[i], extract32(s->dr, i, 1));
+ }
+ }
+}
+
+static uint64_t imx_gpio_read(void *opaque, hwaddr offset, unsigned size)
+{
+ IMXGPIOState *s = IMX_GPIO(opaque);
+ uint32_t reg_value = 0;
+
+ switch (offset) {
+ case DR_ADDR:
+ /*
+ * depending on the "line" configuration, the bit values
+ * are coming either from DR or PSR
+ */
+ reg_value = (s->dr & s->gdir) | (s->psr & ~s->gdir);
+ break;
+
+ case GDIR_ADDR:
+ reg_value = s->gdir;
+ break;
+
+ case PSR_ADDR:
+ reg_value = s->psr & ~s->gdir;
+ break;
+
+ case ICR1_ADDR:
+ reg_value = extract64(s->icr, 0, 32);
+ break;
+
+ case ICR2_ADDR:
+ reg_value = extract64(s->icr, 32, 32);
+ break;
+
+ case IMR_ADDR:
+ reg_value = s->imr;
+ break;
+
+ case ISR_ADDR:
+ reg_value = s->isr;
+ break;
+
+ case EDGE_SEL_ADDR:
+ if (s->has_edge_sel) {
+ reg_value = s->edge_sel;
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: EDGE_SEL register not "
+ "present on this version of GPIO device\n",
+ TYPE_IMX_GPIO, __func__);
+ }
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_GPIO, __func__, offset);
+ break;
+ }
+
+ DPRINTF("(%s) = 0x%" PRIx32 "\n", imx_gpio_reg_name(offset), reg_value);
+
+ return reg_value;
+}
+
+static void imx_gpio_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ IMXGPIOState *s = IMX_GPIO(opaque);
+
+ DPRINTF("(%s, value = 0x%" PRIx32 ")\n", imx_gpio_reg_name(offset),
+ (uint32_t)value);
+
+ switch (offset) {
+ case DR_ADDR:
+ s->dr = value;
+ imx_gpio_set_all_output_lines(s);
+ break;
+
+ case GDIR_ADDR:
+ s->gdir = value;
+ imx_gpio_set_all_output_lines(s);
+ imx_gpio_set_all_int_lines(s);
+ break;
+
+ case ICR1_ADDR:
+ s->icr = deposit64(s->icr, 0, 32, value);
+ imx_gpio_set_all_int_lines(s);
+ break;
+
+ case ICR2_ADDR:
+ s->icr = deposit64(s->icr, 32, 32, value);
+ imx_gpio_set_all_int_lines(s);
+ break;
+
+ case IMR_ADDR:
+ s->imr = value;
+ imx_gpio_update_int(s);
+ break;
+
+ case ISR_ADDR:
+ s->isr |= ~value;
+ imx_gpio_set_all_int_lines(s);
+ break;
+
+ case EDGE_SEL_ADDR:
+ if (s->has_edge_sel) {
+ s->edge_sel = value;
+ imx_gpio_set_all_int_lines(s);
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: EDGE_SEL register not "
+ "present on this version of GPIO device\n",
+ TYPE_IMX_GPIO, __func__);
+ }
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_GPIO, __func__, offset);
+ break;
+ }
+
+ return;
+}
+
+static const MemoryRegionOps imx_gpio_ops = {
+ .read = imx_gpio_read,
+ .write = imx_gpio_write,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_imx_gpio = {
+ .name = TYPE_IMX_GPIO,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(dr, IMXGPIOState),
+ VMSTATE_UINT32(gdir, IMXGPIOState),
+ VMSTATE_UINT32(psr, IMXGPIOState),
+ VMSTATE_UINT64(icr, IMXGPIOState),
+ VMSTATE_UINT32(imr, IMXGPIOState),
+ VMSTATE_UINT32(isr, IMXGPIOState),
+ VMSTATE_BOOL(has_edge_sel, IMXGPIOState),
+ VMSTATE_UINT32(edge_sel, IMXGPIOState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property imx_gpio_properties[] = {
+ DEFINE_PROP_BOOL("has-edge-sel", IMXGPIOState, has_edge_sel, true),
+ DEFINE_PROP_BOOL("has-upper-pin-irq", IMXGPIOState, has_upper_pin_irq,
+ false),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void imx_gpio_reset(DeviceState *dev)
+{
+ IMXGPIOState *s = IMX_GPIO(dev);
+
+ s->dr = 0;
+ s->gdir = 0;
+ s->psr = 0;
+ s->icr = 0;
+ s->imr = 0;
+ s->isr = 0;
+ s->edge_sel = 0;
+
+ imx_gpio_set_all_output_lines(s);
+ imx_gpio_update_int(s);
+}
+
+static void imx_gpio_realize(DeviceState *dev, Error **errp)
+{
+ IMXGPIOState *s = IMX_GPIO(dev);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &imx_gpio_ops, s,
+ TYPE_IMX_GPIO, IMX_GPIO_MEM_SIZE);
+
+ qdev_init_gpio_in(DEVICE(s), imx_gpio_set, IMX_GPIO_PIN_COUNT);
+ qdev_init_gpio_out(DEVICE(s), s->output, IMX_GPIO_PIN_COUNT);
+
+ sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[0]);
+ sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[1]);
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
+}
+
+static void imx_gpio_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = imx_gpio_realize;
+ dc->reset = imx_gpio_reset;
+ dc->props = imx_gpio_properties;
+ dc->vmsd = &vmstate_imx_gpio;
+ dc->desc = "i.MX GPIO controller";
+}
+
+static const TypeInfo imx_gpio_info = {
+ .name = TYPE_IMX_GPIO,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(IMXGPIOState),
+ .class_init = imx_gpio_class_init,
+};
+
+static void imx_gpio_register_types(void)
+{
+ type_register_static(&imx_gpio_info);
+}
+
+type_init(imx_gpio_register_types)
diff --git a/qemu/hw/gpio/max7310.c b/qemu/hw/gpio/max7310.c
index 2f59b134e..1bd5eaf91 100644
--- a/qemu/hw/gpio/max7310.c
+++ b/qemu/hw/gpio/max7310.c
@@ -7,6 +7,7 @@
* This file is licensed under GNU GPL.
*/
+#include "qemu/osdep.h"
#include "hw/i2c/i2c.h"
#define TYPE_MAX7310 "max7310"
diff --git a/qemu/hw/gpio/mpc8xxx.c b/qemu/hw/gpio/mpc8xxx.c
index 1aeaaaaf0..d14971946 100644
--- a/qemu/hw/gpio/mpc8xxx.c
+++ b/qemu/hw/gpio/mpc8xxx.c
@@ -19,6 +19,7 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#define TYPE_MPC8XXX_GPIO "mpc8xxx_gpio"
diff --git a/qemu/hw/gpio/omap_gpio.c b/qemu/hw/gpio/omap_gpio.c
index d92f8cfba..9b1b004fc 100644
--- a/qemu/hw/gpio/omap_gpio.c
+++ b/qemu/hw/gpio/omap_gpio.c
@@ -18,9 +18,11 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/arm/omap.h"
#include "hw/sysbus.h"
+#include "qemu/error-report.h"
struct omap_gpio_s {
qemu_irq irq;
@@ -682,7 +684,8 @@ static int omap_gpio_init(SysBusDevice *sbd)
struct omap_gpif_s *s = OMAP1_GPIO(dev);
if (!s->clk) {
- hw_error("omap-gpio: clk not connected\n");
+ error_report("omap-gpio: clk not connected");
+ return -1;
}
qdev_init_gpio_in(dev, omap_gpio_set, 16);
qdev_init_gpio_out(dev, s->omap1.handler, 16);
@@ -700,25 +703,35 @@ static int omap2_gpio_init(SysBusDevice *sbd)
int i;
if (!s->iclk) {
- hw_error("omap2-gpio: iclk not connected\n");
+ error_report("omap2-gpio: iclk not connected");
+ return -1;
}
+
+ s->modulecount = s->mpu_model < omap2430 ? 4
+ : s->mpu_model < omap3430 ? 5
+ : 6;
+
+ for (i = 0; i < s->modulecount; i++) {
+ if (!s->fclk[i]) {
+ error_report("omap2-gpio: fclk%d not connected", i);
+ return -1;
+ }
+ }
+
if (s->mpu_model < omap3430) {
- s->modulecount = (s->mpu_model < omap2430) ? 4 : 5;
memory_region_init_io(&s->iomem, OBJECT(s), &omap2_gpif_top_ops, s,
"omap2.gpio", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
- } else {
- s->modulecount = 6;
}
- s->modules = g_malloc0(s->modulecount * sizeof(struct omap2_gpio_s));
- s->handler = g_malloc0(s->modulecount * 32 * sizeof(qemu_irq));
+
+ s->modules = g_new0(struct omap2_gpio_s, s->modulecount);
+ s->handler = g_new0(qemu_irq, s->modulecount * 32);
qdev_init_gpio_in(dev, omap2_gpio_set, s->modulecount * 32);
qdev_init_gpio_out(dev, s->handler, s->modulecount * 32);
+
for (i = 0; i < s->modulecount; i++) {
struct omap2_gpio_s *m = &s->modules[i];
- if (!s->fclk[i]) {
- hw_error("omap2-gpio: fclk%d not connected\n", i);
- }
+
m->revision = (s->mpu_model < omap3430) ? 0x18 : 0x25;
m->handler = &s->handler[i * 32];
sysbus_init_irq(sbd, &m->irq[0]); /* mpu irq */
@@ -728,6 +741,7 @@ static int omap2_gpio_init(SysBusDevice *sbd)
"omap.gpio-module", 0x1000);
sysbus_init_mmio(sbd, &m->iomem);
}
+
return 0;
}
diff --git a/qemu/hw/gpio/pl061.c b/qemu/hw/gpio/pl061.c
index 4ba730b47..29dc7fc38 100644
--- a/qemu/hw/gpio/pl061.c
+++ b/qemu/hw/gpio/pl061.c
@@ -8,6 +8,7 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
//#define DEBUG_PL061 1
@@ -55,17 +56,17 @@ typedef struct PL061State {
uint32_t slr;
uint32_t den;
uint32_t cr;
- uint32_t float_high;
uint32_t amsel;
qemu_irq irq;
qemu_irq out[8];
const unsigned char *id;
+ uint32_t rsvd_start; /* reserved area: [rsvd_start, 0xfcc] */
} PL061State;
static const VMStateDescription vmstate_pl061 = {
.name = "pl061",
- .version_id = 3,
- .minimum_version_id = 3,
+ .version_id = 4,
+ .minimum_version_id = 4,
.fields = (VMStateField[]) {
VMSTATE_UINT32(locked, PL061State),
VMSTATE_UINT32(data, PL061State),
@@ -87,7 +88,6 @@ static const VMStateDescription vmstate_pl061 = {
VMSTATE_UINT32(slr, PL061State),
VMSTATE_UINT32(den, PL061State),
VMSTATE_UINT32(cr, PL061State),
- VMSTATE_UINT32(float_high, PL061State),
VMSTATE_UINT32_V(amsel, PL061State, 2),
VMSTATE_END_OF_LIST()
}
@@ -153,12 +153,15 @@ static uint64_t pl061_read(void *opaque, hwaddr offset,
{
PL061State *s = (PL061State *)opaque;
- if (offset >= 0xfd0 && offset < 0x1000) {
- return s->id[(offset - 0xfd0) >> 2];
- }
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 0x400: /* Direction */
return s->dir;
@@ -199,10 +202,12 @@ static uint64_t pl061_read(void *opaque, hwaddr offset,
case 0x528: /* Analog mode select */
return s->amsel;
default:
- qemu_log_mask(LOG_GUEST_ERROR,
- "pl061_read: Bad offset %x\n", (int)offset);
- return 0;
+ break;
}
+err_out:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "pl061_read: Bad offset %x\n", (int)offset);
+ return 0;
}
static void pl061_write(void *opaque, hwaddr offset,
@@ -217,6 +222,9 @@ static void pl061_write(void *opaque, hwaddr offset,
pl061_update(s);
return;
}
+ if (offset >= s->rsvd_start) {
+ goto err_out;
+ }
switch (offset) {
case 0x400: /* Direction */
s->dir = value & 0xff;
@@ -275,16 +283,41 @@ static void pl061_write(void *opaque, hwaddr offset,
s->amsel = value & 0xff;
break;
default:
- qemu_log_mask(LOG_GUEST_ERROR,
- "pl061_write: Bad offset %x\n", (int)offset);
+ goto err_out;
}
pl061_update(s);
+ return;
+err_out:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "pl061_write: Bad offset %x\n", (int)offset);
}
-static void pl061_reset(PL061State *s)
+static void pl061_reset(DeviceState *dev)
{
- s->locked = 1;
- s->cr = 0xff;
+ PL061State *s = PL061(dev);
+
+ /* 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;
+ s->ibe = 0;
+ s->iev = 0;
+ s->im = 0;
+ s->istate = 0;
+ s->afsel = 0;
+ s->dr2r = 0xff;
+ s->dr4r = 0;
+ s->dr8r = 0;
+ s->odr = 0;
+ s->pur = 0;
+ s->pdr = 0;
+ s->slr = 0;
+ s->den = 0;
+ s->locked = 1;
+ s->cr = 0xff;
+ s->amsel = 0;
}
static void pl061_set_irq(void * opaque, int irq, int level)
@@ -317,7 +350,7 @@ static int pl061_initfn(SysBusDevice *sbd)
sysbus_init_irq(sbd, &s->irq);
qdev_init_gpio_in(dev, pl061_set_irq, 8);
qdev_init_gpio_out(dev, s->out, 8);
- pl061_reset(s);
+
return 0;
}
@@ -326,6 +359,7 @@ 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)
@@ -333,6 +367,7 @@ static void pl061_init(Object *obj)
PL061State *s = PL061(obj);
s->id = pl061_id;
+ s->rsvd_start = 0x424;
}
static void pl061_class_init(ObjectClass *klass, void *data)
@@ -342,6 +377,7 @@ static void pl061_class_init(ObjectClass *klass, void *data)
k->init = pl061_initfn;
dc->vmsd = &vmstate_pl061;
+ dc->reset = &pl061_reset;
}
static const TypeInfo pl061_info = {
diff --git a/qemu/hw/gpio/puv3_gpio.c b/qemu/hw/gpio/puv3_gpio.c
index 39840aa73..445afccf9 100644
--- a/qemu/hw/gpio/puv3_gpio.c
+++ b/qemu/hw/gpio/puv3_gpio.c
@@ -8,6 +8,7 @@
* published by the Free Software Foundation, or any later version.
* See the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
diff --git a/qemu/hw/gpio/zaurus.c b/qemu/hw/gpio/zaurus.c
index 24a77272d..555da281c 100644
--- a/qemu/hw/gpio/zaurus.c
+++ b/qemu/hw/gpio/zaurus.c
@@ -15,6 +15,7 @@
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/arm/sharpsl.h"
#include "hw/sysbus.h"
@@ -235,10 +236,6 @@ static const VMStateDescription vmstate_scoop_regs = {
},
};
-static Property scoop_sysbus_properties[] = {
- DEFINE_PROP_END_OF_LIST(),
-};
-
static void scoop_sysbus_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -247,7 +244,6 @@ static void scoop_sysbus_class_init(ObjectClass *klass, void *data)
k->init = scoop_init;
dc->desc = "Scoop2 Sharp custom ASIC";
dc->vmsd = &vmstate_scoop_regs;
- dc->props = scoop_sysbus_properties;
}
static const TypeInfo scoop_sysbus_info = {
diff --git a/qemu/hw/i2c/Makefile.objs b/qemu/hw/i2c/Makefile.objs
index 0f130608c..aeb8f38d7 100644
--- a/qemu/hw/i2c/Makefile.objs
+++ b/qemu/hw/i2c/Makefile.objs
@@ -4,4 +4,5 @@ common-obj-$(CONFIG_ACPI_X86) += smbus_ich9.o
common-obj-$(CONFIG_APM) += pm_smbus.o
common-obj-$(CONFIG_BITBANG_I2C) += bitbang_i2c.o
common-obj-$(CONFIG_EXYNOS4) += exynos4210_i2c.o
+common-obj-$(CONFIG_IMX_I2C) += imx_i2c.o
obj-$(CONFIG_OMAP) += omap_i2c.o
diff --git a/qemu/hw/i2c/bitbang_i2c.c b/qemu/hw/i2c/bitbang_i2c.c
index 6d1bb03d6..6ed206020 100644
--- a/qemu/hw/i2c/bitbang_i2c.c
+++ b/qemu/hw/i2c/bitbang_i2c.c
@@ -9,6 +9,7 @@
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "bitbang_i2c.h"
#include "hw/sysbus.h"
diff --git a/qemu/hw/i2c/core.c b/qemu/hw/i2c/core.c
index 5a6402634..ba22104af 100644
--- a/qemu/hw/i2c/core.c
+++ b/qemu/hw/i2c/core.c
@@ -7,6 +7,7 @@
* This code is licensed under the LGPL.
*/
+#include "qemu/osdep.h"
#include "hw/i2c/i2c.h"
struct I2CBus
diff --git a/qemu/hw/i2c/exynos4210_i2c.c b/qemu/hw/i2c/exynos4210_i2c.c
index fb99dfda1..8c2a2c163 100644
--- a/qemu/hw/i2c/exynos4210_i2c.c
+++ b/qemu/hw/i2c/exynos4210_i2c.c
@@ -20,6 +20,7 @@
*
*/
+#include "qemu/osdep.h"
#include "qemu/timer.h"
#include "hw/sysbus.h"
#include "hw/i2c/i2c.h"
diff --git a/qemu/hw/i2c/imx_i2c.c b/qemu/hw/i2c/imx_i2c.c
new file mode 100644
index 000000000..a01e43ebe
--- /dev/null
+++ b/qemu/hw/i2c/imx_i2c.c
@@ -0,0 +1,337 @@
+/*
+ * i.MX I2C Bus Serial Interface Emulation
+ *
+ * Copyright (C) 2013 Jean-Christophe Dubois. <jcd@tribudubois.net>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "hw/i2c/imx_i2c.h"
+#include "hw/i2c/i2c.h"
+
+#ifndef DEBUG_IMX_I2C
+#define DEBUG_IMX_I2C 0
+#endif
+
+#define DPRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX_I2C) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_I2C, \
+ __func__, ##args); \
+ } \
+ } while (0)
+
+static const char *imx_i2c_get_regname(unsigned offset)
+{
+ switch (offset) {
+ case IADR_ADDR:
+ return "IADR";
+ case IFDR_ADDR:
+ return "IFDR";
+ case I2CR_ADDR:
+ return "I2CR";
+ case I2SR_ADDR:
+ return "I2SR";
+ case I2DR_ADDR:
+ return "I2DR";
+ default:
+ return "[?]";
+ }
+}
+
+static inline bool imx_i2c_is_enabled(IMXI2CState *s)
+{
+ return s->i2cr & I2CR_IEN;
+}
+
+static inline bool imx_i2c_interrupt_is_enabled(IMXI2CState *s)
+{
+ return s->i2cr & I2CR_IIEN;
+}
+
+static inline bool imx_i2c_is_master(IMXI2CState *s)
+{
+ return s->i2cr & I2CR_MSTA;
+}
+
+static void imx_i2c_reset(DeviceState *dev)
+{
+ IMXI2CState *s = IMX_I2C(dev);
+
+ if (s->address != ADDR_RESET) {
+ i2c_end_transfer(s->bus);
+ }
+
+ s->address = ADDR_RESET;
+ s->iadr = IADR_RESET;
+ s->ifdr = IFDR_RESET;
+ s->i2cr = I2CR_RESET;
+ s->i2sr = I2SR_RESET;
+ s->i2dr_read = I2DR_RESET;
+ s->i2dr_write = I2DR_RESET;
+}
+
+static inline void imx_i2c_raise_interrupt(IMXI2CState *s)
+{
+ /*
+ * raise an interrupt if the device is enabled and it is configured
+ * to generate some interrupts.
+ */
+ if (imx_i2c_is_enabled(s) && imx_i2c_interrupt_is_enabled(s)) {
+ s->i2sr |= I2SR_IIF;
+ qemu_irq_raise(s->irq);
+ }
+}
+
+static uint64_t imx_i2c_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ uint16_t value;
+ IMXI2CState *s = IMX_I2C(opaque);
+
+ switch (offset) {
+ case IADR_ADDR:
+ value = s->iadr;
+ break;
+ case IFDR_ADDR:
+ value = s->ifdr;
+ break;
+ case I2CR_ADDR:
+ value = s->i2cr;
+ break;
+ case I2SR_ADDR:
+ value = s->i2sr;
+ break;
+ case I2DR_ADDR:
+ value = s->i2dr_read;
+
+ if (imx_i2c_is_master(s)) {
+ int ret = 0xff;
+
+ if (s->address == ADDR_RESET) {
+ /* something is wrong as the address is not set */
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to read "
+ "without specifying the slave address\n",
+ TYPE_IMX_I2C, __func__);
+ } else if (s->i2cr & I2CR_MTX) {
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to read "
+ "but MTX is set\n", TYPE_IMX_I2C, __func__);
+ } else {
+ /* get the next byte */
+ ret = i2c_recv(s->bus);
+
+ if (ret >= 0) {
+ imx_i2c_raise_interrupt(s);
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: read failed "
+ "for device 0x%02x\n", TYPE_IMX_I2C,
+ __func__, s->address);
+ ret = 0xff;
+ }
+ }
+
+ s->i2dr_read = ret;
+ } else {
+ qemu_log_mask(LOG_UNIMP, "[%s]%s: slave mode not implemented\n",
+ TYPE_IMX_I2C, __func__);
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_I2C, __func__, offset);
+ value = 0;
+ break;
+ }
+
+ DPRINTF("read %s [0x%" HWADDR_PRIx "] -> 0x%02x\n",
+ imx_i2c_get_regname(offset), offset, value);
+
+ return (uint64_t)value;
+}
+
+static void imx_i2c_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ IMXI2CState *s = IMX_I2C(opaque);
+
+ DPRINTF("write %s [0x%" HWADDR_PRIx "] <- 0x%02x\n",
+ imx_i2c_get_regname(offset), offset, (int)value);
+
+ value &= 0xff;
+
+ switch (offset) {
+ case IADR_ADDR:
+ s->iadr = value & IADR_MASK;
+ /* i2c_set_slave_address(s->bus, (uint8_t)s->iadr); */
+ break;
+ case IFDR_ADDR:
+ s->ifdr = value & IFDR_MASK;
+ break;
+ case I2CR_ADDR:
+ if (imx_i2c_is_enabled(s) && ((value & I2CR_IEN) == 0)) {
+ /* This is a soft reset. IADR is preserved during soft resets */
+ uint16_t iadr = s->iadr;
+ imx_i2c_reset(DEVICE(s));
+ s->iadr = iadr;
+ } else { /* normal write */
+ s->i2cr = value & I2CR_MASK;
+
+ if (imx_i2c_is_master(s)) {
+ /* set the bus to busy */
+ s->i2sr |= I2SR_IBB;
+ } else { /* slave mode */
+ /* bus is not busy anymore */
+ s->i2sr &= ~I2SR_IBB;
+
+ /*
+ * if we unset the master mode then it ends the ongoing
+ * transfer if any
+ */
+ if (s->address != ADDR_RESET) {
+ i2c_end_transfer(s->bus);
+ s->address = ADDR_RESET;
+ }
+ }
+
+ if (s->i2cr & I2CR_RSTA) { /* Restart */
+ /* if this is a restart then it ends the ongoing transfer */
+ if (s->address != ADDR_RESET) {
+ i2c_end_transfer(s->bus);
+ s->address = ADDR_RESET;
+ s->i2cr &= ~I2CR_RSTA;
+ }
+ }
+ }
+ break;
+ case I2SR_ADDR:
+ /*
+ * if the user writes 0 to IIF then lower the interrupt and
+ * reset the bit
+ */
+ if ((s->i2sr & I2SR_IIF) && !(value & I2SR_IIF)) {
+ s->i2sr &= ~I2SR_IIF;
+ qemu_irq_lower(s->irq);
+ }
+
+ /*
+ * if the user writes 0 to IAL, reset the bit
+ */
+ if ((s->i2sr & I2SR_IAL) && !(value & I2SR_IAL)) {
+ s->i2sr &= ~I2SR_IAL;
+ }
+
+ break;
+ case I2DR_ADDR:
+ /* if the device is not enabled, nothing to do */
+ if (!imx_i2c_is_enabled(s)) {
+ break;
+ }
+
+ s->i2dr_write = value & I2DR_MASK;
+
+ if (imx_i2c_is_master(s)) {
+ /* If this is the first write cycle then it is the slave addr */
+ if (s->address == ADDR_RESET) {
+ if (i2c_start_transfer(s->bus, extract32(s->i2dr_write, 1, 7),
+ extract32(s->i2dr_write, 0, 1))) {
+ /* if non zero is returned, the adress is not valid */
+ s->i2sr |= I2SR_RXAK;
+ } else {
+ s->address = s->i2dr_write;
+ s->i2sr &= ~I2SR_RXAK;
+ imx_i2c_raise_interrupt(s);
+ }
+ } else { /* This is a normal data write */
+ if (i2c_send(s->bus, s->i2dr_write)) {
+ /* if the target return non zero then end the transfer */
+ s->i2sr |= I2SR_RXAK;
+ s->address = ADDR_RESET;
+ i2c_end_transfer(s->bus);
+ } else {
+ s->i2sr &= ~I2SR_RXAK;
+ imx_i2c_raise_interrupt(s);
+ }
+ }
+ } else {
+ qemu_log_mask(LOG_UNIMP, "[%s]%s: slave mode not implemented\n",
+ TYPE_IMX_I2C, __func__);
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_I2C, __func__, offset);
+ break;
+ }
+}
+
+static const MemoryRegionOps imx_i2c_ops = {
+ .read = imx_i2c_read,
+ .write = imx_i2c_write,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 2,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription imx_i2c_vmstate = {
+ .name = TYPE_IMX_I2C,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT16(address, IMXI2CState),
+ VMSTATE_UINT16(iadr, IMXI2CState),
+ VMSTATE_UINT16(ifdr, IMXI2CState),
+ VMSTATE_UINT16(i2cr, IMXI2CState),
+ VMSTATE_UINT16(i2sr, IMXI2CState),
+ VMSTATE_UINT16(i2dr_read, IMXI2CState),
+ VMSTATE_UINT16(i2dr_write, IMXI2CState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void imx_i2c_realize(DeviceState *dev, Error **errp)
+{
+ IMXI2CState *s = IMX_I2C(dev);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &imx_i2c_ops, s, TYPE_IMX_I2C,
+ IMX_I2C_MEM_SIZE);
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
+ sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
+ s->bus = i2c_init_bus(DEVICE(dev), "i2c");
+}
+
+static void imx_i2c_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->vmsd = &imx_i2c_vmstate;
+ dc->reset = imx_i2c_reset;
+ dc->realize = imx_i2c_realize;
+ dc->desc = "i.MX I2C Controller";
+}
+
+static const TypeInfo imx_i2c_type_info = {
+ .name = TYPE_IMX_I2C,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(IMXI2CState),
+ .class_init = imx_i2c_class_init,
+};
+
+static void imx_i2c_register_types(void)
+{
+ type_register_static(&imx_i2c_type_info);
+}
+
+type_init(imx_i2c_register_types)
diff --git a/qemu/hw/i2c/omap_i2c.c b/qemu/hw/i2c/omap_i2c.c
index b6f544a22..67fbbff8e 100644
--- a/qemu/hw/i2c/omap_i2c.c
+++ b/qemu/hw/i2c/omap_i2c.c
@@ -16,10 +16,12 @@
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i2c/i2c.h"
#include "hw/arm/omap.h"
#include "hw/sysbus.h"
+#include "qemu/error-report.h"
#define TYPE_OMAP_I2C "omap_i2c"
#define OMAP_I2C(obj) OBJECT_CHECK(OMAPI2CState, (obj), TYPE_OMAP_I2C)
@@ -449,12 +451,15 @@ static int omap_i2c_init(SysBusDevice *sbd)
OMAPI2CState *s = OMAP_I2C(dev);
if (!s->fclk) {
- hw_error("omap_i2c: fclk not connected\n");
+ error_report("omap_i2c: fclk not connected");
+ return -1;
}
if (s->revision >= OMAP2_INTR_REV && !s->iclk) {
/* Note that OMAP1 doesn't have a separate interface clock */
- hw_error("omap_i2c: iclk not connected\n");
+ error_report("omap_i2c: iclk not connected");
+ return -1;
}
+
sysbus_init_irq(sbd, &s->irq);
sysbus_init_irq(sbd, &s->drq[0]);
sysbus_init_irq(sbd, &s->drq[1]);
diff --git a/qemu/hw/i2c/pm_smbus.c b/qemu/hw/i2c/pm_smbus.c
index ce1713d26..6fc3923f5 100644
--- a/qemu/hw/i2c/pm_smbus.c
+++ b/qemu/hw/i2c/pm_smbus.c
@@ -17,6 +17,7 @@
* License along with this library; if not, see
* <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/i2c/pm_smbus.h"
diff --git a/qemu/hw/i2c/smbus.c b/qemu/hw/i2c/smbus.c
index 6e27ae8bd..3979b3dad 100644
--- a/qemu/hw/i2c/smbus.c
+++ b/qemu/hw/i2c/smbus.c
@@ -9,6 +9,7 @@
/* TODO: Implement PEC. */
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i2c/i2c.h"
#include "hw/i2c/smbus.h"
diff --git a/qemu/hw/i2c/smbus_eeprom.c b/qemu/hw/i2c/smbus_eeprom.c
index 72c09cba6..5b7bd891b 100644
--- a/qemu/hw/i2c/smbus_eeprom.c
+++ b/qemu/hw/i2c/smbus_eeprom.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i2c/i2c.h"
#include "hw/i2c/smbus.h"
diff --git a/qemu/hw/i2c/smbus_ich9.c b/qemu/hw/i2c/smbus_ich9.c
index 91d4d322c..498f03e83 100644
--- a/qemu/hw/i2c/smbus_ich9.c
+++ b/qemu/hw/i2c/smbus_ich9.c
@@ -24,6 +24,7 @@
* GNU GPL, version 2 or (at your option) any later version.
*
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/i2c/pm_smbus.h"
diff --git a/qemu/hw/i2c/versatile_i2c.c b/qemu/hw/i2c/versatile_i2c.c
index 3c0c2c106..fee3bc761 100644
--- a/qemu/hw/i2c/versatile_i2c.c
+++ b/qemu/hw/i2c/versatile_i2c.c
@@ -21,6 +21,7 @@
*
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "bitbang_i2c.h"
diff --git a/qemu/hw/i386/Makefile.objs b/qemu/hw/i386/Makefile.objs
index bd4f147f9..b52d5b875 100644
--- a/qemu/hw/i386/Makefile.objs
+++ b/qemu/hw/i386/Makefile.objs
@@ -1,5 +1,5 @@
obj-$(CONFIG_KVM) += kvm/
-obj-y += multiboot.o smbios.o
+obj-y += multiboot.o
obj-y += pc.o pc_piix.o pc_q35.o
obj-y += pc_sysfw.o
obj-y += intel_iommu.o
@@ -7,27 +7,4 @@ obj-$(CONFIG_XEN) += ../xenpv/ xen/
obj-y += kvmvapic.o
obj-y += acpi-build.o
-hw/i386/acpi-build.o: hw/i386/acpi-build.c \
- hw/i386/acpi-dsdt.hex hw/i386/q35-acpi-dsdt.hex
-
-iasl-option=$(shell if test -z "`$(1) $(2) 2>&1 > /dev/null`" \
- ; then echo "$(2)"; else echo "$(3)"; fi ;)
-
-ifdef IASL
-#IASL Present. Generate hex files from .dsl
-hw/i386/%.hex: $(SRC_PATH)/hw/i386/%.dsl $(SRC_PATH)/scripts/acpi_extract_preprocess.py $(SRC_PATH)/scripts/acpi_extract.py
- $(call quiet-command, $(CPP) -x c -P $(QEMU_DGFLAGS) $(QEMU_INCLUDES) $< -o $*.dsl.i.orig, " CPP $(TARGET_DIR)$*.dsl.i.orig")
- $(call quiet-command, $(PYTHON) $(SRC_PATH)/scripts/acpi_extract_preprocess.py $*.dsl.i.orig > $*.dsl.i, " ACPI_PREPROCESS $(TARGET_DIR)$*.dsl.i")
- $(call quiet-command, $(IASL) $(call iasl-option,$(IASL),-Pn,) -vs -l -tc -p $* $*.dsl.i $(if $(V), , > /dev/null) 2>&1 ," IASL $(TARGET_DIR)$*.dsl.i")
- $(call quiet-command, $(PYTHON) $(SRC_PATH)/scripts/acpi_extract.py $*.lst > $*.off, " ACPI_EXTRACT $(TARGET_DIR)$*.off")
- $(call quiet-command, cat $*.off > $@, " CAT $(TARGET_DIR)$@")
-else
-#IASL Not present. Restore pre-generated hex files.
-hw/i386/%.hex: $(SRC_PATH)/hw/i386/%.hex.generated
- $(call quiet-command, cp -f $< $@, " CP $(TARGET_DIR)$@")
-endif
-
-.PHONY: cleanhex
-cleanhex:
- rm -f hw/i386/*hex
-clean: cleanhex
+obj-y += pci-assign-load-rom.o
diff --git a/qemu/hw/i386/acpi-build.c b/qemu/hw/i386/acpi-build.c
index 46eddb8e4..64770034f 100644
--- a/qemu/hw/i386/acpi-build.c
+++ b/qemu/hw/i386/acpi-build.c
@@ -20,12 +20,12 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "acpi-build.h"
-#include <stddef.h>
#include <glib.h>
#include "qemu-common.h"
#include "qemu/bitmap.h"
-#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "hw/pci/pci.h"
#include "qom/cpu.h"
@@ -38,10 +38,12 @@
#include "hw/acpi/bios-linker-loader.h"
#include "hw/loader.h"
#include "hw/isa/isa.h"
+#include "hw/block/fdc.h"
#include "hw/acpi/memory_hotplug.h"
#include "sysemu/tpm.h"
#include "hw/acpi/tpm.h"
#include "sysemu/tpm_backend.h"
+#include "hw/timer/mc146818rtc_regs.h"
/* Supported chipsets: */
#include "hw/acpi/piix4.h"
@@ -50,9 +52,7 @@
#include "hw/pci/pci_bus.h"
#include "hw/pci-host/q35.h"
#include "hw/i386/intel_iommu.h"
-
-#include "hw/i386/q35-acpi-dsdt.hex"
-#include "hw/i386/acpi-dsdt.hex"
+#include "hw/timer/hpet.h"
#include "hw/acpi/aml-build.h"
@@ -77,10 +77,6 @@
#define ACPI_BUILD_DPRINTF(fmt, ...)
#endif
-typedef struct AcpiCpuInfo {
- DECLARE_BITMAP(found_cpus, ACPI_CPU_HOTPLUG_ID_LIMIT);
-} AcpiCpuInfo;
-
typedef struct AcpiMcfgInfo {
uint64_t mcfg_base;
uint32_t mcfg_size;
@@ -106,6 +102,7 @@ typedef struct AcpiPmInfo {
} AcpiPmInfo;
typedef struct AcpiMiscInfo {
+ bool is_piix4;
bool has_hpet;
TPMVersion tpm_version;
const unsigned char *dsdt_code;
@@ -121,47 +118,6 @@ typedef struct AcpiBuildPciBusHotplugState {
bool pcihp_bridge_en;
} AcpiBuildPciBusHotplugState;
-static void acpi_get_dsdt(AcpiMiscInfo *info)
-{
- Object *piix = piix4_pm_find();
- Object *lpc = ich9_lpc_find();
- assert(!!piix != !!lpc);
-
- if (piix) {
- info->dsdt_code = AcpiDsdtAmlCode;
- info->dsdt_size = sizeof AcpiDsdtAmlCode;
- }
- if (lpc) {
- info->dsdt_code = Q35AcpiDsdtAmlCode;
- info->dsdt_size = sizeof Q35AcpiDsdtAmlCode;
- }
-}
-
-static
-int acpi_add_cpu_info(Object *o, void *opaque)
-{
- AcpiCpuInfo *cpu = opaque;
- uint64_t apic_id;
-
- if (object_dynamic_cast(o, TYPE_CPU)) {
- apic_id = object_property_get_int(o, "apic-id", NULL);
- assert(apic_id < ACPI_CPU_HOTPLUG_ID_LIMIT);
-
- set_bit(apic_id, cpu->found_cpus);
- }
-
- object_child_foreach(o, acpi_add_cpu_info, opaque);
- return 0;
-}
-
-static void acpi_get_cpu_info(AcpiCpuInfo *cpu)
-{
- Object *root = object_get_root();
-
- memset(cpu->found_cpus, 0, sizeof cpu->found_cpus);
- object_child_foreach(root, acpi_add_cpu_info, cpu);
-}
-
static void acpi_get_pm_info(AcpiPmInfo *pm)
{
Object *piix = piix4_pm_find();
@@ -169,6 +125,7 @@ static void acpi_get_pm_info(AcpiPmInfo *pm)
Object *obj = NULL;
QObject *o;
+ pm->cpu_hp_io_base = 0;
pm->pcihp_io_base = 0;
pm->pcihp_io_len = 0;
if (piix) {
@@ -234,6 +191,17 @@ static void acpi_get_pm_info(AcpiPmInfo *pm)
static void acpi_get_misc_info(AcpiMiscInfo *info)
{
+ Object *piix = piix4_pm_find();
+ Object *lpc = ich9_lpc_find();
+ assert(!!piix != !!lpc);
+
+ if (piix) {
+ info->is_piix4 = true;
+ }
+ if (lpc) {
+ info->is_piix4 = false;
+ }
+
info->has_hpet = hpet_find();
info->tpm_version = tpm_get_version();
info->pvpanic_port = pvpanic_port();
@@ -294,7 +262,7 @@ static void acpi_align_size(GArray *blob, unsigned align)
/* FACS */
static void
-build_facs(GArray *table_data, GArray *linker, PcGuestInfo *guest_info)
+build_facs(GArray *table_data, GArray *linker)
{
AcpiFacsDescriptorRev1 *facs = acpi_data_push(table_data, sizeof *facs);
memcpy(&facs->signature, "FACS", 4);
@@ -333,13 +301,15 @@ static void fadt_setup(AcpiFadtDescriptorRev1 *fadt, AcpiPmInfo *pm)
if (max_cpus > 8) {
fadt->flags |= cpu_to_le32(1 << ACPI_FADT_F_FORCE_APIC_CLUSTER_MODEL);
}
+ fadt->century = RTC_CENTURY;
}
/* FADT */
static void
build_fadt(GArray *table_data, GArray *linker, AcpiPmInfo *pm,
- unsigned facs, unsigned dsdt)
+ unsigned facs, unsigned dsdt,
+ const char *oem_id, const char *oem_table_id)
{
AcpiFadtDescriptorRev1 *fadt = acpi_data_push(table_data, sizeof(*fadt));
@@ -360,13 +330,14 @@ build_fadt(GArray *table_data, GArray *linker, AcpiPmInfo *pm,
fadt_setup(fadt, pm);
build_header(linker, table_data,
- (void *)fadt, "FACP", sizeof(*fadt), 1);
+ (void *)fadt, "FACP", sizeof(*fadt), 1, oem_id, oem_table_id);
}
static void
-build_madt(GArray *table_data, GArray *linker, AcpiCpuInfo *cpu,
- PcGuestInfo *guest_info)
+build_madt(GArray *table_data, GArray *linker, PCMachineState *pcms)
{
+ MachineClass *mc = MACHINE_GET_CLASS(pcms);
+ CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(MACHINE(pcms));
int madt_start = table_data->len;
AcpiMultipleApicTable *madt;
@@ -379,18 +350,28 @@ build_madt(GArray *table_data, GArray *linker, AcpiCpuInfo *cpu,
madt->local_apic_address = cpu_to_le32(APIC_DEFAULT_ADDRESS);
madt->flags = cpu_to_le32(1);
- for (i = 0; i < guest_info->apic_id_limit; i++) {
+ for (i = 0; i < apic_ids->len; i++) {
AcpiMadtProcessorApic *apic = acpi_data_push(table_data, sizeof *apic);
+ int apic_id = apic_ids->cpus[i].arch_id;
+
apic->type = ACPI_APIC_PROCESSOR;
apic->length = sizeof(*apic);
- apic->processor_id = i;
- apic->local_apic_id = i;
- if (test_bit(i, cpu->found_cpus)) {
+ apic->processor_id = apic_id;
+ apic->local_apic_id = apic_id;
+ if (apic_ids->cpus[i].cpu != NULL) {
apic->flags = cpu_to_le32(1);
} else {
+ /* ACPI spec says that LAPIC entry for non present
+ * CPU may be omitted from MADT or it must be marked
+ * as disabled. However omitting non present CPU from
+ * MADT breaks hotplug on linux. So possible CPUs
+ * should be put in MADT but kept disabled.
+ */
apic->flags = cpu_to_le32(0);
}
}
+ g_free(apic_ids);
+
io_apic = acpi_data_push(table_data, sizeof *io_apic);
io_apic->type = ACPI_APIC_IO;
io_apic->length = sizeof(*io_apic);
@@ -399,7 +380,7 @@ build_madt(GArray *table_data, GArray *linker, AcpiCpuInfo *cpu,
io_apic->address = cpu_to_le32(IO_APIC_DEFAULT_ADDRESS);
io_apic->interrupt = cpu_to_le32(0);
- if (guest_info->apic_xrupt_override) {
+ if (pcms->apic_xrupt_override) {
intsrcovr = acpi_data_push(table_data, sizeof *intsrcovr);
intsrcovr->type = ACPI_APIC_XRUPT_OVERRIDE;
intsrcovr->length = sizeof(*intsrcovr);
@@ -430,7 +411,7 @@ build_madt(GArray *table_data, GArray *linker, AcpiCpuInfo *cpu,
build_header(linker, table_data,
(void *)(table_data->data + madt_start), "APIC",
- table_data->len - madt_start, 1);
+ table_data->len - madt_start, 1, NULL, NULL);
}
/* Assign BSEL property to all buses. In the future, this can be changed
@@ -468,7 +449,7 @@ static void build_append_pcihp_notify_entry(Aml *method, int slot)
Aml *if_ctx;
int32_t devfn = PCI_DEVFN(slot, 0);
- if_ctx = aml_if(aml_and(aml_arg(0), aml_int(0x1U << slot)));
+ if_ctx = aml_if(aml_and(aml_arg(0), aml_int(0x1U << slot), NULL));
aml_append(if_ctx, aml_notify(aml_name("S%.02X", devfn), aml_arg(1)));
aml_append(method, if_ctx);
}
@@ -486,7 +467,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
int64_t bsel_val = qint_get_int(qobject_to_qint(bsel));
aml_append(parent_scope, aml_name_decl("BSEL", aml_int(bsel_val)));
- notify_method = aml_method("DVNT", 2);
+ notify_method = aml_method("DVNT", 2, AML_NOTSERIALIZED);
}
for (i = 0; i < ARRAY_SIZE(bus->devices); i += PCI_FUNC_MAX) {
@@ -502,7 +483,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
dev = aml_device("S%.02X", PCI_DEVFN(slot, 0));
aml_append(dev, aml_name_decl("_SUN", aml_int(slot)));
aml_append(dev, aml_name_decl("_ADR", aml_int(slot << 16)));
- method = aml_method("_EJ0", 1);
+ method = aml_method("_EJ0", 1, AML_NOTSERIALIZED);
aml_append(method,
aml_call2("PCEJ", aml_name("BSEL"), aml_name("_SUN"))
);
@@ -545,22 +526,22 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
s3d = 0;
}
- method = aml_method("_S1D", 0);
+ method = aml_method("_S1D", 0, AML_NOTSERIALIZED);
aml_append(method, aml_return(aml_int(0)));
aml_append(dev, method);
- method = aml_method("_S2D", 0);
+ method = aml_method("_S2D", 0, AML_NOTSERIALIZED);
aml_append(method, aml_return(aml_int(0)));
aml_append(dev, method);
- method = aml_method("_S3D", 0);
+ method = aml_method("_S3D", 0, AML_NOTSERIALIZED);
aml_append(method, aml_return(aml_int(s3d)));
aml_append(dev, method);
} else if (hotplug_enabled_dev) {
/* add _SUN/_EJ0 to make slot hotpluggable */
aml_append(dev, aml_name_decl("_SUN", aml_int(slot)));
- method = aml_method("_EJ0", 1);
+ method = aml_method("_EJ0", 1, AML_NOTSERIALIZED);
aml_append(method,
aml_call2("PCEJ", aml_name("BSEL"), aml_name("_SUN"))
);
@@ -589,7 +570,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
/* Append PCNT method to notify about events on local and child buses.
* Add unconditionally for root since DSDT expects it.
*/
- method = aml_method("PCNT", 0);
+ method = aml_method("PCNT", 0, AML_NOTSERIALIZED);
/* If bus supports hotplug select it and notify about local events */
if (bsel) {
@@ -615,6 +596,23 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
qobject_decref(bsel);
}
+/**
+ * build_prt_entry:
+ * @link_name: link name for PCI route entry
+ *
+ * build AML package containing a PCI route entry for @link_name
+ */
+static Aml *build_prt_entry(const char *link_name)
+{
+ Aml *a_zero = aml_int(0);
+ Aml *pkg = aml_package(4);
+ aml_append(pkg, a_zero);
+ aml_append(pkg, a_zero);
+ aml_append(pkg, aml_name("%s", link_name));
+ aml_append(pkg, a_zero);
+ return pkg;
+}
+
/*
* initialize_route - Initialize the interrupt routing rule
* through a specific LINK:
@@ -625,12 +623,8 @@ static Aml *initialize_route(Aml *route, const char *link_name,
Aml *lnk_idx, int idx)
{
Aml *if_ctx = aml_if(aml_equal(lnk_idx, aml_int(idx)));
- Aml *pkg = aml_package(4);
+ Aml *pkg = build_prt_entry(link_name);
- aml_append(pkg, aml_int(0));
- aml_append(pkg, aml_int(0));
- aml_append(pkg, aml_name("%s", link_name));
- aml_append(pkg, aml_int(0));
aml_append(if_ctx, aml_store(pkg, route));
return if_ctx;
@@ -646,11 +640,11 @@ static Aml *initialize_route(Aml *route, const char *link_name,
* The hash function is (slot + pin) & 3 -> "LNK[D|A|B|C]".
*
*/
-static Aml *build_prt(void)
+static Aml *build_prt(bool is_pci0_prt)
{
Aml *method, *while_ctx, *pin, *res;
- method = aml_method("_PRT", 0);
+ method = aml_method("_PRT", 0, AML_NOTSERIALIZED);
res = aml_local(0);
pin = aml_local(1);
aml_append(method, aml_store(aml_package(128), res));
@@ -665,24 +659,49 @@ static Aml *build_prt(void)
/* slot = pin >> 2 */
aml_append(while_ctx,
- aml_store(aml_shiftright(pin, aml_int(2)), slot));
+ aml_store(aml_shiftright(pin, aml_int(2), NULL), slot));
/* lnk_idx = (slot + pin) & 3 */
aml_append(while_ctx,
- aml_store(aml_and(aml_add(pin, slot), aml_int(3)), lnk_idx));
+ aml_store(aml_and(aml_add(pin, slot, NULL), aml_int(3), NULL),
+ lnk_idx));
/* route[2] = "LNK[D|A|B|C]", selection based on pin % 3 */
aml_append(while_ctx, initialize_route(route, "LNKD", lnk_idx, 0));
- aml_append(while_ctx, initialize_route(route, "LNKA", lnk_idx, 1));
+ if (is_pci0_prt) {
+ Aml *if_device_1, *if_pin_4, *else_pin_4;
+
+ /* device 1 is the power-management device, needs SCI */
+ if_device_1 = aml_if(aml_equal(lnk_idx, aml_int(1)));
+ {
+ if_pin_4 = aml_if(aml_equal(pin, aml_int(4)));
+ {
+ aml_append(if_pin_4,
+ aml_store(build_prt_entry("LNKS"), route));
+ }
+ aml_append(if_device_1, if_pin_4);
+ else_pin_4 = aml_else();
+ {
+ aml_append(else_pin_4,
+ aml_store(build_prt_entry("LNKA"), route));
+ }
+ aml_append(if_device_1, else_pin_4);
+ }
+ aml_append(while_ctx, if_device_1);
+ } else {
+ aml_append(while_ctx, initialize_route(route, "LNKA", lnk_idx, 1));
+ }
aml_append(while_ctx, initialize_route(route, "LNKB", lnk_idx, 2));
aml_append(while_ctx, initialize_route(route, "LNKC", lnk_idx, 3));
/* route[0] = 0x[slot]FFFF */
aml_append(while_ctx,
- aml_store(aml_or(aml_shiftleft(slot, aml_int(16)), aml_int(0xFFFF)),
+ aml_store(aml_or(aml_shiftleft(slot, aml_int(16)), aml_int(0xFFFF),
+ NULL),
aml_index(route, aml_int(0))));
/* route[1] = pin & 3 */
aml_append(while_ctx,
- aml_store(aml_and(pin, aml_int(3)), aml_index(route, aml_int(1))));
+ aml_store(aml_and(pin, aml_int(3), NULL),
+ aml_index(route, aml_int(1))));
/* res[pin] = route */
aml_append(while_ctx, aml_store(route, aml_index(res, pin)));
/* pin++ */
@@ -761,16 +780,59 @@ static void crs_replace_with_free_ranges(GPtrArray *ranges,
g_ptr_array_free(free_ranges, false);
}
+/*
+ * crs_range_merge - merges adjacent ranges in the given array.
+ * Array elements are deleted and replaced with the merged ranges.
+ */
+static void crs_range_merge(GPtrArray *range)
+{
+ GPtrArray *tmp = g_ptr_array_new_with_free_func(crs_range_free);
+ CrsRangeEntry *entry;
+ uint64_t range_base, range_limit;
+ int i;
+
+ if (!range->len) {
+ return;
+ }
+
+ g_ptr_array_sort(range, crs_range_compare);
+
+ entry = g_ptr_array_index(range, 0);
+ range_base = entry->base;
+ range_limit = entry->limit;
+ for (i = 1; i < range->len; i++) {
+ entry = g_ptr_array_index(range, i);
+ if (entry->base - 1 == range_limit) {
+ range_limit = entry->limit;
+ } else {
+ crs_range_insert(tmp, range_base, range_limit);
+ range_base = entry->base;
+ range_limit = entry->limit;
+ }
+ }
+ crs_range_insert(tmp, range_base, range_limit);
+
+ g_ptr_array_set_size(range, 0);
+ for (i = 0; i < tmp->len; i++) {
+ entry = g_ptr_array_index(tmp, i);
+ crs_range_insert(range, entry->base, entry->limit);
+ }
+ g_ptr_array_free(tmp, true);
+}
+
static Aml *build_crs(PCIHostState *host,
GPtrArray *io_ranges, GPtrArray *mem_ranges)
{
Aml *crs = aml_resource_template();
+ GPtrArray *host_io_ranges = g_ptr_array_new_with_free_func(crs_range_free);
+ GPtrArray *host_mem_ranges = g_ptr_array_new_with_free_func(crs_range_free);
+ CrsRangeEntry *entry;
uint8_t max_bus = pci_bus_num(host->bus);
uint8_t type;
int devfn;
+ int i;
for (devfn = 0; devfn < ARRAY_SIZE(host->bus->devices); devfn++) {
- int i;
uint64_t range_base, range_limit;
PCIDevice *dev = host->bus->devices[devfn];
@@ -793,26 +855,9 @@ static Aml *build_crs(PCIHostState *host,
}
if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
- aml_append(crs,
- aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED,
- AML_POS_DECODE, AML_ENTIRE_RANGE,
- 0,
- range_base,
- range_limit,
- 0,
- range_limit - range_base + 1));
- crs_range_insert(io_ranges, range_base, range_limit);
+ crs_range_insert(host_io_ranges, range_base, range_limit);
} else { /* "memory" */
- aml_append(crs,
- aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
- AML_MAX_FIXED, AML_NON_CACHEABLE,
- AML_READ_WRITE,
- 0,
- range_base,
- range_limit,
- 0,
- range_limit - range_base + 1));
- crs_range_insert(mem_ranges, range_base, range_limit);
+ crs_range_insert(host_mem_ranges, range_base, range_limit);
}
}
@@ -831,15 +876,7 @@ static Aml *build_crs(PCIHostState *host,
* that do not support multiple root buses
*/
if (range_base && range_base <= range_limit) {
- aml_append(crs,
- aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED,
- AML_POS_DECODE, AML_ENTIRE_RANGE,
- 0,
- range_base,
- range_limit,
- 0,
- range_limit - range_base + 1));
- crs_range_insert(io_ranges, range_base, range_limit);
+ crs_range_insert(host_io_ranges, range_base, range_limit);
}
range_base =
@@ -852,16 +889,7 @@ static Aml *build_crs(PCIHostState *host,
* that do not support multiple root buses
*/
if (range_base && range_base <= range_limit) {
- aml_append(crs,
- aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
- AML_MAX_FIXED, AML_NON_CACHEABLE,
- AML_READ_WRITE,
- 0,
- range_base,
- range_limit,
- 0,
- range_limit - range_base + 1));
- crs_range_insert(mem_ranges, range_base, range_limit);
+ crs_range_insert(host_mem_ranges, range_base, range_limit);
}
range_base =
@@ -874,20 +902,36 @@ static Aml *build_crs(PCIHostState *host,
* that do not support multiple root buses
*/
if (range_base && range_base <= range_limit) {
- aml_append(crs,
- aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
- AML_MAX_FIXED, AML_NON_CACHEABLE,
- AML_READ_WRITE,
- 0,
- range_base,
- range_limit,
- 0,
- range_limit - range_base + 1));
- crs_range_insert(mem_ranges, range_base, range_limit);
+ crs_range_insert(host_mem_ranges, range_base, range_limit);
}
}
}
+ crs_range_merge(host_io_ranges);
+ for (i = 0; i < host_io_ranges->len; i++) {
+ entry = g_ptr_array_index(host_io_ranges, i);
+ aml_append(crs,
+ aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED,
+ AML_POS_DECODE, AML_ENTIRE_RANGE,
+ 0, entry->base, entry->limit, 0,
+ entry->limit - entry->base + 1));
+ crs_range_insert(io_ranges, entry->base, entry->limit);
+ }
+ g_ptr_array_free(host_io_ranges, true);
+
+ crs_range_merge(host_mem_ranges);
+ for (i = 0; i < host_mem_ranges->len; i++) {
+ entry = g_ptr_array_index(host_mem_ranges, i);
+ aml_append(crs,
+ aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
+ AML_MAX_FIXED, AML_NON_CACHEABLE,
+ AML_READ_WRITE,
+ 0, entry->base, entry->limit, 0,
+ entry->limit - entry->base + 1));
+ crs_range_insert(mem_ranges, entry->base, entry->limit);
+ }
+ g_ptr_array_free(host_mem_ranges, true);
+
aml_append(crs,
aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE,
0,
@@ -899,33 +943,1151 @@ static Aml *build_crs(PCIHostState *host,
return crs;
}
+static void build_processor_devices(Aml *sb_scope, MachineState *machine,
+ AcpiPmInfo *pm)
+{
+ int i, apic_idx;
+ Aml *dev;
+ Aml *crs;
+ Aml *pkg;
+ Aml *field;
+ Aml *ifctx;
+ Aml *method;
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
+ CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(machine);
+ PCMachineState *pcms = PC_MACHINE(machine);
+
+ /* The current AML generator can cover the APIC ID range [0..255],
+ * inclusive, for VCPU hotplug. */
+ QEMU_BUILD_BUG_ON(ACPI_CPU_HOTPLUG_ID_LIMIT > 256);
+ g_assert(pcms->apic_id_limit <= ACPI_CPU_HOTPLUG_ID_LIMIT);
+
+ /* create PCI0.PRES device and its _CRS to reserve CPU hotplug MMIO */
+ dev = aml_device("PCI0." stringify(CPU_HOTPLUG_RESOURCE_DEVICE));
+ aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A06")));
+ aml_append(dev,
+ aml_name_decl("_UID", aml_string("CPU Hotplug resources"))
+ );
+ /* device present, functioning, decoding, not shown in UI */
+ aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
+ crs = aml_resource_template();
+ aml_append(crs,
+ aml_io(AML_DECODE16, pm->cpu_hp_io_base, pm->cpu_hp_io_base, 1,
+ pm->cpu_hp_io_len)
+ );
+ aml_append(dev, aml_name_decl("_CRS", crs));
+ aml_append(sb_scope, dev);
+ /* declare CPU hotplug MMIO region and PRS field to access it */
+ aml_append(sb_scope, aml_operation_region(
+ "PRST", AML_SYSTEM_IO, aml_int(pm->cpu_hp_io_base), pm->cpu_hp_io_len));
+ field = aml_field("PRST", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
+ aml_append(field, aml_named_field("PRS", 256));
+ aml_append(sb_scope, field);
+
+ /* build Processor object for each processor */
+ for (i = 0; i < apic_ids->len; i++) {
+ int apic_id = apic_ids->cpus[i].arch_id;
+
+ assert(apic_id < ACPI_CPU_HOTPLUG_ID_LIMIT);
+
+ dev = aml_processor(apic_id, 0, 0, "CP%.02X", apic_id);
+
+ method = aml_method("_MAT", 0, AML_NOTSERIALIZED);
+ aml_append(method,
+ aml_return(aml_call1(CPU_MAT_METHOD, aml_int(apic_id))));
+ aml_append(dev, method);
+
+ method = aml_method("_STA", 0, AML_NOTSERIALIZED);
+ aml_append(method,
+ aml_return(aml_call1(CPU_STATUS_METHOD, aml_int(apic_id))));
+ aml_append(dev, method);
+
+ method = aml_method("_EJ0", 1, AML_NOTSERIALIZED);
+ aml_append(method,
+ aml_return(aml_call2(CPU_EJECT_METHOD, aml_int(apic_id),
+ aml_arg(0)))
+ );
+ aml_append(dev, method);
+
+ aml_append(sb_scope, dev);
+ }
+
+ /* build this code:
+ * Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...}
+ */
+ /* Arg0 = Processor ID = APIC ID */
+ method = aml_method(AML_NOTIFY_METHOD, 2, AML_NOTSERIALIZED);
+ for (i = 0; i < apic_ids->len; i++) {
+ int apic_id = apic_ids->cpus[i].arch_id;
+
+ ifctx = aml_if(aml_equal(aml_arg(0), aml_int(apic_id)));
+ aml_append(ifctx,
+ aml_notify(aml_name("CP%.02X", apic_id), aml_arg(1))
+ );
+ aml_append(method, ifctx);
+ }
+ aml_append(sb_scope, method);
+
+ /* build "Name(CPON, Package() { One, One, ..., Zero, Zero, ... })"
+ *
+ * Note: The ability to create variable-sized packages was first
+ * introduced in ACPI 2.0. ACPI 1.0 only allowed fixed-size packages
+ * ith up to 255 elements. Windows guests up to win2k8 fail when
+ * VarPackageOp is used.
+ */
+ pkg = pcms->apic_id_limit <= 255 ? aml_package(pcms->apic_id_limit) :
+ aml_varpackage(pcms->apic_id_limit);
+
+ for (i = 0, apic_idx = 0; i < apic_ids->len; i++) {
+ int apic_id = apic_ids->cpus[i].arch_id;
+
+ for (; apic_idx < apic_id; apic_idx++) {
+ aml_append(pkg, aml_int(0));
+ }
+ aml_append(pkg, aml_int(apic_ids->cpus[i].cpu ? 1 : 0));
+ apic_idx = apic_id + 1;
+ }
+ aml_append(sb_scope, aml_name_decl(CPU_ON_BITMAP, pkg));
+ g_free(apic_ids);
+}
+
+static void build_memory_devices(Aml *sb_scope, int nr_mem,
+ uint16_t io_base, uint16_t io_len)
+{
+ int i;
+ Aml *scope;
+ Aml *crs;
+ Aml *field;
+ Aml *dev;
+ Aml *method;
+ Aml *ifctx;
+
+ /* build memory devices */
+ assert(nr_mem <= ACPI_MAX_RAM_SLOTS);
+ scope = aml_scope("\\_SB.PCI0." MEMORY_HOTPLUG_DEVICE);
+ aml_append(scope,
+ aml_name_decl(MEMORY_SLOTS_NUMBER, aml_int(nr_mem))
+ );
+
+ crs = aml_resource_template();
+ aml_append(crs,
+ aml_io(AML_DECODE16, io_base, io_base, 0, io_len)
+ );
+ aml_append(scope, aml_name_decl("_CRS", crs));
+
+ aml_append(scope, aml_operation_region(
+ MEMORY_HOTPLUG_IO_REGION, AML_SYSTEM_IO,
+ aml_int(io_base), io_len)
+ );
+
+ field = aml_field(MEMORY_HOTPLUG_IO_REGION, AML_DWORD_ACC,
+ AML_NOLOCK, AML_PRESERVE);
+ aml_append(field, /* read only */
+ aml_named_field(MEMORY_SLOT_ADDR_LOW, 32));
+ aml_append(field, /* read only */
+ aml_named_field(MEMORY_SLOT_ADDR_HIGH, 32));
+ aml_append(field, /* read only */
+ aml_named_field(MEMORY_SLOT_SIZE_LOW, 32));
+ aml_append(field, /* read only */
+ aml_named_field(MEMORY_SLOT_SIZE_HIGH, 32));
+ aml_append(field, /* read only */
+ aml_named_field(MEMORY_SLOT_PROXIMITY, 32));
+ aml_append(scope, field);
+
+ field = aml_field(MEMORY_HOTPLUG_IO_REGION, AML_BYTE_ACC,
+ AML_NOLOCK, AML_WRITE_AS_ZEROS);
+ aml_append(field, aml_reserved_field(160 /* bits, Offset(20) */));
+ aml_append(field, /* 1 if enabled, read only */
+ aml_named_field(MEMORY_SLOT_ENABLED, 1));
+ aml_append(field,
+ /*(read) 1 if has a insert event. (write) 1 to clear event */
+ aml_named_field(MEMORY_SLOT_INSERT_EVENT, 1));
+ aml_append(field,
+ /* (read) 1 if has a remove event. (write) 1 to clear event */
+ aml_named_field(MEMORY_SLOT_REMOVE_EVENT, 1));
+ aml_append(field,
+ /* initiates device eject, write only */
+ aml_named_field(MEMORY_SLOT_EJECT, 1));
+ aml_append(scope, field);
+
+ field = aml_field(MEMORY_HOTPLUG_IO_REGION, AML_DWORD_ACC,
+ AML_NOLOCK, AML_PRESERVE);
+ aml_append(field, /* DIMM selector, write only */
+ aml_named_field(MEMORY_SLOT_SLECTOR, 32));
+ aml_append(field, /* _OST event code, write only */
+ aml_named_field(MEMORY_SLOT_OST_EVENT, 32));
+ aml_append(field, /* _OST status code, write only */
+ aml_named_field(MEMORY_SLOT_OST_STATUS, 32));
+ aml_append(scope, field);
+ aml_append(sb_scope, scope);
+
+ for (i = 0; i < nr_mem; i++) {
+ #define BASEPATH "\\_SB.PCI0." MEMORY_HOTPLUG_DEVICE "."
+ const char *s;
+
+ dev = aml_device("MP%02X", i);
+ aml_append(dev, aml_name_decl("_UID", aml_string("0x%02X", i)));
+ aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C80")));
+
+ method = aml_method("_CRS", 0, AML_NOTSERIALIZED);
+ s = BASEPATH MEMORY_SLOT_CRS_METHOD;
+ aml_append(method, aml_return(aml_call1(s, aml_name("_UID"))));
+ aml_append(dev, method);
+
+ method = aml_method("_STA", 0, AML_NOTSERIALIZED);
+ s = BASEPATH MEMORY_SLOT_STATUS_METHOD;
+ aml_append(method, aml_return(aml_call1(s, aml_name("_UID"))));
+ aml_append(dev, method);
+
+ method = aml_method("_PXM", 0, AML_NOTSERIALIZED);
+ s = BASEPATH MEMORY_SLOT_PROXIMITY_METHOD;
+ aml_append(method, aml_return(aml_call1(s, aml_name("_UID"))));
+ aml_append(dev, method);
+
+ method = aml_method("_OST", 3, AML_NOTSERIALIZED);
+ s = BASEPATH MEMORY_SLOT_OST_METHOD;
+
+ aml_append(method, aml_return(aml_call4(
+ s, aml_name("_UID"), aml_arg(0), aml_arg(1), aml_arg(2)
+ )));
+ aml_append(dev, method);
+
+ method = aml_method("_EJ0", 1, AML_NOTSERIALIZED);
+ s = BASEPATH MEMORY_SLOT_EJECT_METHOD;
+ aml_append(method, aml_return(aml_call2(
+ s, aml_name("_UID"), aml_arg(0))));
+ aml_append(dev, method);
+
+ aml_append(sb_scope, dev);
+ }
+
+ /* build Method(MEMORY_SLOT_NOTIFY_METHOD, 2) {
+ * If (LEqual(Arg0, 0x00)) {Notify(MP00, Arg1)} ... }
+ */
+ method = aml_method(MEMORY_SLOT_NOTIFY_METHOD, 2, AML_NOTSERIALIZED);
+ for (i = 0; i < nr_mem; i++) {
+ ifctx = aml_if(aml_equal(aml_arg(0), aml_int(i)));
+ aml_append(ifctx,
+ aml_notify(aml_name("MP%.02X", i), aml_arg(1))
+ );
+ aml_append(method, ifctx);
+ }
+ aml_append(sb_scope, method);
+}
+
+static void build_hpet_aml(Aml *table)
+{
+ Aml *crs;
+ Aml *field;
+ Aml *method;
+ Aml *if_ctx;
+ Aml *scope = aml_scope("_SB");
+ Aml *dev = aml_device("HPET");
+ Aml *zero = aml_int(0);
+ Aml *id = aml_local(0);
+ Aml *period = aml_local(1);
+
+ aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0103")));
+ aml_append(dev, aml_name_decl("_UID", zero));
+
+ aml_append(dev,
+ aml_operation_region("HPTM", AML_SYSTEM_MEMORY, aml_int(HPET_BASE),
+ HPET_LEN));
+ field = aml_field("HPTM", AML_DWORD_ACC, AML_LOCK, AML_PRESERVE);
+ aml_append(field, aml_named_field("VEND", 32));
+ aml_append(field, aml_named_field("PRD", 32));
+ aml_append(dev, field);
+
+ method = aml_method("_STA", 0, AML_NOTSERIALIZED);
+ aml_append(method, aml_store(aml_name("VEND"), id));
+ aml_append(method, aml_store(aml_name("PRD"), period));
+ aml_append(method, aml_shiftright(id, aml_int(16), id));
+ if_ctx = aml_if(aml_lor(aml_equal(id, zero),
+ aml_equal(id, aml_int(0xffff))));
+ {
+ aml_append(if_ctx, aml_return(zero));
+ }
+ aml_append(method, if_ctx);
+
+ if_ctx = aml_if(aml_lor(aml_equal(period, zero),
+ aml_lgreater(period, aml_int(100000000))));
+ {
+ aml_append(if_ctx, aml_return(zero));
+ }
+ aml_append(method, if_ctx);
+
+ aml_append(method, aml_return(aml_int(0x0F)));
+ aml_append(dev, method);
+
+ crs = aml_resource_template();
+ aml_append(crs, aml_memory32_fixed(HPET_BASE, HPET_LEN, AML_READ_ONLY));
+ aml_append(dev, aml_name_decl("_CRS", crs));
+
+ aml_append(scope, dev);
+ aml_append(table, scope);
+}
+
+static Aml *build_fdinfo_aml(int idx, FloppyDriveType type)
+{
+ Aml *dev, *fdi;
+ uint8_t maxc, maxh, maxs;
+
+ isa_fdc_get_drive_max_chs(type, &maxc, &maxh, &maxs);
+
+ dev = aml_device("FLP%c", 'A' + idx);
+
+ aml_append(dev, aml_name_decl("_ADR", aml_int(idx)));
+
+ fdi = aml_package(16);
+ aml_append(fdi, aml_int(idx)); /* Drive Number */
+ aml_append(fdi,
+ aml_int(cmos_get_fd_drive_type(type))); /* Device Type */
+ /*
+ * the values below are the limits of the drive, and are thus independent
+ * of the inserted media
+ */
+ aml_append(fdi, aml_int(maxc)); /* Maximum Cylinder Number */
+ aml_append(fdi, aml_int(maxs)); /* Maximum Sector Number */
+ aml_append(fdi, aml_int(maxh)); /* Maximum Head Number */
+ /*
+ * SeaBIOS returns the below values for int 0x13 func 0x08 regardless of
+ * the drive type, so shall we
+ */
+ aml_append(fdi, aml_int(0xAF)); /* disk_specify_1 */
+ aml_append(fdi, aml_int(0x02)); /* disk_specify_2 */
+ aml_append(fdi, aml_int(0x25)); /* disk_motor_wait */
+ aml_append(fdi, aml_int(0x02)); /* disk_sector_siz */
+ aml_append(fdi, aml_int(0x12)); /* disk_eot */
+ aml_append(fdi, aml_int(0x1B)); /* disk_rw_gap */
+ aml_append(fdi, aml_int(0xFF)); /* disk_dtl */
+ aml_append(fdi, aml_int(0x6C)); /* disk_formt_gap */
+ aml_append(fdi, aml_int(0xF6)); /* disk_fill */
+ aml_append(fdi, aml_int(0x0F)); /* disk_head_sttl */
+ aml_append(fdi, aml_int(0x08)); /* disk_motor_strt */
+
+ aml_append(dev, aml_name_decl("_FDI", fdi));
+ return dev;
+}
+
+static Aml *build_fdc_device_aml(ISADevice *fdc)
+{
+ int i;
+ Aml *dev;
+ Aml *crs;
+
+#define ACPI_FDE_MAX_FD 4
+ uint32_t fde_buf[5] = {
+ 0, 0, 0, 0, /* presence of floppy drives #0 - #3 */
+ cpu_to_le32(2) /* tape presence (2 == never present) */
+ };
+
+ dev = aml_device("FDC0");
+ aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0700")));
+
+ crs = aml_resource_template();
+ aml_append(crs, aml_io(AML_DECODE16, 0x03F2, 0x03F2, 0x00, 0x04));
+ aml_append(crs, aml_io(AML_DECODE16, 0x03F7, 0x03F7, 0x00, 0x01));
+ aml_append(crs, aml_irq_no_flags(6));
+ aml_append(crs,
+ aml_dma(AML_COMPATIBILITY, AML_NOTBUSMASTER, AML_TRANSFER8, 2));
+ aml_append(dev, aml_name_decl("_CRS", crs));
+
+ for (i = 0; i < MIN(MAX_FD, ACPI_FDE_MAX_FD); i++) {
+ FloppyDriveType type = isa_fdc_get_drive_type(fdc, i);
+
+ if (type < FLOPPY_DRIVE_TYPE_NONE) {
+ fde_buf[i] = cpu_to_le32(1); /* drive present */
+ aml_append(dev, build_fdinfo_aml(i, type));
+ }
+ }
+ aml_append(dev, aml_name_decl("_FDE",
+ aml_buffer(sizeof(fde_buf), (uint8_t *)fde_buf)));
+
+ return dev;
+}
+
+static Aml *build_rtc_device_aml(void)
+{
+ Aml *dev;
+ Aml *crs;
+
+ dev = aml_device("RTC");
+ aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0B00")));
+ crs = aml_resource_template();
+ aml_append(crs, aml_io(AML_DECODE16, 0x0070, 0x0070, 0x10, 0x02));
+ aml_append(crs, aml_irq_no_flags(8));
+ aml_append(crs, aml_io(AML_DECODE16, 0x0072, 0x0072, 0x02, 0x06));
+ aml_append(dev, aml_name_decl("_CRS", crs));
+
+ return dev;
+}
+
+static Aml *build_kbd_device_aml(void)
+{
+ Aml *dev;
+ Aml *crs;
+ Aml *method;
+
+ dev = aml_device("KBD");
+ aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0303")));
+
+ method = aml_method("_STA", 0, AML_NOTSERIALIZED);
+ aml_append(method, aml_return(aml_int(0x0f)));
+ aml_append(dev, method);
+
+ crs = aml_resource_template();
+ aml_append(crs, aml_io(AML_DECODE16, 0x0060, 0x0060, 0x01, 0x01));
+ aml_append(crs, aml_io(AML_DECODE16, 0x0064, 0x0064, 0x01, 0x01));
+ aml_append(crs, aml_irq_no_flags(1));
+ aml_append(dev, aml_name_decl("_CRS", crs));
+
+ return dev;
+}
+
+static Aml *build_mouse_device_aml(void)
+{
+ Aml *dev;
+ Aml *crs;
+ Aml *method;
+
+ dev = aml_device("MOU");
+ aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0F13")));
+
+ method = aml_method("_STA", 0, AML_NOTSERIALIZED);
+ aml_append(method, aml_return(aml_int(0x0f)));
+ aml_append(dev, method);
+
+ crs = aml_resource_template();
+ aml_append(crs, aml_irq_no_flags(12));
+ aml_append(dev, aml_name_decl("_CRS", crs));
+
+ return dev;
+}
+
+static Aml *build_lpt_device_aml(void)
+{
+ Aml *dev;
+ Aml *crs;
+ Aml *method;
+ Aml *if_ctx;
+ Aml *else_ctx;
+ Aml *zero = aml_int(0);
+ Aml *is_present = aml_local(0);
+
+ dev = aml_device("LPT");
+ aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0400")));
+
+ method = aml_method("_STA", 0, AML_NOTSERIALIZED);
+ aml_append(method, aml_store(aml_name("LPEN"), is_present));
+ if_ctx = aml_if(aml_equal(is_present, zero));
+ {
+ aml_append(if_ctx, aml_return(aml_int(0x00)));
+ }
+ aml_append(method, if_ctx);
+ else_ctx = aml_else();
+ {
+ aml_append(else_ctx, aml_return(aml_int(0x0f)));
+ }
+ aml_append(method, else_ctx);
+ aml_append(dev, method);
+
+ crs = aml_resource_template();
+ aml_append(crs, aml_io(AML_DECODE16, 0x0378, 0x0378, 0x08, 0x08));
+ aml_append(crs, aml_irq_no_flags(7));
+ aml_append(dev, aml_name_decl("_CRS", crs));
+
+ return dev;
+}
+
+static Aml *build_com_device_aml(uint8_t uid)
+{
+ Aml *dev;
+ Aml *crs;
+ Aml *method;
+ Aml *if_ctx;
+ Aml *else_ctx;
+ Aml *zero = aml_int(0);
+ Aml *is_present = aml_local(0);
+ const char *enabled_field = "CAEN";
+ uint8_t irq = 4;
+ uint16_t io_port = 0x03F8;
+
+ assert(uid == 1 || uid == 2);
+ if (uid == 2) {
+ enabled_field = "CBEN";
+ irq = 3;
+ io_port = 0x02F8;
+ }
+
+ dev = aml_device("COM%d", uid);
+ aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0501")));
+ aml_append(dev, aml_name_decl("_UID", aml_int(uid)));
+
+ method = aml_method("_STA", 0, AML_NOTSERIALIZED);
+ aml_append(method, aml_store(aml_name("%s", enabled_field), is_present));
+ if_ctx = aml_if(aml_equal(is_present, zero));
+ {
+ aml_append(if_ctx, aml_return(aml_int(0x00)));
+ }
+ aml_append(method, if_ctx);
+ else_ctx = aml_else();
+ {
+ aml_append(else_ctx, aml_return(aml_int(0x0f)));
+ }
+ aml_append(method, else_ctx);
+ aml_append(dev, method);
+
+ crs = aml_resource_template();
+ aml_append(crs, aml_io(AML_DECODE16, io_port, io_port, 0x00, 0x08));
+ aml_append(crs, aml_irq_no_flags(irq));
+ aml_append(dev, aml_name_decl("_CRS", crs));
+
+ return dev;
+}
+
+static void build_isa_devices_aml(Aml *table)
+{
+ ISADevice *fdc = pc_find_fdc0();
+
+ Aml *scope = aml_scope("_SB.PCI0.ISA");
+
+ aml_append(scope, build_rtc_device_aml());
+ aml_append(scope, build_kbd_device_aml());
+ aml_append(scope, build_mouse_device_aml());
+ if (fdc) {
+ aml_append(scope, build_fdc_device_aml(fdc));
+ }
+ aml_append(scope, build_lpt_device_aml());
+ aml_append(scope, build_com_device_aml(1));
+ aml_append(scope, build_com_device_aml(2));
+
+ aml_append(table, scope);
+}
+
+static void build_dbg_aml(Aml *table)
+{
+ Aml *field;
+ Aml *method;
+ Aml *while_ctx;
+ Aml *scope = aml_scope("\\");
+ Aml *buf = aml_local(0);
+ Aml *len = aml_local(1);
+ Aml *idx = aml_local(2);
+
+ aml_append(scope,
+ aml_operation_region("DBG", AML_SYSTEM_IO, aml_int(0x0402), 0x01));
+ field = aml_field("DBG", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
+ aml_append(field, aml_named_field("DBGB", 8));
+ aml_append(scope, field);
+
+ method = aml_method("DBUG", 1, AML_NOTSERIALIZED);
+
+ aml_append(method, aml_to_hexstring(aml_arg(0), buf));
+ aml_append(method, aml_to_buffer(buf, buf));
+ aml_append(method, aml_subtract(aml_sizeof(buf), aml_int(1), len));
+ aml_append(method, aml_store(aml_int(0), idx));
+
+ while_ctx = aml_while(aml_lless(idx, len));
+ aml_append(while_ctx,
+ aml_store(aml_derefof(aml_index(buf, idx)), aml_name("DBGB")));
+ aml_append(while_ctx, aml_increment(idx));
+ aml_append(method, while_ctx);
+
+ aml_append(method, aml_store(aml_int(0x0A), aml_name("DBGB")));
+ aml_append(scope, method);
+
+ aml_append(table, scope);
+}
+
+static Aml *build_link_dev(const char *name, uint8_t uid, Aml *reg)
+{
+ Aml *dev;
+ Aml *crs;
+ Aml *method;
+ uint32_t irqs[] = {5, 10, 11};
+
+ dev = aml_device("%s", name);
+ aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C0F")));
+ aml_append(dev, aml_name_decl("_UID", aml_int(uid)));
+
+ crs = aml_resource_template();
+ aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
+ AML_SHARED, irqs, ARRAY_SIZE(irqs)));
+ aml_append(dev, aml_name_decl("_PRS", crs));
+
+ method = aml_method("_STA", 0, AML_NOTSERIALIZED);
+ aml_append(method, aml_return(aml_call1("IQST", reg)));
+ aml_append(dev, method);
+
+ method = aml_method("_DIS", 0, AML_NOTSERIALIZED);
+ aml_append(method, aml_or(reg, aml_int(0x80), reg));
+ aml_append(dev, method);
+
+ method = aml_method("_CRS", 0, AML_NOTSERIALIZED);
+ aml_append(method, aml_return(aml_call1("IQCR", reg)));
+ aml_append(dev, method);
+
+ method = aml_method("_SRS", 1, AML_NOTSERIALIZED);
+ aml_append(method, aml_create_dword_field(aml_arg(0), aml_int(5), "PRRI"));
+ aml_append(method, aml_store(aml_name("PRRI"), reg));
+ aml_append(dev, method);
+
+ return dev;
+ }
+
+static Aml *build_gsi_link_dev(const char *name, uint8_t uid, uint8_t gsi)
+{
+ Aml *dev;
+ Aml *crs;
+ Aml *method;
+ uint32_t irqs;
+
+ dev = aml_device("%s", name);
+ aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C0F")));
+ aml_append(dev, aml_name_decl("_UID", aml_int(uid)));
+
+ crs = aml_resource_template();
+ irqs = gsi;
+ aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
+ AML_SHARED, &irqs, 1));
+ aml_append(dev, aml_name_decl("_PRS", crs));
+
+ aml_append(dev, aml_name_decl("_CRS", crs));
+
+ /*
+ * _DIS can be no-op because the interrupt cannot be disabled.
+ */
+ method = aml_method("_DIS", 0, AML_NOTSERIALIZED);
+ aml_append(dev, method);
+
+ method = aml_method("_SRS", 1, AML_NOTSERIALIZED);
+ aml_append(dev, method);
+
+ return dev;
+}
+
+/* _CRS method - get current settings */
+static Aml *build_iqcr_method(bool is_piix4)
+{
+ Aml *if_ctx;
+ uint32_t irqs;
+ Aml *method = aml_method("IQCR", 1, AML_SERIALIZED);
+ Aml *crs = aml_resource_template();
+
+ irqs = 0;
+ aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL,
+ AML_ACTIVE_HIGH, AML_SHARED, &irqs, 1));
+ aml_append(method, aml_name_decl("PRR0", crs));
+
+ aml_append(method,
+ aml_create_dword_field(aml_name("PRR0"), aml_int(5), "PRRI"));
+
+ if (is_piix4) {
+ if_ctx = aml_if(aml_lless(aml_arg(0), aml_int(0x80)));
+ aml_append(if_ctx, aml_store(aml_arg(0), aml_name("PRRI")));
+ aml_append(method, if_ctx);
+ } else {
+ aml_append(method,
+ aml_store(aml_and(aml_arg(0), aml_int(0xF), NULL),
+ aml_name("PRRI")));
+ }
+
+ aml_append(method, aml_return(aml_name("PRR0")));
+ return method;
+}
+
+/* _STA method - get status */
+static Aml *build_irq_status_method(void)
+{
+ Aml *if_ctx;
+ Aml *method = aml_method("IQST", 1, AML_NOTSERIALIZED);
+
+ if_ctx = aml_if(aml_and(aml_int(0x80), aml_arg(0), NULL));
+ aml_append(if_ctx, aml_return(aml_int(0x09)));
+ aml_append(method, if_ctx);
+ aml_append(method, aml_return(aml_int(0x0B)));
+ return method;
+}
+
+static void build_piix4_pci0_int(Aml *table)
+{
+ Aml *dev;
+ Aml *crs;
+ Aml *field;
+ Aml *method;
+ uint32_t irqs;
+ Aml *sb_scope = aml_scope("_SB");
+ Aml *pci0_scope = aml_scope("PCI0");
+
+ aml_append(pci0_scope, build_prt(true));
+ aml_append(sb_scope, pci0_scope);
+
+ field = aml_field("PCI0.ISA.P40C", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
+ aml_append(field, aml_named_field("PRQ0", 8));
+ aml_append(field, aml_named_field("PRQ1", 8));
+ aml_append(field, aml_named_field("PRQ2", 8));
+ aml_append(field, aml_named_field("PRQ3", 8));
+ aml_append(sb_scope, field);
+
+ aml_append(sb_scope, build_irq_status_method());
+ aml_append(sb_scope, build_iqcr_method(true));
+
+ aml_append(sb_scope, build_link_dev("LNKA", 0, aml_name("PRQ0")));
+ aml_append(sb_scope, build_link_dev("LNKB", 1, aml_name("PRQ1")));
+ aml_append(sb_scope, build_link_dev("LNKC", 2, aml_name("PRQ2")));
+ aml_append(sb_scope, build_link_dev("LNKD", 3, aml_name("PRQ3")));
+
+ dev = aml_device("LNKS");
+ {
+ aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C0F")));
+ aml_append(dev, aml_name_decl("_UID", aml_int(4)));
+
+ crs = aml_resource_template();
+ irqs = 9;
+ aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL,
+ AML_ACTIVE_HIGH, AML_SHARED,
+ &irqs, 1));
+ aml_append(dev, aml_name_decl("_PRS", crs));
+
+ /* The SCI cannot be disabled and is always attached to GSI 9,
+ * so these are no-ops. We only need this link to override the
+ * polarity to active high and match the content of the MADT.
+ */
+ method = aml_method("_STA", 0, AML_NOTSERIALIZED);
+ aml_append(method, aml_return(aml_int(0x0b)));
+ aml_append(dev, method);
+
+ method = aml_method("_DIS", 0, AML_NOTSERIALIZED);
+ aml_append(dev, method);
+
+ method = aml_method("_CRS", 0, AML_NOTSERIALIZED);
+ aml_append(method, aml_return(aml_name("_PRS")));
+ aml_append(dev, method);
+
+ method = aml_method("_SRS", 1, AML_NOTSERIALIZED);
+ aml_append(dev, method);
+ }
+ aml_append(sb_scope, dev);
+
+ aml_append(table, sb_scope);
+}
+
+static void append_q35_prt_entry(Aml *ctx, uint32_t nr, const char *name)
+{
+ int i;
+ int head;
+ Aml *pkg;
+ char base = name[3] < 'E' ? 'A' : 'E';
+ char *s = g_strdup(name);
+ Aml *a_nr = aml_int((nr << 16) | 0xffff);
+
+ assert(strlen(s) == 4);
+
+ head = name[3] - base;
+ for (i = 0; i < 4; i++) {
+ if (head + i > 3) {
+ head = i * -1;
+ }
+ s[3] = base + head + i;
+ pkg = aml_package(4);
+ aml_append(pkg, a_nr);
+ aml_append(pkg, aml_int(i));
+ aml_append(pkg, aml_name("%s", s));
+ aml_append(pkg, aml_int(0));
+ aml_append(ctx, pkg);
+ }
+ g_free(s);
+}
+
+static Aml *build_q35_routing_table(const char *str)
+{
+ int i;
+ Aml *pkg;
+ char *name = g_strdup_printf("%s ", str);
+
+ pkg = aml_package(128);
+ for (i = 0; i < 0x18; i++) {
+ name[3] = 'E' + (i & 0x3);
+ append_q35_prt_entry(pkg, i, name);
+ }
+
+ name[3] = 'E';
+ append_q35_prt_entry(pkg, 0x18, name);
+
+ /* INTA -> PIRQA for slot 25 - 31, see the default value of D<N>IR */
+ for (i = 0x0019; i < 0x1e; i++) {
+ name[3] = 'A';
+ append_q35_prt_entry(pkg, i, name);
+ }
+
+ /* PCIe->PCI bridge. use PIRQ[E-H] */
+ name[3] = 'E';
+ append_q35_prt_entry(pkg, 0x1e, name);
+ name[3] = 'A';
+ append_q35_prt_entry(pkg, 0x1f, name);
+
+ g_free(name);
+ return pkg;
+}
+
+static void build_q35_pci0_int(Aml *table)
+{
+ Aml *field;
+ Aml *method;
+ Aml *sb_scope = aml_scope("_SB");
+ Aml *pci0_scope = aml_scope("PCI0");
+
+ /* Zero => PIC mode, One => APIC Mode */
+ aml_append(table, aml_name_decl("PICF", aml_int(0)));
+ method = aml_method("_PIC", 1, AML_NOTSERIALIZED);
+ {
+ aml_append(method, aml_store(aml_arg(0), aml_name("PICF")));
+ }
+ aml_append(table, method);
+
+ aml_append(pci0_scope,
+ aml_name_decl("PRTP", build_q35_routing_table("LNK")));
+ aml_append(pci0_scope,
+ aml_name_decl("PRTA", build_q35_routing_table("GSI")));
+
+ method = aml_method("_PRT", 0, AML_NOTSERIALIZED);
+ {
+ Aml *if_ctx;
+ Aml *else_ctx;
+
+ /* PCI IRQ routing table, example from ACPI 2.0a specification,
+ section 6.2.8.1 */
+ /* Note: we provide the same info as the PCI routing
+ table of the Bochs BIOS */
+ if_ctx = aml_if(aml_equal(aml_name("PICF"), aml_int(0)));
+ aml_append(if_ctx, aml_return(aml_name("PRTP")));
+ aml_append(method, if_ctx);
+ else_ctx = aml_else();
+ aml_append(else_ctx, aml_return(aml_name("PRTA")));
+ aml_append(method, else_ctx);
+ }
+ aml_append(pci0_scope, method);
+ aml_append(sb_scope, pci0_scope);
+
+ field = aml_field("PCI0.ISA.PIRQ", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
+ aml_append(field, aml_named_field("PRQA", 8));
+ aml_append(field, aml_named_field("PRQB", 8));
+ aml_append(field, aml_named_field("PRQC", 8));
+ aml_append(field, aml_named_field("PRQD", 8));
+ aml_append(field, aml_reserved_field(0x20));
+ aml_append(field, aml_named_field("PRQE", 8));
+ aml_append(field, aml_named_field("PRQF", 8));
+ aml_append(field, aml_named_field("PRQG", 8));
+ aml_append(field, aml_named_field("PRQH", 8));
+ aml_append(sb_scope, field);
+
+ aml_append(sb_scope, build_irq_status_method());
+ aml_append(sb_scope, build_iqcr_method(false));
+
+ aml_append(sb_scope, build_link_dev("LNKA", 0, aml_name("PRQA")));
+ aml_append(sb_scope, build_link_dev("LNKB", 1, aml_name("PRQB")));
+ aml_append(sb_scope, build_link_dev("LNKC", 2, aml_name("PRQC")));
+ aml_append(sb_scope, build_link_dev("LNKD", 3, aml_name("PRQD")));
+ aml_append(sb_scope, build_link_dev("LNKE", 4, aml_name("PRQE")));
+ aml_append(sb_scope, build_link_dev("LNKF", 5, aml_name("PRQF")));
+ aml_append(sb_scope, build_link_dev("LNKG", 6, aml_name("PRQG")));
+ aml_append(sb_scope, build_link_dev("LNKH", 7, aml_name("PRQH")));
+
+ aml_append(sb_scope, build_gsi_link_dev("GSIA", 0x10, 0x10));
+ aml_append(sb_scope, build_gsi_link_dev("GSIB", 0x11, 0x11));
+ aml_append(sb_scope, build_gsi_link_dev("GSIC", 0x12, 0x12));
+ aml_append(sb_scope, build_gsi_link_dev("GSID", 0x13, 0x13));
+ aml_append(sb_scope, build_gsi_link_dev("GSIE", 0x14, 0x14));
+ aml_append(sb_scope, build_gsi_link_dev("GSIF", 0x15, 0x15));
+ aml_append(sb_scope, build_gsi_link_dev("GSIG", 0x16, 0x16));
+ aml_append(sb_scope, build_gsi_link_dev("GSIH", 0x17, 0x17));
+
+ aml_append(table, sb_scope);
+}
+
+static void build_q35_isa_bridge(Aml *table)
+{
+ Aml *dev;
+ Aml *scope;
+ Aml *field;
+
+ scope = aml_scope("_SB.PCI0");
+ dev = aml_device("ISA");
+ aml_append(dev, aml_name_decl("_ADR", aml_int(0x001F0000)));
+
+ /* ICH9 PCI to ISA irq remapping */
+ aml_append(dev, aml_operation_region("PIRQ", AML_PCI_CONFIG,
+ aml_int(0x60), 0x0C));
+
+ aml_append(dev, aml_operation_region("LPCD", AML_PCI_CONFIG,
+ aml_int(0x80), 0x02));
+ field = aml_field("LPCD", AML_ANY_ACC, AML_NOLOCK, AML_PRESERVE);
+ aml_append(field, aml_named_field("COMA", 3));
+ aml_append(field, aml_reserved_field(1));
+ aml_append(field, aml_named_field("COMB", 3));
+ aml_append(field, aml_reserved_field(1));
+ aml_append(field, aml_named_field("LPTD", 2));
+ aml_append(dev, field);
+
+ aml_append(dev, aml_operation_region("LPCE", AML_PCI_CONFIG,
+ aml_int(0x82), 0x02));
+ /* enable bits */
+ field = aml_field("LPCE", AML_ANY_ACC, AML_NOLOCK, AML_PRESERVE);
+ aml_append(field, aml_named_field("CAEN", 1));
+ aml_append(field, aml_named_field("CBEN", 1));
+ aml_append(field, aml_named_field("LPEN", 1));
+ aml_append(dev, field);
+
+ aml_append(scope, dev);
+ aml_append(table, scope);
+}
+
+static void build_piix4_pm(Aml *table)
+{
+ Aml *dev;
+ Aml *scope;
+
+ scope = aml_scope("_SB.PCI0");
+ dev = aml_device("PX13");
+ aml_append(dev, aml_name_decl("_ADR", aml_int(0x00010003)));
+
+ aml_append(dev, aml_operation_region("P13C", AML_PCI_CONFIG,
+ aml_int(0x00), 0xff));
+ aml_append(scope, dev);
+ aml_append(table, scope);
+}
+
+static void build_piix4_isa_bridge(Aml *table)
+{
+ Aml *dev;
+ Aml *scope;
+ Aml *field;
+
+ scope = aml_scope("_SB.PCI0");
+ dev = aml_device("ISA");
+ aml_append(dev, aml_name_decl("_ADR", aml_int(0x00010000)));
+
+ /* PIIX PCI to ISA irq remapping */
+ aml_append(dev, aml_operation_region("P40C", AML_PCI_CONFIG,
+ aml_int(0x60), 0x04));
+ /* enable bits */
+ field = aml_field("^PX13.P13C", AML_ANY_ACC, AML_NOLOCK, AML_PRESERVE);
+ /* Offset(0x5f),, 7, */
+ aml_append(field, aml_reserved_field(0x2f8));
+ aml_append(field, aml_reserved_field(7));
+ aml_append(field, aml_named_field("LPEN", 1));
+ /* Offset(0x67),, 3, */
+ aml_append(field, aml_reserved_field(0x38));
+ aml_append(field, aml_reserved_field(3));
+ aml_append(field, aml_named_field("CAEN", 1));
+ aml_append(field, aml_reserved_field(3));
+ aml_append(field, aml_named_field("CBEN", 1));
+ aml_append(dev, field);
+
+ aml_append(scope, dev);
+ aml_append(table, scope);
+}
+
+static void build_piix4_pci_hotplug(Aml *table)
+{
+ Aml *scope;
+ Aml *field;
+ Aml *method;
+
+ scope = aml_scope("_SB.PCI0");
+
+ aml_append(scope,
+ aml_operation_region("PCST", AML_SYSTEM_IO, aml_int(0xae00), 0x08));
+ field = aml_field("PCST", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS);
+ aml_append(field, aml_named_field("PCIU", 32));
+ aml_append(field, aml_named_field("PCID", 32));
+ aml_append(scope, field);
+
+ aml_append(scope,
+ aml_operation_region("SEJ", AML_SYSTEM_IO, aml_int(0xae08), 0x04));
+ field = aml_field("SEJ", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS);
+ aml_append(field, aml_named_field("B0EJ", 32));
+ aml_append(scope, field);
+
+ aml_append(scope,
+ aml_operation_region("BNMR", AML_SYSTEM_IO, aml_int(0xae10), 0x04));
+ field = aml_field("BNMR", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS);
+ aml_append(field, aml_named_field("BNUM", 32));
+ aml_append(scope, field);
+
+ aml_append(scope, aml_mutex("BLCK", 0));
+
+ method = aml_method("PCEJ", 2, AML_NOTSERIALIZED);
+ aml_append(method, aml_acquire(aml_name("BLCK"), 0xFFFF));
+ aml_append(method, aml_store(aml_arg(0), aml_name("BNUM")));
+ aml_append(method,
+ aml_store(aml_shiftleft(aml_int(1), aml_arg(1)), aml_name("B0EJ")));
+ aml_append(method, aml_release(aml_name("BLCK")));
+ aml_append(method, aml_return(aml_int(0)));
+ aml_append(scope, method);
+
+ aml_append(table, scope);
+}
+
+static Aml *build_q35_osc_method(void)
+{
+ Aml *if_ctx;
+ Aml *if_ctx2;
+ Aml *else_ctx;
+ Aml *method;
+ Aml *a_cwd1 = aml_name("CDW1");
+ Aml *a_ctrl = aml_name("CTRL");
+
+ method = aml_method("_OSC", 4, AML_NOTSERIALIZED);
+ aml_append(method, aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1"));
+
+ if_ctx = aml_if(aml_equal(
+ aml_arg(0), aml_touuid("33DB4D5B-1FF7-401C-9657-7441C03DD766")));
+ aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(4), "CDW2"));
+ aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3"));
+
+ aml_append(if_ctx, aml_store(aml_name("CDW2"), aml_name("SUPP")));
+ aml_append(if_ctx, aml_store(aml_name("CDW3"), a_ctrl));
+
+ /*
+ * Always allow native PME, AER (no dependencies)
+ * Never allow SHPC (no SHPC controller in this system)
+ */
+ aml_append(if_ctx, aml_and(a_ctrl, aml_int(0x1D), a_ctrl));
+
+ if_ctx2 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(1))));
+ /* Unknown revision */
+ aml_append(if_ctx2, aml_or(a_cwd1, aml_int(0x08), a_cwd1));
+ aml_append(if_ctx, if_ctx2);
+
+ if_ctx2 = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), a_ctrl)));
+ /* Capabilities bits were masked */
+ aml_append(if_ctx2, aml_or(a_cwd1, aml_int(0x10), a_cwd1));
+ aml_append(if_ctx, if_ctx2);
+
+ /* Update DWORD3 in the buffer */
+ aml_append(if_ctx, aml_store(a_ctrl, aml_name("CDW3")));
+ aml_append(method, if_ctx);
+
+ else_ctx = aml_else();
+ /* Unrecognized UUID */
+ aml_append(else_ctx, aml_or(a_cwd1, aml_int(4), a_cwd1));
+ aml_append(method, else_ctx);
+
+ aml_append(method, aml_return(aml_arg(3)));
+ return method;
+}
+
static void
-build_ssdt(GArray *table_data, GArray *linker,
- AcpiCpuInfo *cpu, AcpiPmInfo *pm, AcpiMiscInfo *misc,
- PcPciInfo *pci, PcGuestInfo *guest_info)
+build_dsdt(GArray *table_data, GArray *linker,
+ AcpiPmInfo *pm, AcpiMiscInfo *misc,
+ PcPciInfo *pci, MachineState *machine)
{
- MachineState *machine = MACHINE(qdev_get_machine());
- uint32_t nr_mem = machine->ram_slots;
- unsigned acpi_cpus = guest_info->apic_id_limit;
- Aml *ssdt, *sb_scope, *scope, *pkg, *dev, *method, *crs, *field, *ifctx;
- PCIBus *bus = NULL;
- GPtrArray *io_ranges = g_ptr_array_new_with_free_func(crs_range_free);
- GPtrArray *mem_ranges = g_ptr_array_new_with_free_func(crs_range_free);
CrsRangeEntry *entry;
+ Aml *dsdt, *sb_scope, *scope, *dev, *method, *field, *pkg, *crs;
+ GPtrArray *mem_ranges = g_ptr_array_new_with_free_func(crs_range_free);
+ GPtrArray *io_ranges = g_ptr_array_new_with_free_func(crs_range_free);
+ PCMachineState *pcms = PC_MACHINE(machine);
+ uint32_t nr_mem = machine->ram_slots;
int root_bus_limit = 0xFF;
+ PCIBus *bus = NULL;
int i;
- ssdt = init_aml_allocator();
- /* The current AML generator can cover the APIC ID range [0..255],
- * inclusive, for VCPU hotplug. */
- QEMU_BUILD_BUG_ON(ACPI_CPU_HOTPLUG_ID_LIMIT > 256);
- g_assert(acpi_cpus <= ACPI_CPU_HOTPLUG_ID_LIMIT);
+ dsdt = init_aml_allocator();
/* Reserve space for header */
- acpi_data_push(ssdt->buf, sizeof(AcpiTableHeader));
+ acpi_data_push(dsdt->buf, sizeof(AcpiTableHeader));
+
+ build_dbg_aml(dsdt);
+ if (misc->is_piix4) {
+ sb_scope = aml_scope("_SB");
+ dev = aml_device("PCI0");
+ aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A03")));
+ aml_append(dev, aml_name_decl("_ADR", aml_int(0)));
+ aml_append(dev, aml_name_decl("_UID", aml_int(1)));
+ aml_append(sb_scope, dev);
+ aml_append(dsdt, sb_scope);
+
+ build_hpet_aml(dsdt);
+ build_piix4_pm(dsdt);
+ build_piix4_isa_bridge(dsdt);
+ build_isa_devices_aml(dsdt);
+ build_piix4_pci_hotplug(dsdt);
+ build_piix4_pci0_int(dsdt);
+ } else {
+ sb_scope = aml_scope("_SB");
+ aml_append(sb_scope,
+ aml_operation_region("PCST", AML_SYSTEM_IO, aml_int(0xae00), 0x0c));
+ aml_append(sb_scope,
+ aml_operation_region("PCSB", AML_SYSTEM_IO, aml_int(0xae0c), 0x01));
+ field = aml_field("PCSB", AML_ANY_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS);
+ aml_append(field, aml_named_field("PCIB", 8));
+ aml_append(sb_scope, field);
+ aml_append(dsdt, sb_scope);
+
+ sb_scope = aml_scope("_SB");
+ dev = aml_device("PCI0");
+ aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A08")));
+ aml_append(dev, aml_name_decl("_CID", aml_eisaid("PNP0A03")));
+ aml_append(dev, aml_name_decl("_ADR", aml_int(0)));
+ aml_append(dev, aml_name_decl("_UID", aml_int(1)));
+ aml_append(dev, aml_name_decl("SUPP", aml_int(0)));
+ aml_append(dev, aml_name_decl("CTRL", aml_int(0)));
+ aml_append(dev, build_q35_osc_method());
+ aml_append(sb_scope, dev);
+ aml_append(dsdt, sb_scope);
+
+ build_hpet_aml(dsdt);
+ build_q35_isa_bridge(dsdt);
+ build_isa_devices_aml(dsdt);
+ build_q35_pci0_int(dsdt);
+ }
- /* Extra PCI root buses are implemented only for i440fx */
- bus = find_i440fx();
+ build_cpu_hotplug_aml(dsdt);
+ build_memory_hotplug_aml(dsdt, nr_mem, pm->mem_hp_io_base,
+ pm->mem_hp_io_len);
+
+ scope = aml_scope("_GPE");
+ {
+ aml_append(scope, aml_name_decl("_HID", aml_string("ACPI0006")));
+
+ aml_append(scope, aml_method("_L00", 0, AML_NOTSERIALIZED));
+
+ if (misc->is_piix4) {
+ method = aml_method("_E01", 0, AML_NOTSERIALIZED);
+ aml_append(method,
+ aml_acquire(aml_name("\\_SB.PCI0.BLCK"), 0xFFFF));
+ aml_append(method, aml_call0("\\_SB.PCI0.PCNT"));
+ aml_append(method, aml_release(aml_name("\\_SB.PCI0.BLCK")));
+ aml_append(scope, method);
+ } else {
+ aml_append(scope, aml_method("_L01", 0, AML_NOTSERIALIZED));
+ }
+
+ method = aml_method("_E02", 0, AML_NOTSERIALIZED);
+ aml_append(method, aml_call0("\\_SB." CPU_SCAN_METHOD));
+ aml_append(scope, method);
+
+ method = aml_method("_E03", 0, AML_NOTSERIALIZED);
+ aml_append(method, aml_call0(MEMORY_HOTPLUG_HANDLER_PATH));
+ aml_append(scope, method);
+
+ aml_append(scope, aml_method("_L04", 0, AML_NOTSERIALIZED));
+ aml_append(scope, aml_method("_L05", 0, AML_NOTSERIALIZED));
+ aml_append(scope, aml_method("_L06", 0, AML_NOTSERIALIZED));
+ aml_append(scope, aml_method("_L07", 0, AML_NOTSERIALIZED));
+ aml_append(scope, aml_method("_L08", 0, AML_NOTSERIALIZED));
+ aml_append(scope, aml_method("_L09", 0, AML_NOTSERIALIZED));
+ aml_append(scope, aml_method("_L0A", 0, AML_NOTSERIALIZED));
+ aml_append(scope, aml_method("_L0B", 0, AML_NOTSERIALIZED));
+ aml_append(scope, aml_method("_L0C", 0, AML_NOTSERIALIZED));
+ aml_append(scope, aml_method("_L0D", 0, AML_NOTSERIALIZED));
+ aml_append(scope, aml_method("_L0E", 0, AML_NOTSERIALIZED));
+ aml_append(scope, aml_method("_L0F", 0, AML_NOTSERIALIZED));
+ }
+ aml_append(dsdt, scope);
+
+ bus = PC_MACHINE(machine)->bus;
if (bus) {
QLIST_FOREACH(bus, &bus->child, sibling) {
uint8_t bus_num = pci_bus_num(bus);
@@ -950,12 +2112,12 @@ build_ssdt(GArray *table_data, GArray *linker,
aml_append(dev, aml_name_decl("_PXM", aml_int(numa_node)));
}
- aml_append(dev, build_prt());
+ aml_append(dev, build_prt(false));
crs = build_crs(PCI_HOST_BRIDGE(BUS(bus)->parent),
io_ranges, mem_ranges);
aml_append(dev, aml_name_decl("_CRS", crs));
aml_append(scope, dev);
- aml_append(ssdt, scope);
+ aml_append(dsdt, scope);
}
}
@@ -1005,6 +2167,11 @@ build_ssdt(GArray *table_data, GArray *linker,
0, pci->w64.begin, pci->w64.end - 1, 0,
pci->w64.end - pci->w64.begin));
}
+
+ if (misc->tpm_version != TPM_VERSION_UNSPEC) {
+ aml_append(crs, aml_memory32_fixed(TPM_TIS_ADDR_BASE,
+ TPM_TIS_ADDR_SIZE, AML_READ_WRITE));
+ }
aml_append(scope, aml_name_decl("_CRS", crs));
/* reserve GPE0 block resources */
@@ -1039,7 +2206,7 @@ build_ssdt(GArray *table_data, GArray *linker,
aml_append(dev, aml_name_decl("_CRS", crs));
aml_append(scope, dev);
}
- aml_append(ssdt, scope);
+ aml_append(dsdt, scope);
/* create S3_ / S4_ / S5_ packages if necessary */
scope = aml_scope("\\");
@@ -1068,7 +2235,36 @@ build_ssdt(GArray *table_data, GArray *linker,
aml_append(pkg, aml_int(0)); /* reserved */
aml_append(pkg, aml_int(0)); /* reserved */
aml_append(scope, aml_name_decl("_S5", pkg));
- aml_append(ssdt, scope);
+ aml_append(dsdt, scope);
+
+ /* create fw_cfg node, unconditionally */
+ {
+ /* when using port i/o, the 8-bit data register *always* overlaps
+ * with half of the 16-bit control register. Hence, the total size
+ * of the i/o region used is FW_CFG_CTL_SIZE; when using DMA, the
+ * DMA control register is located at FW_CFG_DMA_IO_BASE + 4 */
+ uint8_t io_size = object_property_get_bool(OBJECT(pcms->fw_cfg),
+ "dma_enabled", NULL) ?
+ ROUND_UP(FW_CFG_CTL_SIZE, 4) + sizeof(dma_addr_t) :
+ FW_CFG_CTL_SIZE;
+
+ scope = aml_scope("\\_SB.PCI0");
+ dev = aml_device("FWCF");
+
+ aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0002")));
+
+ /* device present, functioning, decoding, not shown in UI */
+ aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
+
+ crs = aml_resource_template();
+ aml_append(crs,
+ aml_io(AML_DECODE16, FW_CFG_IO_BASE, FW_CFG_IO_BASE, 0x01, io_size)
+ );
+ aml_append(dev, aml_name_decl("_CRS", crs));
+
+ aml_append(scope, dev);
+ aml_append(dsdt, scope);
+ }
if (misc->applesmc_io_base) {
scope = aml_scope("\\_SB.PCI0.ISA");
@@ -1087,7 +2283,7 @@ build_ssdt(GArray *table_data, GArray *linker,
aml_append(dev, aml_name_decl("_CRS", crs));
aml_append(scope, dev);
- aml_append(ssdt, scope);
+ aml_append(dsdt, scope);
}
if (misc->pvpanic_port) {
@@ -1103,214 +2299,33 @@ build_ssdt(GArray *table_data, GArray *linker,
aml_append(dev, aml_name_decl("_CRS", crs));
aml_append(dev, aml_operation_region("PEOR", AML_SYSTEM_IO,
- misc->pvpanic_port, 1));
- field = aml_field("PEOR", AML_BYTE_ACC, AML_PRESERVE);
+ aml_int(misc->pvpanic_port), 1));
+ field = aml_field("PEOR", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
aml_append(field, aml_named_field("PEPT", 8));
aml_append(dev, field);
/* device present, functioning, decoding, shown in UI */
aml_append(dev, aml_name_decl("_STA", aml_int(0xF)));
- method = aml_method("RDPT", 0);
+ method = aml_method("RDPT", 0, AML_NOTSERIALIZED);
aml_append(method, aml_store(aml_name("PEPT"), aml_local(0)));
aml_append(method, aml_return(aml_local(0)));
aml_append(dev, method);
- method = aml_method("WRPT", 1);
+ method = aml_method("WRPT", 1, AML_NOTSERIALIZED);
aml_append(method, aml_store(aml_arg(0), aml_name("PEPT")));
aml_append(dev, method);
aml_append(scope, dev);
- aml_append(ssdt, scope);
+ aml_append(dsdt, scope);
}
sb_scope = aml_scope("\\_SB");
{
- /* create PCI0.PRES device and its _CRS to reserve CPU hotplug MMIO */
- dev = aml_device("PCI0." stringify(CPU_HOTPLUG_RESOURCE_DEVICE));
- aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A06")));
- aml_append(dev,
- aml_name_decl("_UID", aml_string("CPU Hotplug resources"))
- );
- /* device present, functioning, decoding, not shown in UI */
- aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
- crs = aml_resource_template();
- aml_append(crs,
- aml_io(AML_DECODE16, pm->cpu_hp_io_base, pm->cpu_hp_io_base, 1,
- pm->cpu_hp_io_len)
- );
- aml_append(dev, aml_name_decl("_CRS", crs));
- aml_append(sb_scope, dev);
- /* declare CPU hotplug MMIO region and PRS field to access it */
- aml_append(sb_scope, aml_operation_region(
- "PRST", AML_SYSTEM_IO, pm->cpu_hp_io_base, pm->cpu_hp_io_len));
- field = aml_field("PRST", AML_BYTE_ACC, AML_PRESERVE);
- aml_append(field, aml_named_field("PRS", 256));
- aml_append(sb_scope, field);
-
- /* build Processor object for each processor */
- for (i = 0; i < acpi_cpus; i++) {
- dev = aml_processor(i, 0, 0, "CP%.02X", i);
-
- method = aml_method("_MAT", 0);
- aml_append(method, aml_return(aml_call1("CPMA", aml_int(i))));
- aml_append(dev, method);
-
- method = aml_method("_STA", 0);
- aml_append(method, aml_return(aml_call1("CPST", aml_int(i))));
- aml_append(dev, method);
-
- method = aml_method("_EJ0", 1);
- aml_append(method,
- aml_return(aml_call2("CPEJ", aml_int(i), aml_arg(0)))
- );
- aml_append(dev, method);
+ build_processor_devices(sb_scope, machine, pm);
- aml_append(sb_scope, dev);
- }
-
- /* build this code:
- * Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...}
- */
- /* Arg0 = Processor ID = APIC ID */
- method = aml_method("NTFY", 2);
- for (i = 0; i < acpi_cpus; i++) {
- ifctx = aml_if(aml_equal(aml_arg(0), aml_int(i)));
- aml_append(ifctx,
- aml_notify(aml_name("CP%.02X", i), aml_arg(1))
- );
- aml_append(method, ifctx);
- }
- aml_append(sb_scope, method);
-
- /* build "Name(CPON, Package() { One, One, ..., Zero, Zero, ... })"
- *
- * Note: The ability to create variable-sized packages was first
- * introduced in ACPI 2.0. ACPI 1.0 only allowed fixed-size packages
- * ith up to 255 elements. Windows guests up to win2k8 fail when
- * VarPackageOp is used.
- */
- pkg = acpi_cpus <= 255 ? aml_package(acpi_cpus) :
- aml_varpackage(acpi_cpus);
-
- for (i = 0; i < acpi_cpus; i++) {
- uint8_t b = test_bit(i, cpu->found_cpus) ? 0x01 : 0x00;
- aml_append(pkg, aml_int(b));
- }
- aml_append(sb_scope, aml_name_decl("CPON", pkg));
-
- /* build memory devices */
- assert(nr_mem <= ACPI_MAX_RAM_SLOTS);
- scope = aml_scope("\\_SB.PCI0." stringify(MEMORY_HOTPLUG_DEVICE));
- aml_append(scope,
- aml_name_decl(stringify(MEMORY_SLOTS_NUMBER), aml_int(nr_mem))
- );
-
- crs = aml_resource_template();
- aml_append(crs,
- aml_io(AML_DECODE16, pm->mem_hp_io_base, pm->mem_hp_io_base, 0,
- pm->mem_hp_io_len)
- );
- aml_append(scope, aml_name_decl("_CRS", crs));
-
- aml_append(scope, aml_operation_region(
- stringify(MEMORY_HOTPLUG_IO_REGION), AML_SYSTEM_IO,
- pm->mem_hp_io_base, pm->mem_hp_io_len)
- );
-
- field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), AML_DWORD_ACC,
- AML_PRESERVE);
- aml_append(field, /* read only */
- aml_named_field(stringify(MEMORY_SLOT_ADDR_LOW), 32));
- aml_append(field, /* read only */
- aml_named_field(stringify(MEMORY_SLOT_ADDR_HIGH), 32));
- aml_append(field, /* read only */
- aml_named_field(stringify(MEMORY_SLOT_SIZE_LOW), 32));
- aml_append(field, /* read only */
- aml_named_field(stringify(MEMORY_SLOT_SIZE_HIGH), 32));
- aml_append(field, /* read only */
- aml_named_field(stringify(MEMORY_SLOT_PROXIMITY), 32));
- aml_append(scope, field);
-
- field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), AML_BYTE_ACC,
- AML_WRITE_AS_ZEROS);
- aml_append(field, aml_reserved_field(160 /* bits, Offset(20) */));
- aml_append(field, /* 1 if enabled, read only */
- aml_named_field(stringify(MEMORY_SLOT_ENABLED), 1));
- aml_append(field,
- /*(read) 1 if has a insert event. (write) 1 to clear event */
- aml_named_field(stringify(MEMORY_SLOT_INSERT_EVENT), 1));
- aml_append(field,
- /* (read) 1 if has a remove event. (write) 1 to clear event */
- aml_named_field(stringify(MEMORY_SLOT_REMOVE_EVENT), 1));
- aml_append(field,
- /* initiates device eject, write only */
- aml_named_field(stringify(MEMORY_SLOT_EJECT), 1));
- aml_append(scope, field);
-
- field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), AML_DWORD_ACC,
- AML_PRESERVE);
- aml_append(field, /* DIMM selector, write only */
- aml_named_field(stringify(MEMORY_SLOT_SLECTOR), 32));
- aml_append(field, /* _OST event code, write only */
- aml_named_field(stringify(MEMORY_SLOT_OST_EVENT), 32));
- aml_append(field, /* _OST status code, write only */
- aml_named_field(stringify(MEMORY_SLOT_OST_STATUS), 32));
- aml_append(scope, field);
-
- aml_append(sb_scope, scope);
-
- for (i = 0; i < nr_mem; i++) {
- #define BASEPATH "\\_SB.PCI0." stringify(MEMORY_HOTPLUG_DEVICE) "."
- const char *s;
-
- dev = aml_device("MP%02X", i);
- aml_append(dev, aml_name_decl("_UID", aml_string("0x%02X", i)));
- aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C80")));
-
- method = aml_method("_CRS", 0);
- s = BASEPATH stringify(MEMORY_SLOT_CRS_METHOD);
- aml_append(method, aml_return(aml_call1(s, aml_name("_UID"))));
- aml_append(dev, method);
-
- method = aml_method("_STA", 0);
- s = BASEPATH stringify(MEMORY_SLOT_STATUS_METHOD);
- aml_append(method, aml_return(aml_call1(s, aml_name("_UID"))));
- aml_append(dev, method);
-
- method = aml_method("_PXM", 0);
- s = BASEPATH stringify(MEMORY_SLOT_PROXIMITY_METHOD);
- aml_append(method, aml_return(aml_call1(s, aml_name("_UID"))));
- aml_append(dev, method);
-
- method = aml_method("_OST", 3);
- s = BASEPATH stringify(MEMORY_SLOT_OST_METHOD);
- aml_append(method, aml_return(aml_call4(
- s, aml_name("_UID"), aml_arg(0), aml_arg(1), aml_arg(2)
- )));
- aml_append(dev, method);
-
- method = aml_method("_EJ0", 1);
- s = BASEPATH stringify(MEMORY_SLOT_EJECT_METHOD);
- aml_append(method, aml_return(aml_call2(
- s, aml_name("_UID"), aml_arg(0))));
- aml_append(dev, method);
-
- aml_append(sb_scope, dev);
- }
-
- /* build Method(MEMORY_SLOT_NOTIFY_METHOD, 2) {
- * If (LEqual(Arg0, 0x00)) {Notify(MP00, Arg1)} ... }
- */
- method = aml_method(stringify(MEMORY_SLOT_NOTIFY_METHOD), 2);
- for (i = 0; i < nr_mem; i++) {
- ifctx = aml_if(aml_equal(aml_arg(0), aml_int(i)));
- aml_append(ifctx,
- aml_notify(aml_name("MP%.02X", i), aml_arg(1))
- );
- aml_append(method, ifctx);
- }
- aml_append(sb_scope, method);
+ build_memory_devices(sb_scope, nr_mem, pm->mem_hp_io_base,
+ pm->mem_hp_io_len);
{
Object *pci_host;
@@ -1333,7 +2348,12 @@ build_ssdt(GArray *table_data, GArray *linker,
crs = aml_resource_template();
aml_append(crs, aml_memory32_fixed(TPM_TIS_ADDR_BASE,
TPM_TIS_ADDR_SIZE, AML_READ_WRITE));
- aml_append(crs, aml_irq_no_flags(TPM_TIS_IRQ));
+ /*
+ FIXME: TPM_TIS_IRQ=5 conflicts with PNP0C0F irqs,
+ Rewrite to take IRQ from TPM device model and
+ fix default IRQ value there to use some unused IRQ
+ */
+ /* aml_append(crs, aml_irq_no_flags(TPM_TIS_IRQ)); */
aml_append(dev, aml_name_decl("_CRS", crs));
aml_append(scope, dev);
}
@@ -1341,14 +2361,14 @@ build_ssdt(GArray *table_data, GArray *linker,
aml_append(sb_scope, scope);
}
}
- aml_append(ssdt, sb_scope);
+ aml_append(dsdt, sb_scope);
}
/* copy AML table into ACPI tables blob and patch header there */
- g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len);
+ g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len);
build_header(linker, table_data,
- (void *)(table_data->data + table_data->len - ssdt->buf->len),
- "SSDT", ssdt->buf->len, 1);
+ (void *)(table_data->data + table_data->len - dsdt->buf->len),
+ "DSDT", dsdt->buf->len, 1, NULL, NULL);
free_aml_allocator();
}
@@ -1364,7 +2384,7 @@ build_hpet(GArray *table_data, GArray *linker)
hpet->timer_block_id = cpu_to_le32(0x8086a201);
hpet->addr.address = cpu_to_le64(HPET_BASE);
build_header(linker, table_data,
- (void *)hpet, "HPET", sizeof(*hpet), 1);
+ (void *)hpet, "HPET", sizeof(*hpet), 1, NULL, NULL);
}
static void
@@ -1387,7 +2407,7 @@ build_tpm_tcpa(GArray *table_data, GArray *linker, GArray *tcpalog)
sizeof(tcpa->log_area_start_address));
build_header(linker, table_data,
- (void *)tcpa, "TCPA", sizeof(*tcpa), 2);
+ (void *)tcpa, "TCPA", sizeof(*tcpa), 2, NULL, NULL);
acpi_data_push(tcpalog, TPM_LOG_AREA_MINIMUM_SIZE);
}
@@ -1404,7 +2424,7 @@ build_tpm2(GArray *table_data, GArray *linker)
tpm2_ptr->start_method = cpu_to_le32(TPM2_START_METHOD_MMIO);
build_header(linker, table_data,
- (void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4);
+ (void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4, NULL, NULL);
}
typedef enum {
@@ -1428,7 +2448,7 @@ acpi_build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base,
}
static void
-build_srat(GArray *table_data, GArray *linker, PcGuestInfo *guest_info)
+build_srat(GArray *table_data, GArray *linker, MachineState *machine)
{
AcpiSystemResourceAffinityTable *srat;
AcpiSratProcessorAffinity *core;
@@ -1438,7 +2458,9 @@ build_srat(GArray *table_data, GArray *linker, PcGuestInfo *guest_info)
uint64_t curnode;
int srat_start, numa_start, slots;
uint64_t mem_len, mem_base, next_base;
- PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
+ CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(machine);
+ PCMachineState *pcms = PC_MACHINE(machine);
ram_addr_t hotplugabble_address_space_size =
object_property_get_int(OBJECT(pcms), PC_MACHINE_MEMHP_REGION_SIZE,
NULL);
@@ -1447,14 +2469,15 @@ build_srat(GArray *table_data, GArray *linker, PcGuestInfo *guest_info)
srat = acpi_data_push(table_data, sizeof *srat);
srat->reserved1 = cpu_to_le32(1);
- core = (void *)(srat + 1);
- for (i = 0; i < guest_info->apic_id_limit; ++i) {
+ for (i = 0; i < apic_ids->len; i++) {
+ int apic_id = apic_ids->cpus[i].arch_id;
+
core = acpi_data_push(table_data, sizeof *core);
core->type = ACPI_SRAT_PROCESSOR;
core->length = sizeof(*core);
- core->local_apic_id = i;
- curnode = guest_info->node_cpu[i];
+ core->local_apic_id = apic_id;
+ curnode = pcms->node_cpu[apic_id];
core->proximity_lo = curnode;
memset(core->proximity_hi, 0, 3);
core->local_sapic_eid = 0;
@@ -1471,33 +2494,33 @@ build_srat(GArray *table_data, GArray *linker, PcGuestInfo *guest_info)
numamem = acpi_data_push(table_data, sizeof *numamem);
acpi_build_srat_memory(numamem, 0, 640*1024, 0, MEM_AFFINITY_ENABLED);
next_base = 1024 * 1024;
- for (i = 1; i < guest_info->numa_nodes + 1; ++i) {
+ for (i = 1; i < pcms->numa_nodes + 1; ++i) {
mem_base = next_base;
- mem_len = guest_info->node_mem[i - 1];
+ mem_len = pcms->node_mem[i - 1];
if (i == 1) {
mem_len -= 1024 * 1024;
}
next_base = mem_base + mem_len;
/* Cut out the ACPI_PCI hole */
- if (mem_base <= guest_info->ram_size_below_4g &&
- next_base > guest_info->ram_size_below_4g) {
- mem_len -= next_base - guest_info->ram_size_below_4g;
+ if (mem_base <= pcms->below_4g_mem_size &&
+ next_base > pcms->below_4g_mem_size) {
+ mem_len -= next_base - pcms->below_4g_mem_size;
if (mem_len > 0) {
numamem = acpi_data_push(table_data, sizeof *numamem);
acpi_build_srat_memory(numamem, mem_base, mem_len, i - 1,
MEM_AFFINITY_ENABLED);
}
mem_base = 1ULL << 32;
- mem_len = next_base - guest_info->ram_size_below_4g;
- next_base += (1ULL << 32) - guest_info->ram_size_below_4g;
+ mem_len = next_base - pcms->below_4g_mem_size;
+ next_base += (1ULL << 32) - pcms->below_4g_mem_size;
}
numamem = acpi_data_push(table_data, sizeof *numamem);
acpi_build_srat_memory(numamem, mem_base, mem_len, i - 1,
MEM_AFFINITY_ENABLED);
}
slots = (table_data->len - numa_start) / sizeof *numamem;
- for (; slots < guest_info->numa_nodes + 2; slots++) {
+ for (; slots < pcms->numa_nodes + 2; slots++) {
numamem = acpi_data_push(table_data, sizeof *numamem);
acpi_build_srat_memory(numamem, 0, 0, 0, MEM_AFFINITY_NOFLAGS);
}
@@ -1518,7 +2541,8 @@ build_srat(GArray *table_data, GArray *linker, PcGuestInfo *guest_info)
build_header(linker, table_data,
(void *)(table_data->data + srat_start),
"SRAT",
- table_data->len - srat_start, 1);
+ table_data->len - srat_start, 1, NULL, NULL);
+ g_free(apic_ids);
}
static void
@@ -1547,7 +2571,7 @@ build_mcfg_q35(GArray *table_data, GArray *linker, AcpiMcfgInfo *info)
} else {
sig = "MCFG";
}
- build_header(linker, table_data, (void *)mcfg, sig, len, 1);
+ build_header(linker, table_data, (void *)mcfg, sig, len, 1, NULL, NULL);
}
static void
@@ -1571,22 +2595,7 @@ build_dmar_q35(GArray *table_data, GArray *linker)
drhd->address = cpu_to_le64(Q35_HOST_BRIDGE_IOMMU_ADDR);
build_header(linker, table_data, (void *)(table_data->data + dmar_start),
- "DMAR", table_data->len - dmar_start, 1);
-}
-
-static void
-build_dsdt(GArray *table_data, GArray *linker, AcpiMiscInfo *misc)
-{
- AcpiTableHeader *dsdt;
-
- assert(misc->dsdt_code && misc->dsdt_size);
-
- dsdt = acpi_data_push(table_data, misc->dsdt_size);
- memcpy(dsdt, misc->dsdt_code, misc->dsdt_size);
-
- memset(dsdt, 0, sizeof *dsdt);
- build_header(linker, table_data, dsdt, "DSDT",
- misc->dsdt_size, 1);
+ "DMAR", table_data->len - dmar_start, 1, NULL, NULL);
}
static GArray *
@@ -1608,7 +2617,8 @@ build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt)
rsdp->checksum = 0;
/* Checksum to be filled by Guest linker */
bios_linker_loader_add_checksum(linker, ACPI_BUILD_RSDP_FILE,
- rsdp, rsdp, sizeof *rsdp, &rsdp->checksum);
+ rsdp_table, rsdp, sizeof *rsdp,
+ &rsdp->checksum);
return rsdp_table;
}
@@ -1619,7 +2629,6 @@ struct AcpiBuildState {
MemoryRegion *table_mr;
/* Is table patched? */
uint8_t patched;
- PcGuestInfo *guest_info;
void *rsdp;
MemoryRegion *rsdp_mr;
MemoryRegion *linker_mr;
@@ -1658,11 +2667,12 @@ static bool acpi_has_iommu(void)
}
static
-void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables)
+void acpi_build(AcpiBuildTables *tables, MachineState *machine)
{
+ PCMachineState *pcms = PC_MACHINE(machine);
+ PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
GArray *table_offsets;
- unsigned facs, ssdt, dsdt, rsdt;
- AcpiCpuInfo cpu;
+ unsigned facs, dsdt, rsdt, fadt;
AcpiPmInfo pm;
AcpiMiscInfo misc;
AcpiMcfgInfo mcfg;
@@ -1670,12 +2680,12 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables)
uint8_t *u;
size_t aml_len = 0;
GArray *tables_blob = tables->table_data;
+ AcpiSlicOem slic_oem = { .id = NULL, .table_id = NULL };
- acpi_get_cpu_info(&cpu);
acpi_get_pm_info(&pm);
- acpi_get_dsdt(&misc);
acpi_get_misc_info(&misc);
acpi_get_pci_info(&pci);
+ acpi_get_slic_oem(&slic_oem);
table_offsets = g_array_new(false, true /* clear */,
sizeof(uint32_t));
@@ -1691,11 +2701,11 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables)
* requirements.
*/
facs = tables_blob->len;
- build_facs(tables_blob, tables->linker, guest_info);
+ build_facs(tables_blob, tables->linker);
/* DSDT is pointed to by FADT */
dsdt = tables_blob->len;
- build_dsdt(tables_blob, tables->linker, &misc);
+ build_dsdt(tables_blob, tables->linker, &pm, &misc, &pci, machine);
/* Count the size of the DSDT and SSDT, we will need it for legacy
* sizing of ACPI tables.
@@ -1703,17 +2713,14 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables)
aml_len += tables_blob->len - dsdt;
/* ACPI tables pointed to by RSDT */
+ fadt = tables_blob->len;
acpi_add_table(table_offsets, tables_blob);
- build_fadt(tables_blob, tables->linker, &pm, facs, dsdt);
-
- ssdt = tables_blob->len;
- acpi_add_table(table_offsets, tables_blob);
- build_ssdt(tables_blob, tables->linker, &cpu, &pm, &misc, &pci,
- guest_info);
- aml_len += tables_blob->len - ssdt;
+ build_fadt(tables_blob, tables->linker, &pm, facs, dsdt,
+ slic_oem.id, slic_oem.table_id);
+ aml_len += tables_blob->len - fadt;
acpi_add_table(table_offsets, tables_blob);
- build_madt(tables_blob, tables->linker, &cpu, guest_info);
+ build_madt(tables_blob, tables->linker, pcms);
if (misc.has_hpet) {
acpi_add_table(table_offsets, tables_blob);
@@ -1728,9 +2735,9 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables)
build_tpm2(tables_blob, tables->linker);
}
}
- if (guest_info->numa_nodes) {
+ if (pcms->numa_nodes) {
acpi_add_table(table_offsets, tables_blob);
- build_srat(tables_blob, tables->linker, guest_info);
+ build_srat(tables_blob, tables->linker, machine);
}
if (acpi_get_mcfg(&mcfg)) {
acpi_add_table(table_offsets, tables_blob);
@@ -1740,6 +2747,9 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables)
acpi_add_table(table_offsets, tables_blob);
build_dmar_q35(tables_blob, tables->linker);
}
+ if (pcms->acpi_nvdimm_state.is_enabled) {
+ nvdimm_build_acpi(table_offsets, tables_blob, tables->linker);
+ }
/* Add tables supplied by user (if any) */
for (u = acpi_table_first(); u; u = acpi_table_next(u)) {
@@ -1751,7 +2761,8 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables)
/* RSDT is pointed to by RSDP */
rsdt = tables_blob->len;
- build_rsdt(tables_blob, tables->linker, table_offsets);
+ build_rsdt(tables_blob, tables->linker, table_offsets,
+ slic_oem.id, slic_oem.table_id);
/* RSDP is in FSEG memory, so allocate it separately */
build_rsdp(tables->rsdp, tables->linker, rsdt);
@@ -1773,12 +2784,12 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables)
*
* All this is for PIIX4, since QEMU 2.0 didn't support Q35 migration.
*/
- if (guest_info->legacy_acpi_table_size) {
+ if (pcmc->legacy_acpi_table_size) {
/* Subtracting aml_len gives the size of fixed tables. Then add the
* size of the PIIX4 DSDT/SSDT in QEMU 2.0.
*/
int legacy_aml_len =
- guest_info->legacy_acpi_table_size +
+ pcmc->legacy_acpi_table_size +
ACPI_BUILD_LEGACY_CPU_AML_SIZE * max_cpus;
int legacy_table_size =
ROUND_UP(tables_blob->len - aml_len + legacy_aml_len,
@@ -1817,7 +2828,7 @@ static void acpi_ram_update(MemoryRegion *mr, GArray *data)
memory_region_set_dirty(mr, 0, size);
}
-static void acpi_build_update(void *build_opaque, uint32_t offset)
+static void acpi_build_update(void *build_opaque)
{
AcpiBuildState *build_state = build_opaque;
AcpiBuildTables tables;
@@ -1830,7 +2841,7 @@ static void acpi_build_update(void *build_opaque, uint32_t offset)
acpi_build_tables_init(&tables);
- acpi_build(build_state->guest_info, &tables);
+ acpi_build(&tables, MACHINE(qdev_get_machine()));
acpi_ram_update(build_state->table_mr, tables.table_data);
@@ -1868,17 +2879,19 @@ static const VMStateDescription vmstate_acpi_build = {
},
};
-void acpi_setup(PcGuestInfo *guest_info)
+void acpi_setup(void)
{
+ PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
+ PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
AcpiBuildTables tables;
AcpiBuildState *build_state;
- if (!guest_info->fw_cfg) {
+ if (!pcms->fw_cfg) {
ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n");
return;
}
- if (!guest_info->has_acpi_build) {
+ if (!pcmc->has_acpi_build) {
ACPI_BUILD_DPRINTF("ACPI build disabled. Bailing out.\n");
return;
}
@@ -1890,12 +2903,10 @@ void acpi_setup(PcGuestInfo *guest_info)
build_state = g_malloc0(sizeof *build_state);
- build_state->guest_info = guest_info;
-
acpi_set_pci_info();
acpi_build_tables_init(&tables);
- acpi_build(build_state->guest_info, &tables);
+ acpi_build(&tables, MACHINE(pcms));
/* Now expose it all to Guest */
build_state->table_mr = acpi_add_rom_blob(build_state, tables.table_data,
@@ -1906,10 +2917,10 @@ void acpi_setup(PcGuestInfo *guest_info)
build_state->linker_mr =
acpi_add_rom_blob(build_state, tables.linker, "etc/table-loader", 0);
- fw_cfg_add_file(guest_info->fw_cfg, ACPI_BUILD_TPMLOG_FILE,
+ fw_cfg_add_file(pcms->fw_cfg, ACPI_BUILD_TPMLOG_FILE,
tables.tcpalog->data, acpi_data_len(tables.tcpalog));
- if (!guest_info->rsdp_in_ram) {
+ if (!pcmc->rsdp_in_ram) {
/*
* Keep for compatibility with old machine types.
* Though RSDP is small, its contents isn't immutable, so
@@ -1918,7 +2929,7 @@ void acpi_setup(PcGuestInfo *guest_info)
uint32_t rsdp_size = acpi_data_len(tables.rsdp);
build_state->rsdp = g_memdup(tables.rsdp->data, rsdp_size);
- fw_cfg_add_file_callback(guest_info->fw_cfg, ACPI_BUILD_RSDP_FILE,
+ fw_cfg_add_file_callback(pcms->fw_cfg, ACPI_BUILD_RSDP_FILE,
acpi_build_update, build_state,
build_state->rsdp, rsdp_size);
build_state->rsdp_mr = NULL;
diff --git a/qemu/hw/i386/acpi-build.h b/qemu/hw/i386/acpi-build.h
index e57b1aafd..007332e51 100644
--- a/qemu/hw/i386/acpi-build.h
+++ b/qemu/hw/i386/acpi-build.h
@@ -2,8 +2,6 @@
#ifndef HW_I386_ACPI_BUILD_H
#define HW_I386_ACPI_BUILD_H
-#include "qemu/typedefs.h"
-
-void acpi_setup(PcGuestInfo *);
+void acpi_setup(void);
#endif
diff --git a/qemu/hw/i386/acpi-dsdt-cpu-hotplug.dsl b/qemu/hw/i386/acpi-dsdt-cpu-hotplug.dsl
deleted file mode 100644
index 1aff74627..000000000
--- a/qemu/hw/i386/acpi-dsdt-cpu-hotplug.dsl
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-/****************************************************************
- * CPU hotplug
- ****************************************************************/
-
-Scope(\_SB) {
- /* Objects filled in by run-time generated SSDT */
- External(NTFY, MethodObj)
- External(CPON, PkgObj)
- External(PRS, FieldUnitObj)
-
- /* Methods called by run-time generated SSDT Processor objects */
- Method(CPMA, 1, NotSerialized) {
- // _MAT method - create an madt apic buffer
- // Arg0 = Processor ID = Local APIC ID
- // Local0 = CPON flag for this cpu
- Store(DerefOf(Index(CPON, Arg0)), Local0)
- // Local1 = Buffer (in madt apic form) to return
- Store(Buffer(8) {0x00, 0x08, 0x00, 0x00, 0x00, 0, 0, 0}, Local1)
- // Update the processor id, lapic id, and enable/disable status
- Store(Arg0, Index(Local1, 2))
- Store(Arg0, Index(Local1, 3))
- Store(Local0, Index(Local1, 4))
- Return (Local1)
- }
- Method(CPST, 1, NotSerialized) {
- // _STA method - return ON status of cpu
- // Arg0 = Processor ID = Local APIC ID
- // Local0 = CPON flag for this cpu
- Store(DerefOf(Index(CPON, Arg0)), Local0)
- If (Local0) {
- Return (0xF)
- } Else {
- Return (0x0)
- }
- }
- Method(CPEJ, 2, NotSerialized) {
- // _EJ0 method - eject callback
- Sleep(200)
- }
-
-#define CPU_STATUS_LEN ACPI_GPE_PROC_LEN
- Method(PRSC, 0) {
- // Local5 = active cpu bitmap
- Store(PRS, Local5)
- // Local2 = last read byte from bitmap
- Store(Zero, Local2)
- // Local0 = Processor ID / APIC ID iterator
- Store(Zero, Local0)
- While (LLess(Local0, SizeOf(CPON))) {
- // Local1 = CPON flag for this cpu
- Store(DerefOf(Index(CPON, Local0)), Local1)
- If (And(Local0, 0x07)) {
- // Shift down previously read bitmap byte
- ShiftRight(Local2, 1, Local2)
- } Else {
- // Read next byte from cpu bitmap
- Store(DerefOf(Index(Local5, ShiftRight(Local0, 3))), Local2)
- }
- // Local3 = active state for this cpu
- Store(And(Local2, 1), Local3)
-
- If (LNotEqual(Local1, Local3)) {
- // State change - update CPON with new state
- Store(Local3, Index(CPON, Local0))
- // Do CPU notify
- If (LEqual(Local3, 1)) {
- NTFY(Local0, 1)
- } Else {
- NTFY(Local0, 3)
- }
- }
- Increment(Local0)
- }
- }
-}
diff --git a/qemu/hw/i386/acpi-dsdt-dbug.dsl b/qemu/hw/i386/acpi-dsdt-dbug.dsl
deleted file mode 100644
index 86230f75a..000000000
--- a/qemu/hw/i386/acpi-dsdt-dbug.dsl
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-/****************************************************************
- * Debugging
- ****************************************************************/
-
-Scope(\) {
- /* Debug Output */
- OperationRegion(DBG, SystemIO, 0x0402, 0x01)
- Field(DBG, ByteAcc, NoLock, Preserve) {
- DBGB, 8,
- }
-
- /* Debug method - use this method to send output to the QEMU
- * BIOS debug port. This method handles strings, integers,
- * and buffers. For example: DBUG("abc") DBUG(0x123) */
- Method(DBUG, 1) {
- ToHexString(Arg0, Local0)
- ToBuffer(Local0, Local0)
- Subtract(SizeOf(Local0), 1, Local1)
- Store(Zero, Local2)
- While (LLess(Local2, Local1)) {
- Store(DerefOf(Index(Local0, Local2)), DBGB)
- Increment(Local2)
- }
- Store(0x0A, DBGB)
- }
-}
diff --git a/qemu/hw/i386/acpi-dsdt-hpet.dsl b/qemu/hw/i386/acpi-dsdt-hpet.dsl
deleted file mode 100644
index 44961b87a..000000000
--- a/qemu/hw/i386/acpi-dsdt-hpet.dsl
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-/****************************************************************
- * HPET
- ****************************************************************/
-
-Scope(\_SB) {
- Device(HPET) {
- Name(_HID, EISAID("PNP0103"))
- Name(_UID, 0)
- OperationRegion(HPTM, SystemMemory, 0xFED00000, 0x400)
- Field(HPTM, DWordAcc, Lock, Preserve) {
- VEND, 32,
- PRD, 32,
- }
- Method(_STA, 0, NotSerialized) {
- Store(VEND, Local0)
- Store(PRD, Local1)
- ShiftRight(Local0, 16, Local0)
- If (LOr(LEqual(Local0, 0), LEqual(Local0, 0xffff))) {
- Return (0x0)
- }
- If (LOr(LEqual(Local1, 0), LGreater(Local1, 100000000))) {
- Return (0x0)
- }
- Return (0x0F)
- }
- Name(_CRS, ResourceTemplate() {
- Memory32Fixed(ReadOnly,
- 0xFED00000, // Address Base
- 0x00000400, // Address Length
- )
- })
- }
-}
diff --git a/qemu/hw/i386/acpi-dsdt-isa.dsl b/qemu/hw/i386/acpi-dsdt-isa.dsl
deleted file mode 100644
index 89caa1649..000000000
--- a/qemu/hw/i386/acpi-dsdt-isa.dsl
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-/* Common legacy ISA style devices. */
-Scope(\_SB.PCI0.ISA) {
-
- Device(RTC) {
- Name(_HID, EisaId("PNP0B00"))
- Name(_CRS, ResourceTemplate() {
- IO(Decode16, 0x0070, 0x0070, 0x10, 0x02)
- IRQNoFlags() { 8 }
- IO(Decode16, 0x0072, 0x0072, 0x02, 0x06)
- })
- }
-
- Device(KBD) {
- Name(_HID, EisaId("PNP0303"))
- Method(_STA, 0, NotSerialized) {
- Return (0x0f)
- }
- Name(_CRS, ResourceTemplate() {
- IO(Decode16, 0x0060, 0x0060, 0x01, 0x01)
- IO(Decode16, 0x0064, 0x0064, 0x01, 0x01)
- IRQNoFlags() { 1 }
- })
- }
-
- Device(MOU) {
- Name(_HID, EisaId("PNP0F13"))
- Method(_STA, 0, NotSerialized) {
- Return (0x0f)
- }
- Name(_CRS, ResourceTemplate() {
- IRQNoFlags() { 12 }
- })
- }
-
- Device(FDC0) {
- Name(_HID, EisaId("PNP0700"))
- Method(_STA, 0, NotSerialized) {
- Store(FDEN, Local0)
- If (LEqual(Local0, 0)) {
- Return (0x00)
- } Else {
- Return (0x0F)
- }
- }
- Name(_CRS, ResourceTemplate() {
- IO(Decode16, 0x03F2, 0x03F2, 0x00, 0x04)
- IO(Decode16, 0x03F7, 0x03F7, 0x00, 0x01)
- IRQNoFlags() { 6 }
- DMA(Compatibility, NotBusMaster, Transfer8) { 2 }
- })
- }
-
- Device(LPT) {
- Name(_HID, EisaId("PNP0400"))
- Method(_STA, 0, NotSerialized) {
- Store(LPEN, Local0)
- If (LEqual(Local0, 0)) {
- Return (0x00)
- } Else {
- Return (0x0F)
- }
- }
- Name(_CRS, ResourceTemplate() {
- IO(Decode16, 0x0378, 0x0378, 0x08, 0x08)
- IRQNoFlags() { 7 }
- })
- }
-
- Device(COM1) {
- Name(_HID, EisaId("PNP0501"))
- Name(_UID, 0x01)
- Method(_STA, 0, NotSerialized) {
- Store(CAEN, Local0)
- If (LEqual(Local0, 0)) {
- Return (0x00)
- } Else {
- Return (0x0F)
- }
- }
- Name(_CRS, ResourceTemplate() {
- IO(Decode16, 0x03F8, 0x03F8, 0x00, 0x08)
- IRQNoFlags() { 4 }
- })
- }
-
- Device(COM2) {
- Name(_HID, EisaId("PNP0501"))
- Name(_UID, 0x02)
- Method(_STA, 0, NotSerialized) {
- Store(CBEN, Local0)
- If (LEqual(Local0, 0)) {
- Return (0x00)
- } Else {
- Return (0x0F)
- }
- }
- Name(_CRS, ResourceTemplate() {
- IO(Decode16, 0x02F8, 0x02F8, 0x00, 0x08)
- IRQNoFlags() { 3 }
- })
- }
-}
diff --git a/qemu/hw/i386/acpi-dsdt-mem-hotplug.dsl b/qemu/hw/i386/acpi-dsdt-mem-hotplug.dsl
deleted file mode 100644
index c2bb6a160..000000000
--- a/qemu/hw/i386/acpi-dsdt-mem-hotplug.dsl
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * 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 <http://www.gnu.org/licenses/>.
- */
-
- External(MEMORY_SLOT_NOTIFY_METHOD, MethodObj)
-
- Scope(\_SB.PCI0) {
- Device(MEMORY_HOTPLUG_DEVICE) {
- Name(_HID, "PNP0A06")
- Name(_UID, "Memory hotplug resources")
- External(MEMORY_SLOTS_NUMBER, IntObj)
-
- /* Memory hotplug IO registers */
- External(MEMORY_SLOT_ADDR_LOW, FieldUnitObj) // read only
- External(MEMORY_SLOT_ADDR_HIGH, FieldUnitObj) // read only
- External(MEMORY_SLOT_SIZE_LOW, FieldUnitObj) // read only
- External(MEMORY_SLOT_SIZE_HIGH, FieldUnitObj) // read only
- External(MEMORY_SLOT_PROXIMITY, FieldUnitObj) // read only
- External(MEMORY_SLOT_ENABLED, FieldUnitObj) // 1 if enabled, read only
- External(MEMORY_SLOT_INSERT_EVENT, FieldUnitObj) // (read) 1 if has a insert event. (write) 1 to clear event
- External(MEMORY_SLOT_REMOVE_EVENT, FieldUnitObj) // (read) 1 if has a remove event. (write) 1 to clear event
- External(MEMORY_SLOT_EJECT, FieldUnitObj) // initiates device eject, write only
- External(MEMORY_SLOT_SLECTOR, FieldUnitObj) // DIMM selector, write only
- External(MEMORY_SLOT_OST_EVENT, FieldUnitObj) // _OST event code, write only
- External(MEMORY_SLOT_OST_STATUS, FieldUnitObj) // _OST status code, write only
-
- Method(_STA, 0) {
- If (LEqual(MEMORY_SLOTS_NUMBER, Zero)) {
- Return(0x0)
- }
- /* present, functioning, decoding, not shown in UI */
- Return(0xB)
- }
-
- Mutex (MEMORY_SLOT_LOCK, 0)
-
- Method(MEMORY_SLOT_SCAN_METHOD, 0) {
- If (LEqual(MEMORY_SLOTS_NUMBER, Zero)) {
- Return(Zero)
- }
-
- Store(Zero, Local0) // Mem devs iterrator
- Acquire(MEMORY_SLOT_LOCK, 0xFFFF)
- while (LLess(Local0, MEMORY_SLOTS_NUMBER)) {
- Store(Local0, MEMORY_SLOT_SLECTOR) // select Local0 DIMM
- If (LEqual(MEMORY_SLOT_INSERT_EVENT, One)) { // Memory device needs check
- MEMORY_SLOT_NOTIFY_METHOD(Local0, 1)
- Store(1, MEMORY_SLOT_INSERT_EVENT)
- } Elseif (LEqual(MEMORY_SLOT_REMOVE_EVENT, One)) { // Ejection request
- MEMORY_SLOT_NOTIFY_METHOD(Local0, 3)
- Store(1, MEMORY_SLOT_REMOVE_EVENT)
- }
- Add(Local0, One, Local0) // goto next DIMM
- }
- Release(MEMORY_SLOT_LOCK)
- Return(One)
- }
-
- Method(MEMORY_SLOT_STATUS_METHOD, 1) {
- Store(Zero, Local0)
-
- Acquire(MEMORY_SLOT_LOCK, 0xFFFF)
- Store(ToInteger(Arg0), MEMORY_SLOT_SLECTOR) // select DIMM
-
- If (LEqual(MEMORY_SLOT_ENABLED, One)) {
- Store(0xF, Local0)
- }
-
- Release(MEMORY_SLOT_LOCK)
- Return(Local0)
- }
-
- Method(MEMORY_SLOT_CRS_METHOD, 1, Serialized) {
- Acquire(MEMORY_SLOT_LOCK, 0xFFFF)
- Store(ToInteger(Arg0), MEMORY_SLOT_SLECTOR) // select DIMM
-
- Name(MR64, ResourceTemplate() {
- QWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed,
- Cacheable, ReadWrite,
- 0x0000000000000000, // Address Space Granularity
- 0x0000000000000000, // Address Range Minimum
- 0xFFFFFFFFFFFFFFFE, // Address Range Maximum
- 0x0000000000000000, // Address Translation Offset
- 0xFFFFFFFFFFFFFFFF, // Address Length
- ,, MW64, AddressRangeMemory, TypeStatic)
- })
-
- CreateDWordField(MR64, 14, MINL)
- CreateDWordField(MR64, 18, MINH)
- CreateDWordField(MR64, 38, LENL)
- CreateDWordField(MR64, 42, LENH)
- CreateDWordField(MR64, 22, MAXL)
- CreateDWordField(MR64, 26, MAXH)
-
- Store(MEMORY_SLOT_ADDR_HIGH, MINH)
- Store(MEMORY_SLOT_ADDR_LOW, MINL)
- Store(MEMORY_SLOT_SIZE_HIGH, LENH)
- Store(MEMORY_SLOT_SIZE_LOW, LENL)
-
- // 64-bit math: MAX = MIN + LEN - 1
- Add(MINL, LENL, MAXL)
- Add(MINH, LENH, MAXH)
- If (LLess(MAXL, MINL)) {
- Add(MAXH, One, MAXH)
- }
- If (LLess(MAXL, One)) {
- Subtract(MAXH, One, MAXH)
- }
- Subtract(MAXL, One, MAXL)
-
- If (LEqual(MAXH, Zero)){
- Name(MR32, ResourceTemplate() {
- DWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed,
- Cacheable, ReadWrite,
- 0x00000000, // Address Space Granularity
- 0x00000000, // Address Range Minimum
- 0xFFFFFFFE, // Address Range Maximum
- 0x00000000, // Address Translation Offset
- 0xFFFFFFFF, // Address Length
- ,, MW32, AddressRangeMemory, TypeStatic)
- })
- CreateDWordField(MR32, MW32._MIN, MIN)
- CreateDWordField(MR32, MW32._MAX, MAX)
- CreateDWordField(MR32, MW32._LEN, LEN)
- Store(MINL, MIN)
- Store(MAXL, MAX)
- Store(LENL, LEN)
-
- Release(MEMORY_SLOT_LOCK)
- Return(MR32)
- }
-
- Release(MEMORY_SLOT_LOCK)
- Return(MR64)
- }
-
- Method(MEMORY_SLOT_PROXIMITY_METHOD, 1) {
- Acquire(MEMORY_SLOT_LOCK, 0xFFFF)
- Store(ToInteger(Arg0), MEMORY_SLOT_SLECTOR) // select DIMM
- Store(MEMORY_SLOT_PROXIMITY, Local0)
- Release(MEMORY_SLOT_LOCK)
- Return(Local0)
- }
-
- Method(MEMORY_SLOT_OST_METHOD, 4) {
- Acquire(MEMORY_SLOT_LOCK, 0xFFFF)
- Store(ToInteger(Arg0), MEMORY_SLOT_SLECTOR) // select DIMM
- Store(Arg1, MEMORY_SLOT_OST_EVENT)
- Store(Arg2, MEMORY_SLOT_OST_STATUS)
- Release(MEMORY_SLOT_LOCK)
- }
-
- Method(MEMORY_SLOT_EJECT_METHOD, 2) {
- Acquire(MEMORY_SLOT_LOCK, 0xFFFF)
- Store(ToInteger(Arg0), MEMORY_SLOT_SLECTOR) // select DIMM
- Store(1, MEMORY_SLOT_EJECT)
- Release(MEMORY_SLOT_LOCK)
- }
- } // Device()
- } // Scope()
diff --git a/qemu/hw/i386/acpi-dsdt.dsl b/qemu/hw/i386/acpi-dsdt.dsl
deleted file mode 100644
index a2d84ecf8..000000000
--- a/qemu/hw/i386/acpi-dsdt.dsl
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * Bochs/QEMU ACPI DSDT ASL definition
- *
- * Copyright (c) 2006 Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * 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, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-ACPI_EXTRACT_ALL_CODE AcpiDsdtAmlCode
-
-DefinitionBlock (
- "acpi-dsdt.aml", // Output Filename
- "DSDT", // Signature
- 0x01, // DSDT Compliance Revision
- "BXPC", // OEMID
- "BXDSDT", // TABLE ID
- 0x1 // OEM Revision
- )
-{
-
-#include "acpi-dsdt-dbug.dsl"
-
- Scope(\_SB) {
- Device(PCI0) {
- Name(_HID, EisaId("PNP0A03"))
- Name(_ADR, 0x00)
- Name(_UID, 1)
-//#define PX13 S0B_
-// External(PX13, DeviceObj)
- }
- }
-
-#include "acpi-dsdt-hpet.dsl"
-
-
-/****************************************************************
- * PIIX4 PM
- ****************************************************************/
-
- Scope(\_SB.PCI0) {
- Device(PX13) {
- Name(_ADR, 0x00010003)
- OperationRegion(P13C, PCI_Config, 0x00, 0xff)
- }
- }
-
-
-/****************************************************************
- * PIIX3 ISA bridge
- ****************************************************************/
-
- Scope(\_SB.PCI0) {
-
- External(ISA, DeviceObj)
-
- Device(ISA) {
- Name(_ADR, 0x00010000)
-
- /* PIIX PCI to ISA irq remapping */
- OperationRegion(P40C, PCI_Config, 0x60, 0x04)
-
- /* enable bits */
- Field(\_SB.PCI0.PX13.P13C, AnyAcc, NoLock, Preserve) {
- Offset(0x5f),
- , 7,
- LPEN, 1, // LPT
- Offset(0x67),
- , 3,
- CAEN, 1, // COM1
- , 3,
- CBEN, 1, // COM2
- }
- Name(FDEN, 1)
- }
- }
-
-#include "acpi-dsdt-isa.dsl"
-
-
-/****************************************************************
- * PCI hotplug
- ****************************************************************/
-
- Scope(\_SB.PCI0) {
- OperationRegion(PCST, SystemIO, 0xae00, 0x08)
- Field(PCST, DWordAcc, NoLock, WriteAsZeros) {
- PCIU, 32,
- PCID, 32,
- }
-
- OperationRegion(SEJ, SystemIO, 0xae08, 0x04)
- Field(SEJ, DWordAcc, NoLock, WriteAsZeros) {
- B0EJ, 32,
- }
-
- OperationRegion(BNMR, SystemIO, 0xae10, 0x04)
- Field(BNMR, DWordAcc, NoLock, WriteAsZeros) {
- BNUM, 32,
- }
-
- /* Lock to protect access to fields above. */
- Mutex(BLCK, 0)
-
- /* Methods called by bulk generated PCI devices below */
-
- /* Methods called by hotplug devices */
- Method(PCEJ, 2, NotSerialized) {
- // _EJ0 method - eject callback
- Acquire(BLCK, 0xFFFF)
- Store(Arg0, BNUM)
- Store(ShiftLeft(1, Arg1), B0EJ)
- Release(BLCK)
- Return (0x0)
- }
-
- /* Hotplug notification method supplied by SSDT */
- External(\_SB.PCI0.PCNT, MethodObj)
- }
-
-
-/****************************************************************
- * PCI IRQs
- ****************************************************************/
-
- Scope(\_SB) {
- Scope(PCI0) {
- Method (_PRT, 0) {
- Store(Package(128) {}, Local0)
- Store(Zero, Local1)
- While(LLess(Local1, 128)) {
- // slot = pin >> 2
- Store(ShiftRight(Local1, 2), Local2)
-
- // lnk = (slot + pin) & 3
- Store(And(Add(Local1, Local2), 3), Local3)
- If (LEqual(Local3, 0)) {
- Store(Package(4) { Zero, Zero, LNKD, Zero }, Local4)
- }
- If (LEqual(Local3, 1)) {
- // device 1 is the power-management device, needs SCI
- If (LEqual(Local1, 4)) {
- Store(Package(4) { Zero, Zero, LNKS, Zero }, Local4)
- } Else {
- Store(Package(4) { Zero, Zero, LNKA, Zero }, Local4)
- }
- }
- If (LEqual(Local3, 2)) {
- Store(Package(4) { Zero, Zero, LNKB, Zero }, Local4)
- }
- If (LEqual(Local3, 3)) {
- Store(Package(4) { Zero, Zero, LNKC, Zero }, Local4)
- }
-
- // Complete the interrupt routing entry:
- // Package(4) { 0x[slot]FFFF, [pin], [link], 0) }
-
- Store(Or(ShiftLeft(Local2, 16), 0xFFFF), Index(Local4, 0))
- Store(And(Local1, 3), Index(Local4, 1))
- Store(Local4, Index(Local0, Local1))
-
- Increment(Local1)
- }
-
- Return(Local0)
- }
- }
-
- Field(PCI0.ISA.P40C, ByteAcc, NoLock, Preserve) {
- PRQ0, 8,
- PRQ1, 8,
- PRQ2, 8,
- PRQ3, 8
- }
-
- Method(IQST, 1, NotSerialized) {
- // _STA method - get status
- If (And(0x80, Arg0)) {
- Return (0x09)
- }
- Return (0x0B)
- }
- Method(IQCR, 1, Serialized) {
- // _CRS method - get current settings
- Name(PRR0, ResourceTemplate() {
- Interrupt(, Level, ActiveHigh, Shared) { 0 }
- })
- CreateDWordField(PRR0, 0x05, PRRI)
- If (LLess(Arg0, 0x80)) {
- Store(Arg0, PRRI)
- }
- Return (PRR0)
- }
-
-#define define_link(link, uid, reg) \
- Device(link) { \
- Name(_HID, EISAID("PNP0C0F")) \
- Name(_UID, uid) \
- Name(_PRS, ResourceTemplate() { \
- Interrupt(, Level, ActiveHigh, Shared) { \
- 5, 10, 11 \
- } \
- }) \
- Method(_STA, 0, NotSerialized) { \
- Return (IQST(reg)) \
- } \
- Method(_DIS, 0, NotSerialized) { \
- Or(reg, 0x80, reg) \
- } \
- Method(_CRS, 0, NotSerialized) { \
- Return (IQCR(reg)) \
- } \
- Method(_SRS, 1, NotSerialized) { \
- CreateDWordField(Arg0, 0x05, PRRI) \
- Store(PRRI, reg) \
- } \
- }
-
- define_link(LNKA, 0, PRQ0)
- define_link(LNKB, 1, PRQ1)
- define_link(LNKC, 2, PRQ2)
- define_link(LNKD, 3, PRQ3)
-
- Device(LNKS) {
- Name(_HID, EISAID("PNP0C0F"))
- Name(_UID, 4)
- Name(_PRS, ResourceTemplate() {
- Interrupt(, Level, ActiveHigh, Shared) { 9 }
- })
-
- // The SCI cannot be disabled and is always attached to GSI 9,
- // so these are no-ops. We only need this link to override the
- // polarity to active high and match the content of the MADT.
- Method(_STA, 0, NotSerialized) { Return (0x0b) }
- Method(_DIS, 0, NotSerialized) { }
- Method(_CRS, 0, NotSerialized) { Return (_PRS) }
- Method(_SRS, 1, NotSerialized) { }
- }
- }
-
-#include "hw/acpi/pc-hotplug.h"
-#define CPU_STATUS_BASE PIIX4_CPU_HOTPLUG_IO_BASE
-#include "acpi-dsdt-cpu-hotplug.dsl"
-#include "acpi-dsdt-mem-hotplug.dsl"
-
-
-/****************************************************************
- * General purpose events
- ****************************************************************/
- Scope(\_GPE) {
- Name(_HID, "ACPI0006")
-
- Method(_L00) {
- }
- Method(_E01) {
- // PCI hotplug event
- Acquire(\_SB.PCI0.BLCK, 0xFFFF)
- \_SB.PCI0.PCNT()
- Release(\_SB.PCI0.BLCK)
- }
- Method(_E02) {
- // CPU hotplug event
- \_SB.PRSC()
- }
- Method(_E03) {
- // Memory hotplug event
- \_SB.PCI0.MEMORY_HOTPLUG_DEVICE.MEMORY_SLOT_SCAN_METHOD()
- }
- Method(_L04) {
- }
- Method(_L05) {
- }
- Method(_L06) {
- }
- Method(_L07) {
- }
- Method(_L08) {
- }
- Method(_L09) {
- }
- Method(_L0A) {
- }
- Method(_L0B) {
- }
- Method(_L0C) {
- }
- Method(_L0D) {
- }
- Method(_L0E) {
- }
- Method(_L0F) {
- }
- }
-}
diff --git a/qemu/hw/i386/acpi-dsdt.hex.generated b/qemu/hw/i386/acpi-dsdt.hex.generated
deleted file mode 100644
index ecaa4a548..000000000
--- a/qemu/hw/i386/acpi-dsdt.hex.generated
+++ /dev/null
@@ -1,2972 +0,0 @@
-static unsigned char AcpiDsdtAmlCode[] = {
-0x44,
-0x53,
-0x44,
-0x54,
-0x9a,
-0xb,
-0x0,
-0x0,
-0x1,
-0xf8,
-0x42,
-0x58,
-0x50,
-0x43,
-0x0,
-0x0,
-0x42,
-0x58,
-0x44,
-0x53,
-0x44,
-0x54,
-0x0,
-0x0,
-0x1,
-0x0,
-0x0,
-0x0,
-0x49,
-0x4e,
-0x54,
-0x4c,
-0x7,
-0x11,
-0x14,
-0x20,
-0x10,
-0x49,
-0x4,
-0x5c,
-0x0,
-0x5b,
-0x80,
-0x44,
-0x42,
-0x47,
-0x5f,
-0x1,
-0xb,
-0x2,
-0x4,
-0x1,
-0x5b,
-0x81,
-0xb,
-0x44,
-0x42,
-0x47,
-0x5f,
-0x1,
-0x44,
-0x42,
-0x47,
-0x42,
-0x8,
-0x14,
-0x2c,
-0x44,
-0x42,
-0x55,
-0x47,
-0x1,
-0x98,
-0x68,
-0x60,
-0x96,
-0x60,
-0x60,
-0x74,
-0x87,
-0x60,
-0x1,
-0x61,
-0x70,
-0x0,
-0x62,
-0xa2,
-0x10,
-0x95,
-0x62,
-0x61,
-0x70,
-0x83,
-0x88,
-0x60,
-0x62,
-0x0,
-0x44,
-0x42,
-0x47,
-0x42,
-0x75,
-0x62,
-0x70,
-0xa,
-0xa,
-0x44,
-0x42,
-0x47,
-0x42,
-0x10,
-0x22,
-0x5f,
-0x53,
-0x42,
-0x5f,
-0x5b,
-0x82,
-0x1b,
-0x50,
-0x43,
-0x49,
-0x30,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xa,
-0x3,
-0x8,
-0x5f,
-0x41,
-0x44,
-0x52,
-0x0,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0x1,
-0x10,
-0x4d,
-0x8,
-0x5f,
-0x53,
-0x42,
-0x5f,
-0x5b,
-0x82,
-0x45,
-0x8,
-0x48,
-0x50,
-0x45,
-0x54,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0x1,
-0x3,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0x0,
-0x5b,
-0x80,
-0x48,
-0x50,
-0x54,
-0x4d,
-0x0,
-0xc,
-0x0,
-0x0,
-0xd0,
-0xfe,
-0xb,
-0x0,
-0x4,
-0x5b,
-0x81,
-0x10,
-0x48,
-0x50,
-0x54,
-0x4d,
-0x13,
-0x56,
-0x45,
-0x4e,
-0x44,
-0x20,
-0x50,
-0x52,
-0x44,
-0x5f,
-0x20,
-0x14,
-0x36,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0x70,
-0x56,
-0x45,
-0x4e,
-0x44,
-0x60,
-0x70,
-0x50,
-0x52,
-0x44,
-0x5f,
-0x61,
-0x7a,
-0x60,
-0xa,
-0x10,
-0x60,
-0xa0,
-0xc,
-0x91,
-0x93,
-0x60,
-0x0,
-0x93,
-0x60,
-0xb,
-0xff,
-0xff,
-0xa4,
-0x0,
-0xa0,
-0xe,
-0x91,
-0x93,
-0x61,
-0x0,
-0x94,
-0x61,
-0xc,
-0x0,
-0xe1,
-0xf5,
-0x5,
-0xa4,
-0x0,
-0xa4,
-0xa,
-0xf,
-0x8,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x11,
-0x11,
-0xa,
-0xe,
-0x86,
-0x9,
-0x0,
-0x0,
-0x0,
-0x0,
-0xd0,
-0xfe,
-0x0,
-0x4,
-0x0,
-0x0,
-0x79,
-0x0,
-0x10,
-0x25,
-0x2e,
-0x5f,
-0x53,
-0x42,
-0x5f,
-0x50,
-0x43,
-0x49,
-0x30,
-0x5b,
-0x82,
-0x19,
-0x50,
-0x58,
-0x31,
-0x33,
-0x8,
-0x5f,
-0x41,
-0x44,
-0x52,
-0xc,
-0x3,
-0x0,
-0x1,
-0x0,
-0x5b,
-0x80,
-0x50,
-0x31,
-0x33,
-0x43,
-0x2,
-0x0,
-0xa,
-0xff,
-0x10,
-0x46,
-0x5,
-0x2e,
-0x5f,
-0x53,
-0x42,
-0x5f,
-0x50,
-0x43,
-0x49,
-0x30,
-0x5b,
-0x82,
-0x49,
-0x4,
-0x49,
-0x53,
-0x41,
-0x5f,
-0x8,
-0x5f,
-0x41,
-0x44,
-0x52,
-0xc,
-0x0,
-0x0,
-0x1,
-0x0,
-0x5b,
-0x80,
-0x50,
-0x34,
-0x30,
-0x43,
-0x2,
-0xa,
-0x60,
-0xa,
-0x4,
-0x5b,
-0x81,
-0x26,
-0x5e,
-0x2e,
-0x50,
-0x58,
-0x31,
-0x33,
-0x50,
-0x31,
-0x33,
-0x43,
-0x0,
-0x0,
-0x48,
-0x2f,
-0x0,
-0x7,
-0x4c,
-0x50,
-0x45,
-0x4e,
-0x1,
-0x0,
-0x38,
-0x0,
-0x3,
-0x43,
-0x41,
-0x45,
-0x4e,
-0x1,
-0x0,
-0x3,
-0x43,
-0x42,
-0x45,
-0x4e,
-0x1,
-0x8,
-0x46,
-0x44,
-0x45,
-0x4e,
-0x1,
-0x10,
-0x4c,
-0x1b,
-0x2f,
-0x3,
-0x5f,
-0x53,
-0x42,
-0x5f,
-0x50,
-0x43,
-0x49,
-0x30,
-0x49,
-0x53,
-0x41,
-0x5f,
-0x5b,
-0x82,
-0x2d,
-0x52,
-0x54,
-0x43,
-0x5f,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xb,
-0x0,
-0x8,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x11,
-0x18,
-0xa,
-0x15,
-0x47,
-0x1,
-0x70,
-0x0,
-0x70,
-0x0,
-0x10,
-0x2,
-0x22,
-0x0,
-0x1,
-0x47,
-0x1,
-0x72,
-0x0,
-0x72,
-0x0,
-0x2,
-0x6,
-0x79,
-0x0,
-0x5b,
-0x82,
-0x37,
-0x4b,
-0x42,
-0x44,
-0x5f,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0x3,
-0x3,
-0x14,
-0x9,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0xa4,
-0xa,
-0xf,
-0x8,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x11,
-0x18,
-0xa,
-0x15,
-0x47,
-0x1,
-0x60,
-0x0,
-0x60,
-0x0,
-0x1,
-0x1,
-0x47,
-0x1,
-0x64,
-0x0,
-0x64,
-0x0,
-0x1,
-0x1,
-0x22,
-0x2,
-0x0,
-0x79,
-0x0,
-0x5b,
-0x82,
-0x27,
-0x4d,
-0x4f,
-0x55,
-0x5f,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xf,
-0x13,
-0x14,
-0x9,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0xa4,
-0xa,
-0xf,
-0x8,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x11,
-0x8,
-0xa,
-0x5,
-0x22,
-0x0,
-0x10,
-0x79,
-0x0,
-0x5b,
-0x82,
-0x4a,
-0x4,
-0x46,
-0x44,
-0x43,
-0x30,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0x7,
-0x0,
-0x14,
-0x18,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0x70,
-0x46,
-0x44,
-0x45,
-0x4e,
-0x60,
-0xa0,
-0x6,
-0x93,
-0x60,
-0x0,
-0xa4,
-0x0,
-0xa1,
-0x4,
-0xa4,
-0xa,
-0xf,
-0x8,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x11,
-0x1b,
-0xa,
-0x18,
-0x47,
-0x1,
-0xf2,
-0x3,
-0xf2,
-0x3,
-0x0,
-0x4,
-0x47,
-0x1,
-0xf7,
-0x3,
-0xf7,
-0x3,
-0x0,
-0x1,
-0x22,
-0x40,
-0x0,
-0x2a,
-0x4,
-0x0,
-0x79,
-0x0,
-0x5b,
-0x82,
-0x3e,
-0x4c,
-0x50,
-0x54,
-0x5f,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0x4,
-0x0,
-0x14,
-0x18,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0x70,
-0x4c,
-0x50,
-0x45,
-0x4e,
-0x60,
-0xa0,
-0x6,
-0x93,
-0x60,
-0x0,
-0xa4,
-0x0,
-0xa1,
-0x4,
-0xa4,
-0xa,
-0xf,
-0x8,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x11,
-0x10,
-0xa,
-0xd,
-0x47,
-0x1,
-0x78,
-0x3,
-0x78,
-0x3,
-0x8,
-0x8,
-0x22,
-0x80,
-0x0,
-0x79,
-0x0,
-0x5b,
-0x82,
-0x45,
-0x4,
-0x43,
-0x4f,
-0x4d,
-0x31,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0x5,
-0x1,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0x1,
-0x14,
-0x18,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0x70,
-0x43,
-0x41,
-0x45,
-0x4e,
-0x60,
-0xa0,
-0x6,
-0x93,
-0x60,
-0x0,
-0xa4,
-0x0,
-0xa1,
-0x4,
-0xa4,
-0xa,
-0xf,
-0x8,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x11,
-0x10,
-0xa,
-0xd,
-0x47,
-0x1,
-0xf8,
-0x3,
-0xf8,
-0x3,
-0x0,
-0x8,
-0x22,
-0x10,
-0x0,
-0x79,
-0x0,
-0x5b,
-0x82,
-0x46,
-0x4,
-0x43,
-0x4f,
-0x4d,
-0x32,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0x5,
-0x1,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0xa,
-0x2,
-0x14,
-0x18,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0x70,
-0x43,
-0x42,
-0x45,
-0x4e,
-0x60,
-0xa0,
-0x6,
-0x93,
-0x60,
-0x0,
-0xa4,
-0x0,
-0xa1,
-0x4,
-0xa4,
-0xa,
-0xf,
-0x8,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x11,
-0x10,
-0xa,
-0xd,
-0x47,
-0x1,
-0xf8,
-0x2,
-0xf8,
-0x2,
-0x0,
-0x8,
-0x22,
-0x8,
-0x0,
-0x79,
-0x0,
-0x10,
-0x48,
-0x8,
-0x2e,
-0x5f,
-0x53,
-0x42,
-0x5f,
-0x50,
-0x43,
-0x49,
-0x30,
-0x5b,
-0x80,
-0x50,
-0x43,
-0x53,
-0x54,
-0x1,
-0xb,
-0x0,
-0xae,
-0xa,
-0x8,
-0x5b,
-0x81,
-0x10,
-0x50,
-0x43,
-0x53,
-0x54,
-0x43,
-0x50,
-0x43,
-0x49,
-0x55,
-0x20,
-0x50,
-0x43,
-0x49,
-0x44,
-0x20,
-0x5b,
-0x80,
-0x53,
-0x45,
-0x4a,
-0x5f,
-0x1,
-0xb,
-0x8,
-0xae,
-0xa,
-0x4,
-0x5b,
-0x81,
-0xb,
-0x53,
-0x45,
-0x4a,
-0x5f,
-0x43,
-0x42,
-0x30,
-0x45,
-0x4a,
-0x20,
-0x5b,
-0x80,
-0x42,
-0x4e,
-0x4d,
-0x52,
-0x1,
-0xb,
-0x10,
-0xae,
-0xa,
-0x4,
-0x5b,
-0x81,
-0xb,
-0x42,
-0x4e,
-0x4d,
-0x52,
-0x43,
-0x42,
-0x4e,
-0x55,
-0x4d,
-0x20,
-0x5b,
-0x1,
-0x42,
-0x4c,
-0x43,
-0x4b,
-0x0,
-0x14,
-0x25,
-0x50,
-0x43,
-0x45,
-0x4a,
-0x2,
-0x5b,
-0x23,
-0x42,
-0x4c,
-0x43,
-0x4b,
-0xff,
-0xff,
-0x70,
-0x68,
-0x42,
-0x4e,
-0x55,
-0x4d,
-0x70,
-0x79,
-0x1,
-0x69,
-0x0,
-0x42,
-0x30,
-0x45,
-0x4a,
-0x5b,
-0x27,
-0x42,
-0x4c,
-0x43,
-0x4b,
-0xa4,
-0x0,
-0x10,
-0x4e,
-0x36,
-0x5f,
-0x53,
-0x42,
-0x5f,
-0x10,
-0x4b,
-0xa,
-0x50,
-0x43,
-0x49,
-0x30,
-0x14,
-0x44,
-0xa,
-0x5f,
-0x50,
-0x52,
-0x54,
-0x0,
-0x70,
-0x12,
-0x2,
-0x80,
-0x60,
-0x70,
-0x0,
-0x61,
-0xa2,
-0x42,
-0x9,
-0x95,
-0x61,
-0xa,
-0x80,
-0x70,
-0x7a,
-0x61,
-0xa,
-0x2,
-0x0,
-0x62,
-0x70,
-0x7b,
-0x72,
-0x61,
-0x62,
-0x0,
-0xa,
-0x3,
-0x0,
-0x63,
-0xa0,
-0x10,
-0x93,
-0x63,
-0x0,
-0x70,
-0x12,
-0x9,
-0x4,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x44,
-0x0,
-0x64,
-0xa0,
-0x24,
-0x93,
-0x63,
-0x1,
-0xa0,
-0x11,
-0x93,
-0x61,
-0xa,
-0x4,
-0x70,
-0x12,
-0x9,
-0x4,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x53,
-0x0,
-0x64,
-0xa1,
-0xd,
-0x70,
-0x12,
-0x9,
-0x4,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x41,
-0x0,
-0x64,
-0xa0,
-0x11,
-0x93,
-0x63,
-0xa,
-0x2,
-0x70,
-0x12,
-0x9,
-0x4,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x42,
-0x0,
-0x64,
-0xa0,
-0x11,
-0x93,
-0x63,
-0xa,
-0x3,
-0x70,
-0x12,
-0x9,
-0x4,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x43,
-0x0,
-0x64,
-0x70,
-0x7d,
-0x79,
-0x62,
-0xa,
-0x10,
-0x0,
-0xb,
-0xff,
-0xff,
-0x0,
-0x88,
-0x64,
-0x0,
-0x0,
-0x70,
-0x7b,
-0x61,
-0xa,
-0x3,
-0x0,
-0x88,
-0x64,
-0x1,
-0x0,
-0x70,
-0x64,
-0x88,
-0x60,
-0x61,
-0x0,
-0x75,
-0x61,
-0xa4,
-0x60,
-0x5b,
-0x81,
-0x24,
-0x2f,
-0x3,
-0x50,
-0x43,
-0x49,
-0x30,
-0x49,
-0x53,
-0x41,
-0x5f,
-0x50,
-0x34,
-0x30,
-0x43,
-0x1,
-0x50,
-0x52,
-0x51,
-0x30,
-0x8,
-0x50,
-0x52,
-0x51,
-0x31,
-0x8,
-0x50,
-0x52,
-0x51,
-0x32,
-0x8,
-0x50,
-0x52,
-0x51,
-0x33,
-0x8,
-0x14,
-0x13,
-0x49,
-0x51,
-0x53,
-0x54,
-0x1,
-0xa0,
-0x9,
-0x7b,
-0xa,
-0x80,
-0x68,
-0x0,
-0xa4,
-0xa,
-0x9,
-0xa4,
-0xa,
-0xb,
-0x14,
-0x36,
-0x49,
-0x51,
-0x43,
-0x52,
-0x9,
-0x8,
-0x50,
-0x52,
-0x52,
-0x30,
-0x11,
-0xe,
-0xa,
-0xb,
-0x89,
-0x6,
-0x0,
-0x9,
-0x1,
-0x0,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x8a,
-0x50,
-0x52,
-0x52,
-0x30,
-0xa,
-0x5,
-0x50,
-0x52,
-0x52,
-0x49,
-0xa0,
-0xb,
-0x95,
-0x68,
-0xa,
-0x80,
-0x70,
-0x68,
-0x50,
-0x52,
-0x52,
-0x49,
-0xa4,
-0x50,
-0x52,
-0x52,
-0x30,
-0x5b,
-0x82,
-0x4c,
-0x7,
-0x4c,
-0x4e,
-0x4b,
-0x41,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xc,
-0xf,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0x0,
-0x8,
-0x5f,
-0x50,
-0x52,
-0x53,
-0x11,
-0x16,
-0xa,
-0x13,
-0x89,
-0xe,
-0x0,
-0x9,
-0x3,
-0x5,
-0x0,
-0x0,
-0x0,
-0xa,
-0x0,
-0x0,
-0x0,
-0xb,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x14,
-0xf,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0xa4,
-0x49,
-0x51,
-0x53,
-0x54,
-0x50,
-0x52,
-0x51,
-0x30,
-0x14,
-0x11,
-0x5f,
-0x44,
-0x49,
-0x53,
-0x0,
-0x7d,
-0x50,
-0x52,
-0x51,
-0x30,
-0xa,
-0x80,
-0x50,
-0x52,
-0x51,
-0x30,
-0x14,
-0xf,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x0,
-0xa4,
-0x49,
-0x51,
-0x43,
-0x52,
-0x50,
-0x52,
-0x51,
-0x30,
-0x14,
-0x17,
-0x5f,
-0x53,
-0x52,
-0x53,
-0x1,
-0x8a,
-0x68,
-0xa,
-0x5,
-0x50,
-0x52,
-0x52,
-0x49,
-0x70,
-0x50,
-0x52,
-0x52,
-0x49,
-0x50,
-0x52,
-0x51,
-0x30,
-0x5b,
-0x82,
-0x4c,
-0x7,
-0x4c,
-0x4e,
-0x4b,
-0x42,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xc,
-0xf,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0x1,
-0x8,
-0x5f,
-0x50,
-0x52,
-0x53,
-0x11,
-0x16,
-0xa,
-0x13,
-0x89,
-0xe,
-0x0,
-0x9,
-0x3,
-0x5,
-0x0,
-0x0,
-0x0,
-0xa,
-0x0,
-0x0,
-0x0,
-0xb,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x14,
-0xf,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0xa4,
-0x49,
-0x51,
-0x53,
-0x54,
-0x50,
-0x52,
-0x51,
-0x31,
-0x14,
-0x11,
-0x5f,
-0x44,
-0x49,
-0x53,
-0x0,
-0x7d,
-0x50,
-0x52,
-0x51,
-0x31,
-0xa,
-0x80,
-0x50,
-0x52,
-0x51,
-0x31,
-0x14,
-0xf,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x0,
-0xa4,
-0x49,
-0x51,
-0x43,
-0x52,
-0x50,
-0x52,
-0x51,
-0x31,
-0x14,
-0x17,
-0x5f,
-0x53,
-0x52,
-0x53,
-0x1,
-0x8a,
-0x68,
-0xa,
-0x5,
-0x50,
-0x52,
-0x52,
-0x49,
-0x70,
-0x50,
-0x52,
-0x52,
-0x49,
-0x50,
-0x52,
-0x51,
-0x31,
-0x5b,
-0x82,
-0x4d,
-0x7,
-0x4c,
-0x4e,
-0x4b,
-0x43,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xc,
-0xf,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0xa,
-0x2,
-0x8,
-0x5f,
-0x50,
-0x52,
-0x53,
-0x11,
-0x16,
-0xa,
-0x13,
-0x89,
-0xe,
-0x0,
-0x9,
-0x3,
-0x5,
-0x0,
-0x0,
-0x0,
-0xa,
-0x0,
-0x0,
-0x0,
-0xb,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x14,
-0xf,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0xa4,
-0x49,
-0x51,
-0x53,
-0x54,
-0x50,
-0x52,
-0x51,
-0x32,
-0x14,
-0x11,
-0x5f,
-0x44,
-0x49,
-0x53,
-0x0,
-0x7d,
-0x50,
-0x52,
-0x51,
-0x32,
-0xa,
-0x80,
-0x50,
-0x52,
-0x51,
-0x32,
-0x14,
-0xf,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x0,
-0xa4,
-0x49,
-0x51,
-0x43,
-0x52,
-0x50,
-0x52,
-0x51,
-0x32,
-0x14,
-0x17,
-0x5f,
-0x53,
-0x52,
-0x53,
-0x1,
-0x8a,
-0x68,
-0xa,
-0x5,
-0x50,
-0x52,
-0x52,
-0x49,
-0x70,
-0x50,
-0x52,
-0x52,
-0x49,
-0x50,
-0x52,
-0x51,
-0x32,
-0x5b,
-0x82,
-0x4d,
-0x7,
-0x4c,
-0x4e,
-0x4b,
-0x44,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xc,
-0xf,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0xa,
-0x3,
-0x8,
-0x5f,
-0x50,
-0x52,
-0x53,
-0x11,
-0x16,
-0xa,
-0x13,
-0x89,
-0xe,
-0x0,
-0x9,
-0x3,
-0x5,
-0x0,
-0x0,
-0x0,
-0xa,
-0x0,
-0x0,
-0x0,
-0xb,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x14,
-0xf,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0xa4,
-0x49,
-0x51,
-0x53,
-0x54,
-0x50,
-0x52,
-0x51,
-0x33,
-0x14,
-0x11,
-0x5f,
-0x44,
-0x49,
-0x53,
-0x0,
-0x7d,
-0x50,
-0x52,
-0x51,
-0x33,
-0xa,
-0x80,
-0x50,
-0x52,
-0x51,
-0x33,
-0x14,
-0xf,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x0,
-0xa4,
-0x49,
-0x51,
-0x43,
-0x52,
-0x50,
-0x52,
-0x51,
-0x33,
-0x14,
-0x17,
-0x5f,
-0x53,
-0x52,
-0x53,
-0x1,
-0x8a,
-0x68,
-0xa,
-0x5,
-0x50,
-0x52,
-0x52,
-0x49,
-0x70,
-0x50,
-0x52,
-0x52,
-0x49,
-0x50,
-0x52,
-0x51,
-0x33,
-0x5b,
-0x82,
-0x4f,
-0x4,
-0x4c,
-0x4e,
-0x4b,
-0x53,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xc,
-0xf,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0xa,
-0x4,
-0x8,
-0x5f,
-0x50,
-0x52,
-0x53,
-0x11,
-0xe,
-0xa,
-0xb,
-0x89,
-0x6,
-0x0,
-0x9,
-0x1,
-0x9,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x14,
-0x9,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0xa4,
-0xa,
-0xb,
-0x14,
-0x6,
-0x5f,
-0x44,
-0x49,
-0x53,
-0x0,
-0x14,
-0xb,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x0,
-0xa4,
-0x5f,
-0x50,
-0x52,
-0x53,
-0x14,
-0x6,
-0x5f,
-0x53,
-0x52,
-0x53,
-0x1,
-0x10,
-0x4d,
-0xc,
-0x5f,
-0x53,
-0x42,
-0x5f,
-0x14,
-0x35,
-0x43,
-0x50,
-0x4d,
-0x41,
-0x1,
-0x70,
-0x83,
-0x88,
-0x43,
-0x50,
-0x4f,
-0x4e,
-0x68,
-0x0,
-0x60,
-0x70,
-0x11,
-0xb,
-0xa,
-0x8,
-0x0,
-0x8,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x61,
-0x70,
-0x68,
-0x88,
-0x61,
-0xa,
-0x2,
-0x0,
-0x70,
-0x68,
-0x88,
-0x61,
-0xa,
-0x3,
-0x0,
-0x70,
-0x60,
-0x88,
-0x61,
-0xa,
-0x4,
-0x0,
-0xa4,
-0x61,
-0x14,
-0x1a,
-0x43,
-0x50,
-0x53,
-0x54,
-0x1,
-0x70,
-0x83,
-0x88,
-0x43,
-0x50,
-0x4f,
-0x4e,
-0x68,
-0x0,
-0x60,
-0xa0,
-0x5,
-0x60,
-0xa4,
-0xa,
-0xf,
-0xa1,
-0x3,
-0xa4,
-0x0,
-0x14,
-0xa,
-0x43,
-0x50,
-0x45,
-0x4a,
-0x2,
-0x5b,
-0x22,
-0xa,
-0xc8,
-0x14,
-0x4a,
-0x6,
-0x50,
-0x52,
-0x53,
-0x43,
-0x0,
-0x70,
-0x50,
-0x52,
-0x53,
-0x5f,
-0x65,
-0x70,
-0x0,
-0x62,
-0x70,
-0x0,
-0x60,
-0xa2,
-0x46,
-0x5,
-0x95,
-0x60,
-0x87,
-0x43,
-0x50,
-0x4f,
-0x4e,
-0x70,
-0x83,
-0x88,
-0x43,
-0x50,
-0x4f,
-0x4e,
-0x60,
-0x0,
-0x61,
-0xa0,
-0xa,
-0x7b,
-0x60,
-0xa,
-0x7,
-0x0,
-0x7a,
-0x62,
-0x1,
-0x62,
-0xa1,
-0xc,
-0x70,
-0x83,
-0x88,
-0x65,
-0x7a,
-0x60,
-0xa,
-0x3,
-0x0,
-0x0,
-0x62,
-0x70,
-0x7b,
-0x62,
-0x1,
-0x0,
-0x63,
-0xa0,
-0x22,
-0x92,
-0x93,
-0x61,
-0x63,
-0x70,
-0x63,
-0x88,
-0x43,
-0x50,
-0x4f,
-0x4e,
-0x60,
-0x0,
-0xa0,
-0xa,
-0x93,
-0x63,
-0x1,
-0x4e,
-0x54,
-0x46,
-0x59,
-0x60,
-0x1,
-0xa1,
-0x8,
-0x4e,
-0x54,
-0x46,
-0x59,
-0x60,
-0xa,
-0x3,
-0x75,
-0x60,
-0x10,
-0x44,
-0x2a,
-0x2e,
-0x5f,
-0x53,
-0x42,
-0x5f,
-0x50,
-0x43,
-0x49,
-0x30,
-0x5b,
-0x82,
-0x47,
-0x29,
-0x4d,
-0x48,
-0x50,
-0x44,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xd,
-0x50,
-0x4e,
-0x50,
-0x30,
-0x41,
-0x30,
-0x36,
-0x0,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0xd,
-0x4d,
-0x65,
-0x6d,
-0x6f,
-0x72,
-0x79,
-0x20,
-0x68,
-0x6f,
-0x74,
-0x70,
-0x6c,
-0x75,
-0x67,
-0x20,
-0x72,
-0x65,
-0x73,
-0x6f,
-0x75,
-0x72,
-0x63,
-0x65,
-0x73,
-0x0,
-0x14,
-0x13,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0xa0,
-0x9,
-0x93,
-0x4d,
-0x44,
-0x4e,
-0x52,
-0x0,
-0xa4,
-0x0,
-0xa4,
-0xa,
-0xb,
-0x5b,
-0x1,
-0x4d,
-0x4c,
-0x43,
-0x4b,
-0x0,
-0x14,
-0x4a,
-0x4,
-0x4d,
-0x53,
-0x43,
-0x4e,
-0x0,
-0xa0,
-0x9,
-0x93,
-0x4d,
-0x44,
-0x4e,
-0x52,
-0x0,
-0xa4,
-0x0,
-0x70,
-0x0,
-0x60,
-0x5b,
-0x23,
-0x4d,
-0x4c,
-0x43,
-0x4b,
-0xff,
-0xff,
-0xa2,
-0x25,
-0x95,
-0x60,
-0x4d,
-0x44,
-0x4e,
-0x52,
-0x70,
-0x60,
-0x4d,
-0x53,
-0x45,
-0x4c,
-0xa0,
-0x13,
-0x93,
-0x4d,
-0x49,
-0x4e,
-0x53,
-0x1,
-0x4d,
-0x54,
-0x46,
-0x59,
-0x60,
-0x1,
-0x70,
-0x1,
-0x4d,
-0x49,
-0x4e,
-0x53,
-0x72,
-0x60,
-0x1,
-0x60,
-0x5b,
-0x27,
-0x4d,
-0x4c,
-0x43,
-0x4b,
-0xa4,
-0x1,
-0x14,
-0x2d,
-0x4d,
-0x52,
-0x53,
-0x54,
-0x1,
-0x70,
-0x0,
-0x60,
-0x5b,
-0x23,
-0x4d,
-0x4c,
-0x43,
-0x4b,
-0xff,
-0xff,
-0x70,
-0x99,
-0x68,
-0x0,
-0x4d,
-0x53,
-0x45,
-0x4c,
-0xa0,
-0xb,
-0x93,
-0x4d,
-0x45,
-0x53,
-0x5f,
-0x1,
-0x70,
-0xa,
-0xf,
-0x60,
-0x5b,
-0x27,
-0x4d,
-0x4c,
-0x43,
-0x4b,
-0xa4,
-0x60,
-0x14,
-0x41,
-0x18,
-0x4d,
-0x43,
-0x52,
-0x53,
-0x9,
-0x5b,
-0x23,
-0x4d,
-0x4c,
-0x43,
-0x4b,
-0xff,
-0xff,
-0x70,
-0x99,
-0x68,
-0x0,
-0x4d,
-0x53,
-0x45,
-0x4c,
-0x8,
-0x4d,
-0x52,
-0x36,
-0x34,
-0x11,
-0x33,
-0xa,
-0x30,
-0x8a,
-0x2b,
-0x0,
-0x0,
-0xc,
-0x3,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0xfe,
-0xff,
-0xff,
-0xff,
-0xff,
-0xff,
-0xff,
-0xff,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0xff,
-0xff,
-0xff,
-0xff,
-0xff,
-0xff,
-0xff,
-0xff,
-0x79,
-0x0,
-0x8a,
-0x4d,
-0x52,
-0x36,
-0x34,
-0xa,
-0xe,
-0x4d,
-0x49,
-0x4e,
-0x4c,
-0x8a,
-0x4d,
-0x52,
-0x36,
-0x34,
-0xa,
-0x12,
-0x4d,
-0x49,
-0x4e,
-0x48,
-0x8a,
-0x4d,
-0x52,
-0x36,
-0x34,
-0xa,
-0x26,
-0x4c,
-0x45,
-0x4e,
-0x4c,
-0x8a,
-0x4d,
-0x52,
-0x36,
-0x34,
-0xa,
-0x2a,
-0x4c,
-0x45,
-0x4e,
-0x48,
-0x8a,
-0x4d,
-0x52,
-0x36,
-0x34,
-0xa,
-0x16,
-0x4d,
-0x41,
-0x58,
-0x4c,
-0x8a,
-0x4d,
-0x52,
-0x36,
-0x34,
-0xa,
-0x1a,
-0x4d,
-0x41,
-0x58,
-0x48,
-0x70,
-0x4d,
-0x52,
-0x42,
-0x48,
-0x4d,
-0x49,
-0x4e,
-0x48,
-0x70,
-0x4d,
-0x52,
-0x42,
-0x4c,
-0x4d,
-0x49,
-0x4e,
-0x4c,
-0x70,
-0x4d,
-0x52,
-0x4c,
-0x48,
-0x4c,
-0x45,
-0x4e,
-0x48,
-0x70,
-0x4d,
-0x52,
-0x4c,
-0x4c,
-0x4c,
-0x45,
-0x4e,
-0x4c,
-0x72,
-0x4d,
-0x49,
-0x4e,
-0x4c,
-0x4c,
-0x45,
-0x4e,
-0x4c,
-0x4d,
-0x41,
-0x58,
-0x4c,
-0x72,
-0x4d,
-0x49,
-0x4e,
-0x48,
-0x4c,
-0x45,
-0x4e,
-0x48,
-0x4d,
-0x41,
-0x58,
-0x48,
-0xa0,
-0x14,
-0x95,
-0x4d,
-0x41,
-0x58,
-0x4c,
-0x4d,
-0x49,
-0x4e,
-0x4c,
-0x72,
-0x4d,
-0x41,
-0x58,
-0x48,
-0x1,
-0x4d,
-0x41,
-0x58,
-0x48,
-0xa0,
-0x11,
-0x95,
-0x4d,
-0x41,
-0x58,
-0x4c,
-0x1,
-0x74,
-0x4d,
-0x41,
-0x58,
-0x48,
-0x1,
-0x4d,
-0x41,
-0x58,
-0x48,
-0x74,
-0x4d,
-0x41,
-0x58,
-0x4c,
-0x1,
-0x4d,
-0x41,
-0x58,
-0x4c,
-0xa0,
-0x44,
-0x7,
-0x93,
-0x4d,
-0x41,
-0x58,
-0x48,
-0x0,
-0x8,
-0x4d,
-0x52,
-0x33,
-0x32,
-0x11,
-0x1f,
-0xa,
-0x1c,
-0x87,
-0x17,
-0x0,
-0x0,
-0xc,
-0x3,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0xfe,
-0xff,
-0xff,
-0xff,
-0x0,
-0x0,
-0x0,
-0x0,
-0xff,
-0xff,
-0xff,
-0xff,
-0x79,
-0x0,
-0x8a,
-0x4d,
-0x52,
-0x33,
-0x32,
-0xa,
-0xa,
-0x4d,
-0x49,
-0x4e,
-0x5f,
-0x8a,
-0x4d,
-0x52,
-0x33,
-0x32,
-0xa,
-0xe,
-0x4d,
-0x41,
-0x58,
-0x5f,
-0x8a,
-0x4d,
-0x52,
-0x33,
-0x32,
-0xa,
-0x16,
-0x4c,
-0x45,
-0x4e,
-0x5f,
-0x70,
-0x4d,
-0x49,
-0x4e,
-0x4c,
-0x4d,
-0x49,
-0x4e,
-0x5f,
-0x70,
-0x4d,
-0x41,
-0x58,
-0x4c,
-0x4d,
-0x41,
-0x58,
-0x5f,
-0x70,
-0x4c,
-0x45,
-0x4e,
-0x4c,
-0x4c,
-0x45,
-0x4e,
-0x5f,
-0x5b,
-0x27,
-0x4d,
-0x4c,
-0x43,
-0x4b,
-0xa4,
-0x4d,
-0x52,
-0x33,
-0x32,
-0x5b,
-0x27,
-0x4d,
-0x4c,
-0x43,
-0x4b,
-0xa4,
-0x4d,
-0x52,
-0x36,
-0x34,
-0x14,
-0x24,
-0x4d,
-0x50,
-0x58,
-0x4d,
-0x1,
-0x5b,
-0x23,
-0x4d,
-0x4c,
-0x43,
-0x4b,
-0xff,
-0xff,
-0x70,
-0x99,
-0x68,
-0x0,
-0x4d,
-0x53,
-0x45,
-0x4c,
-0x70,
-0x4d,
-0x50,
-0x58,
-0x5f,
-0x60,
-0x5b,
-0x27,
-0x4d,
-0x4c,
-0x43,
-0x4b,
-0xa4,
-0x60,
-0x14,
-0x28,
-0x4d,
-0x4f,
-0x53,
-0x54,
-0x4,
-0x5b,
-0x23,
-0x4d,
-0x4c,
-0x43,
-0x4b,
-0xff,
-0xff,
-0x70,
-0x99,
-0x68,
-0x0,
-0x4d,
-0x53,
-0x45,
-0x4c,
-0x70,
-0x69,
-0x4d,
-0x4f,
-0x45,
-0x56,
-0x70,
-0x6a,
-0x4d,
-0x4f,
-0x53,
-0x43,
-0x5b,
-0x27,
-0x4d,
-0x4c,
-0x43,
-0x4b,
-0x10,
-0x45,
-0xd,
-0x5f,
-0x47,
-0x50,
-0x45,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xd,
-0x41,
-0x43,
-0x50,
-0x49,
-0x30,
-0x30,
-0x30,
-0x36,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x30,
-0x0,
-0x14,
-0x39,
-0x5f,
-0x45,
-0x30,
-0x31,
-0x0,
-0x5b,
-0x23,
-0x5c,
-0x2f,
-0x3,
-0x5f,
-0x53,
-0x42,
-0x5f,
-0x50,
-0x43,
-0x49,
-0x30,
-0x42,
-0x4c,
-0x43,
-0x4b,
-0xff,
-0xff,
-0x5c,
-0x2f,
-0x3,
-0x5f,
-0x53,
-0x42,
-0x5f,
-0x50,
-0x43,
-0x49,
-0x30,
-0x50,
-0x43,
-0x4e,
-0x54,
-0x5b,
-0x27,
-0x5c,
-0x2f,
-0x3,
-0x5f,
-0x53,
-0x42,
-0x5f,
-0x50,
-0x43,
-0x49,
-0x30,
-0x42,
-0x4c,
-0x43,
-0x4b,
-0x14,
-0x10,
-0x5f,
-0x45,
-0x30,
-0x32,
-0x0,
-0x5c,
-0x2e,
-0x5f,
-0x53,
-0x42,
-0x5f,
-0x50,
-0x52,
-0x53,
-0x43,
-0x14,
-0x19,
-0x5f,
-0x45,
-0x30,
-0x33,
-0x0,
-0x5c,
-0x2f,
-0x4,
-0x5f,
-0x53,
-0x42,
-0x5f,
-0x50,
-0x43,
-0x49,
-0x30,
-0x4d,
-0x48,
-0x50,
-0x44,
-0x4d,
-0x53,
-0x43,
-0x4e,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x34,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x35,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x36,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x37,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x38,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x39,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x41,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x42,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x43,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x44,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x45,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x46,
-0x0
-};
diff --git a/qemu/hw/i386/intel_iommu.c b/qemu/hw/i386/intel_iommu.c
index 08055a8d8..347718f93 100644
--- a/qemu/hw/i386/intel_iommu.c
+++ b/qemu/hw/i386/intel_iommu.c
@@ -19,9 +19,11 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "exec/address-spaces.h"
#include "intel_iommu_internal.h"
+#include "hw/pci/pci.h"
/*#define DEBUG_INTEL_IOMMU*/
#ifdef DEBUG_INTEL_IOMMU
@@ -151,14 +153,27 @@ static gboolean vtd_hash_remove_by_domain(gpointer key, gpointer value,
return entry->domain_id == domain_id;
}
+/* The shift of an addr for a certain level of paging structure */
+static inline uint32_t vtd_slpt_level_shift(uint32_t level)
+{
+ return VTD_PAGE_SHIFT_4K + (level - 1) * VTD_SL_LEVEL_BITS;
+}
+
+static inline uint64_t vtd_slpt_level_page_mask(uint32_t level)
+{
+ return ~((1ULL << vtd_slpt_level_shift(level)) - 1);
+}
+
static gboolean vtd_hash_remove_by_page(gpointer key, gpointer value,
gpointer user_data)
{
VTDIOTLBEntry *entry = (VTDIOTLBEntry *)value;
VTDIOTLBPageInvInfo *info = (VTDIOTLBPageInvInfo *)user_data;
- uint64_t gfn = info->gfn & info->mask;
+ uint64_t gfn = (info->addr >> VTD_PAGE_SHIFT_4K) & info->mask;
+ uint64_t gfn_tlb = (info->addr & entry->mask) >> VTD_PAGE_SHIFT_4K;
return (entry->domain_id == info->domain_id) &&
- ((entry->gfn & info->mask) == gfn);
+ (((entry->gfn & info->mask) == gfn) ||
+ (entry->gfn == gfn_tlb));
}
/* Reset all the gen of VTDAddressSpace to zero and set the gen of
@@ -166,19 +181,17 @@ static gboolean vtd_hash_remove_by_page(gpointer key, gpointer value,
*/
static void vtd_reset_context_cache(IntelIOMMUState *s)
{
- VTDAddressSpace **pvtd_as;
VTDAddressSpace *vtd_as;
- uint32_t bus_it;
+ VTDBus *vtd_bus;
+ GHashTableIter bus_it;
uint32_t devfn_it;
+ g_hash_table_iter_init(&bus_it, s->vtd_as_by_busptr);
+
VTD_DPRINTF(CACHE, "global context_cache_gen=1");
- for (bus_it = 0; bus_it < VTD_PCI_BUS_MAX; ++bus_it) {
- pvtd_as = s->address_spaces[bus_it];
- if (!pvtd_as) {
- continue;
- }
+ while (g_hash_table_iter_next (&bus_it, NULL, (void**)&vtd_bus)) {
for (devfn_it = 0; devfn_it < VTD_PCI_DEVFN_MAX; ++devfn_it) {
- vtd_as = pvtd_as[devfn_it];
+ vtd_as = vtd_bus->dev_as[devfn_it];
if (!vtd_as) {
continue;
}
@@ -194,24 +207,46 @@ static void vtd_reset_iotlb(IntelIOMMUState *s)
g_hash_table_remove_all(s->iotlb);
}
+static uint64_t vtd_get_iotlb_key(uint64_t gfn, uint8_t source_id,
+ uint32_t level)
+{
+ return gfn | ((uint64_t)(source_id) << VTD_IOTLB_SID_SHIFT) |
+ ((uint64_t)(level) << VTD_IOTLB_LVL_SHIFT);
+}
+
+static uint64_t vtd_get_iotlb_gfn(hwaddr addr, uint32_t level)
+{
+ return (addr & vtd_slpt_level_page_mask(level)) >> VTD_PAGE_SHIFT_4K;
+}
+
static VTDIOTLBEntry *vtd_lookup_iotlb(IntelIOMMUState *s, uint16_t source_id,
hwaddr addr)
{
+ VTDIOTLBEntry *entry;
uint64_t key;
+ int level;
+
+ for (level = VTD_SL_PT_LEVEL; level < VTD_SL_PML4_LEVEL; level++) {
+ key = vtd_get_iotlb_key(vtd_get_iotlb_gfn(addr, level),
+ source_id, level);
+ entry = g_hash_table_lookup(s->iotlb, &key);
+ if (entry) {
+ goto out;
+ }
+ }
- key = (addr >> VTD_PAGE_SHIFT_4K) |
- ((uint64_t)(source_id) << VTD_IOTLB_SID_SHIFT);
- return g_hash_table_lookup(s->iotlb, &key);
-
+out:
+ return entry;
}
static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id,
uint16_t domain_id, hwaddr addr, uint64_t slpte,
- bool read_flags, bool write_flags)
+ bool read_flags, bool write_flags,
+ uint32_t level)
{
VTDIOTLBEntry *entry = g_malloc(sizeof(*entry));
uint64_t *key = g_malloc(sizeof(*key));
- uint64_t gfn = addr >> VTD_PAGE_SHIFT_4K;
+ uint64_t gfn = vtd_get_iotlb_gfn(addr, level);
VTD_DPRINTF(CACHE, "update iotlb sid 0x%"PRIx16 " gpa 0x%"PRIx64
" slpte 0x%"PRIx64 " did 0x%"PRIx16, source_id, addr, slpte,
@@ -226,7 +261,8 @@ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id,
entry->slpte = slpte;
entry->read_flags = read_flags;
entry->write_flags = write_flags;
- *key = gfn | ((uint64_t)(source_id) << VTD_IOTLB_SID_SHIFT);
+ entry->mask = vtd_slpt_level_page_mask(level);
+ *key = vtd_get_iotlb_key(gfn, source_id, level);
g_hash_table_replace(s->iotlb, key, entry);
}
@@ -501,12 +537,6 @@ static inline dma_addr_t vtd_get_slpt_base_from_context(VTDContextEntry *ce)
return ce->lo & VTD_CONTEXT_ENTRY_SLPTPTR;
}
-/* The shift of an addr for a certain level of paging structure */
-static inline uint32_t vtd_slpt_level_shift(uint32_t level)
-{
- return VTD_PAGE_SHIFT_4K + (level - 1) * VTD_SL_LEVEL_BITS;
-}
-
static inline uint64_t vtd_get_slpte_addr(uint64_t slpte)
{
return slpte & VTD_SL_PT_BASE_ADDR_MASK;
@@ -754,14 +784,15 @@ static inline bool vtd_is_interrupt_addr(hwaddr addr)
* @is_write: The access is a write operation
* @entry: IOMMUTLBEntry that contain the addr to be translated and result
*/
-static void vtd_do_iommu_translate(VTDAddressSpace *vtd_as, uint8_t bus_num,
+static void vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus,
uint8_t devfn, hwaddr addr, bool is_write,
IOMMUTLBEntry *entry)
{
IntelIOMMUState *s = vtd_as->iommu_state;
VTDContextEntry ce;
+ uint8_t bus_num = pci_bus_num(bus);
VTDContextCacheEntry *cc_entry = &vtd_as->context_cache_entry;
- uint64_t slpte;
+ uint64_t slpte, page_mask;
uint32_t level;
uint16_t source_id = vtd_make_source_id(bus_num, devfn);
int ret_fr;
@@ -801,6 +832,7 @@ static void vtd_do_iommu_translate(VTDAddressSpace *vtd_as, uint8_t bus_num,
slpte = iotlb_entry->slpte;
reads = iotlb_entry->read_flags;
writes = iotlb_entry->write_flags;
+ page_mask = iotlb_entry->mask;
goto out;
}
/* Try to fetch context-entry from cache first */
@@ -847,12 +879,13 @@ static void vtd_do_iommu_translate(VTDAddressSpace *vtd_as, uint8_t bus_num,
return;
}
+ page_mask = vtd_slpt_level_page_mask(level);
vtd_update_iotlb(s, source_id, VTD_CONTEXT_ENTRY_DID(ce.hi), addr, slpte,
- reads, writes);
+ reads, writes, level);
out:
- entry->iova = addr & VTD_PAGE_MASK_4K;
- entry->translated_addr = vtd_get_slpte_addr(slpte) & VTD_PAGE_MASK_4K;
- entry->addr_mask = ~VTD_PAGE_MASK_4K;
+ entry->iova = addr & page_mask;
+ entry->translated_addr = vtd_get_slpte_addr(slpte) & page_mask;
+ entry->addr_mask = ~page_mask;
entry->perm = (writes ? 2 : 0) + (reads ? 1 : 0);
}
@@ -874,6 +907,29 @@ static void vtd_context_global_invalidate(IntelIOMMUState *s)
}
}
+
+/* Find the VTD address space currently associated with a given bus number,
+ */
+static VTDBus *vtd_find_as_from_bus_num(IntelIOMMUState *s, uint8_t bus_num)
+{
+ VTDBus *vtd_bus = s->vtd_as_by_bus_num[bus_num];
+ if (!vtd_bus) {
+ /* Iterate over the registered buses to find the one
+ * which currently hold this bus number, and update the bus_num lookup table:
+ */
+ GHashTableIter iter;
+
+ g_hash_table_iter_init(&iter, s->vtd_as_by_busptr);
+ while (g_hash_table_iter_next (&iter, NULL, (void**)&vtd_bus)) {
+ if (pci_bus_num(vtd_bus->bus) == bus_num) {
+ s->vtd_as_by_bus_num[bus_num] = vtd_bus;
+ return vtd_bus;
+ }
+ }
+ }
+ return vtd_bus;
+}
+
/* Do a context-cache device-selective invalidation.
* @func_mask: FM field after shifting
*/
@@ -882,7 +938,7 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s,
uint16_t func_mask)
{
uint16_t mask;
- VTDAddressSpace **pvtd_as;
+ VTDBus *vtd_bus;
VTDAddressSpace *vtd_as;
uint16_t devfn;
uint16_t devfn_it;
@@ -903,11 +959,11 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s,
}
VTD_DPRINTF(INV, "device-selective invalidation source 0x%"PRIx16
" mask %"PRIu16, source_id, mask);
- pvtd_as = s->address_spaces[VTD_SID_TO_BUS(source_id)];
- if (pvtd_as) {
+ vtd_bus = vtd_find_as_from_bus_num(s, VTD_SID_TO_BUS(source_id));
+ if (vtd_bus) {
devfn = VTD_SID_TO_DEVFN(source_id);
for (devfn_it = 0; devfn_it < VTD_PCI_DEVFN_MAX; ++devfn_it) {
- vtd_as = pvtd_as[devfn_it];
+ vtd_as = vtd_bus->dev_as[devfn_it];
if (vtd_as && ((devfn_it & mask) == (devfn & mask))) {
VTD_DPRINTF(INV, "invalidate context-cahce of devfn 0x%"PRIx16,
devfn_it);
@@ -967,7 +1023,7 @@ static void vtd_iotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id,
assert(am <= VTD_MAMV);
info.domain_id = domain_id;
- info.gfn = addr >> VTD_PAGE_SHIFT_4K;
+ info.addr = addr;
info.mask = ~((1 << am) - 1);
g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_page, &info);
}
@@ -1805,11 +1861,11 @@ static IOMMUTLBEntry vtd_iommu_translate(MemoryRegion *iommu, hwaddr addr,
return ret;
}
- vtd_do_iommu_translate(vtd_as, vtd_as->bus_num, vtd_as->devfn, addr,
+ vtd_do_iommu_translate(vtd_as, vtd_as->bus, vtd_as->devfn, addr,
is_write, &ret);
VTD_DPRINTF(MMU,
"bus %"PRIu8 " slot %"PRIu8 " func %"PRIu8 " devfn %"PRIu8
- " gpa 0x%"PRIx64 " hpa 0x%"PRIx64, vtd_as->bus_num,
+ " gpa 0x%"PRIx64 " hpa 0x%"PRIx64, pci_bus_num(vtd_as->bus),
VTD_PCI_SLOT(vtd_as->devfn), VTD_PCI_FUNC(vtd_as->devfn),
vtd_as->devfn, addr, ret.translated_addr);
return ret;
@@ -1839,6 +1895,38 @@ static Property vtd_properties[] = {
DEFINE_PROP_END_OF_LIST(),
};
+
+VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn)
+{
+ uintptr_t key = (uintptr_t)bus;
+ VTDBus *vtd_bus = g_hash_table_lookup(s->vtd_as_by_busptr, &key);
+ VTDAddressSpace *vtd_dev_as;
+
+ if (!vtd_bus) {
+ /* No corresponding free() */
+ vtd_bus = g_malloc0(sizeof(VTDBus) + sizeof(VTDAddressSpace *) * VTD_PCI_DEVFN_MAX);
+ vtd_bus->bus = bus;
+ key = (uintptr_t)bus;
+ g_hash_table_insert(s->vtd_as_by_busptr, &key, vtd_bus);
+ }
+
+ vtd_dev_as = vtd_bus->dev_as[devfn];
+
+ if (!vtd_dev_as) {
+ vtd_bus->dev_as[devfn] = vtd_dev_as = g_malloc0(sizeof(VTDAddressSpace));
+
+ vtd_dev_as->bus = bus;
+ vtd_dev_as->devfn = (uint8_t)devfn;
+ vtd_dev_as->iommu_state = s;
+ vtd_dev_as->context_cache_entry.context_cache_gen = 0;
+ memory_region_init_iommu(&vtd_dev_as->iommu, OBJECT(s),
+ &s->iommu_ops, "intel_iommu", UINT64_MAX);
+ address_space_init(&vtd_dev_as->as,
+ &vtd_dev_as->iommu, "intel_iommu");
+ }
+ return vtd_dev_as;
+}
+
/* Do the initialization. It will also be called when reset, so pay
* attention when adding new initialization stuff.
*/
@@ -1861,7 +1949,7 @@ static void vtd_init(IntelIOMMUState *s)
s->iq_last_desc_type = VTD_INV_DESC_NONE;
s->next_frcd_reg = 0;
s->cap = VTD_CAP_FRO | VTD_CAP_NFR | VTD_CAP_ND | VTD_CAP_MGAW |
- VTD_CAP_SAGAW | VTD_CAP_MAMV | VTD_CAP_PSI;
+ VTD_CAP_SAGAW | VTD_CAP_MAMV | VTD_CAP_PSI | VTD_CAP_SLLPS;
s->ecap = VTD_ECAP_QI | VTD_ECAP_IRO;
vtd_reset_context_cache(s);
@@ -1931,13 +2019,15 @@ static void vtd_realize(DeviceState *dev, Error **errp)
IntelIOMMUState *s = INTEL_IOMMU_DEVICE(dev);
VTD_DPRINTF(GENERAL, "");
- memset(s->address_spaces, 0, sizeof(s->address_spaces));
+ memset(s->vtd_as_by_bus_num, 0, sizeof(s->vtd_as_by_bus_num));
memory_region_init_io(&s->csrmem, OBJECT(s), &vtd_mem_ops, s,
"intel_iommu", DMAR_REG_SIZE);
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->csrmem);
/* No corresponding destroy */
s->iotlb = g_hash_table_new_full(vtd_uint64_hash, vtd_uint64_equal,
g_free, g_free);
+ s->vtd_as_by_busptr = g_hash_table_new_full(vtd_uint64_hash, vtd_uint64_equal,
+ g_free, g_free);
vtd_init(s);
}
diff --git a/qemu/hw/i386/intel_iommu_internal.h b/qemu/hw/i386/intel_iommu_internal.h
index ba288ab1d..e5f514c6e 100644
--- a/qemu/hw/i386/intel_iommu_internal.h
+++ b/qemu/hw/i386/intel_iommu_internal.h
@@ -113,6 +113,7 @@
/* The shift of source_id in the key of IOTLB hash table */
#define VTD_IOTLB_SID_SHIFT 36
+#define VTD_IOTLB_LVL_SHIFT 44
#define VTD_IOTLB_MAX_SIZE 1024 /* Max size of the hash table */
/* IOTLB_REG */
@@ -185,9 +186,10 @@
#define VTD_CAP_ND (((VTD_DOMAIN_ID_SHIFT - 4) / 2) & 7ULL)
#define VTD_MGAW 39 /* Maximum Guest Address Width */
#define VTD_CAP_MGAW (((VTD_MGAW - 1) & 0x3fULL) << 16)
-#define VTD_MAMV 9ULL
+#define VTD_MAMV 18ULL
#define VTD_CAP_MAMV (VTD_MAMV << 48)
#define VTD_CAP_PSI (1ULL << 39)
+#define VTD_CAP_SLLPS ((1ULL << 34) | (1ULL << 35))
/* Supported Adjusted Guest Address Widths */
#define VTD_CAP_SAGAW_SHIFT 8
@@ -320,7 +322,7 @@ typedef struct VTDInvDesc VTDInvDesc;
/* Information about page-selective IOTLB invalidate */
struct VTDIOTLBPageInvInfo {
uint16_t domain_id;
- uint64_t gfn;
+ uint64_t addr;
uint8_t mask;
};
typedef struct VTDIOTLBPageInvInfo VTDIOTLBPageInvInfo;
diff --git a/qemu/hw/i386/kvm/apic.c b/qemu/hw/i386/kvm/apic.c
index 5b470562a..3c7c8fa00 100644
--- a/qemu/hw/i386/kvm/apic.c
+++ b/qemu/hw/i386/kvm/apic.c
@@ -9,6 +9,7 @@
* This work is licensed under the terms of the GNU GPL version 2.
* See the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
#include "hw/i386/apic_internal.h"
#include "hw/pci/msi.h"
#include "sysemu/kvm.h"
@@ -185,7 +186,7 @@ static void kvm_apic_realize(DeviceState *dev, Error **errp)
APIC_SPACE_SIZE);
if (kvm_has_gsi_routing()) {
- msi_supported = true;
+ msi_nonbroken = true;
}
}
diff --git a/qemu/hw/i386/kvm/clock.c b/qemu/hw/i386/kvm/clock.c
index 0593a3f1f..a3b300cad 100644
--- a/qemu/hw/i386/kvm/clock.c
+++ b/qemu/hw/i386/kvm/clock.c
@@ -13,6 +13,7 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/host-utils.h"
#include "sysemu/sysemu.h"
diff --git a/qemu/hw/i386/kvm/i8254.c b/qemu/hw/i386/kvm/i8254.c
index 90eea10df..a4462e5ca 100644
--- a/qemu/hw/i386/kvm/i8254.c
+++ b/qemu/hw/i386/kvm/i8254.c
@@ -22,6 +22,8 @@
* 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/timer.h"
#include "sysemu/sysemu.h"
#include "hw/timer/i8254.h"
diff --git a/qemu/hw/i386/kvm/i8259.c b/qemu/hw/i386/kvm/i8259.c
index 53e3ca8c6..2b207de01 100644
--- a/qemu/hw/i386/kvm/i8259.c
+++ b/qemu/hw/i386/kvm/i8259.c
@@ -9,6 +9,7 @@
* This work is licensed under the terms of the GNU GPL version 2.
* See the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
#include "hw/isa/i8259_internal.h"
#include "hw/i386/apic_internal.h"
#include "sysemu/kvm.h"
diff --git a/qemu/hw/i386/kvm/ioapic.c b/qemu/hw/i386/kvm/ioapic.c
index d2a6c4cf6..8eb2c7a70 100644
--- a/qemu/hw/i386/kvm/ioapic.c
+++ b/qemu/hw/i386/kvm/ioapic.c
@@ -10,6 +10,8 @@
* See the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
+#include "monitor/monitor.h"
#include "hw/i386/pc.h"
#include "hw/i386/ioapic_internal.h"
#include "hw/i386/apic_internal.h"
@@ -110,6 +112,15 @@ static void kvm_ioapic_put(IOAPICCommonState *s)
}
}
+void kvm_ioapic_dump_state(Monitor *mon, const QDict *qdict)
+{
+ IOAPICCommonState s;
+
+ kvm_ioapic_get(&s);
+
+ ioapic_print_redtbl(mon, &s);
+}
+
static void kvm_ioapic_reset(DeviceState *dev)
{
IOAPICCommonState *s = IOAPIC_COMMON(dev);
diff --git a/qemu/hw/i386/kvm/pci-assign.c b/qemu/hw/i386/kvm/pci-assign.c
index 74d22f4fd..bf425a2b9 100644
--- a/qemu/hw/i386/kvm/pci-assign.c
+++ b/qemu/hw/i386/kvm/pci-assign.c
@@ -20,12 +20,9 @@
* Copyright (C) 2008, Red Hat, Amit Shah (amit.shah@redhat.com)
* Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@il.ibm.com)
*/
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/io.h>
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/stat.h>
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "qemu/error-report.h"
@@ -37,6 +34,7 @@
#include "hw/pci/pci.h"
#include "hw/pci/msi.h"
#include "kvm_i386.h"
+#include "hw/pci/pci-assign.h"
#define MSIX_PAGE_SIZE 0x1000
@@ -48,17 +46,6 @@
#define IORESOURCE_PREFETCH 0x00002000 /* No side effects */
#define IORESOURCE_MEM_64 0x00100000
-//#define DEVICE_ASSIGNMENT_DEBUG
-
-#ifdef DEVICE_ASSIGNMENT_DEBUG
-#define DEBUG(fmt, ...) \
- do { \
- fprintf(stderr, "%s: " fmt, __func__ , __VA_ARGS__); \
- } while (0)
-#else
-#define DEBUG(fmt, ...)
-#endif
-
typedef struct PCIRegion {
int type; /* Memory or port I/O */
int valid;
@@ -781,7 +768,7 @@ static char *assign_failed_examine(const AssignedDevice *dev)
"*** $ echo \"%04x:%02x:%02x.%x\" > /sys/bus/pci/drivers/"
"pci-stub/bind\n"
"*** $ echo \"%04x %04x\" > /sys/bus/pci/drivers/pci-stub/remove_id\n"
- "***",
+ "***\n",
ns, dev->host.domain, dev->host.bus, dev->host.slot,
dev->host.function, vendor_id, device_id,
dev->host.domain, dev->host.bus, dev->host.slot, dev->host.function,
@@ -789,7 +776,7 @@ static char *assign_failed_examine(const AssignedDevice *dev)
dev->host.function, vendor_id, device_id);
fail:
- return g_strdup("Couldn't find out why.");
+ return g_strdup("Couldn't find out why.\n");
}
static void assign_device(AssignedDevice *dev, Error **errp)
@@ -823,8 +810,9 @@ static void assign_device(AssignedDevice *dev, Error **errp)
char *cause;
cause = assign_failed_examine(dev);
- error_setg_errno(errp, -r, "Failed to assign device \"%s\"\n%s",
- dev->dev.qdev.id, cause);
+ error_setg_errno(errp, -r, "Failed to assign device \"%s\"",
+ dev->dev.qdev.id);
+ error_append_hint(errp, "%s", cause);
g_free(cause);
break;
}
@@ -923,11 +911,10 @@ retry:
dev->features |= ASSIGNED_DEVICE_PREFER_MSI_MASK;
goto retry;
}
- error_setg_errno(errp, -r,
- "Failed to assign irq for \"%s\"\n"
- "Perhaps you are assigning a device "
- "that shares an IRQ with another device?",
+ error_setg_errno(errp, -r, "Failed to assign irq for \"%s\"",
dev->dev.qdev.id);
+ error_append_hint(errp, "Perhaps you are assigning a device "
+ "that shares an IRQ with another device?\n");
return r;
}
@@ -990,7 +977,7 @@ static void assigned_dev_update_msi(PCIDevice *pci_dev)
MSIMessage msg = msi_get_message(pci_dev, 0);
int virq;
- virq = kvm_irqchip_add_msi_route(kvm_state, msg);
+ virq = kvm_irqchip_add_msi_route(kvm_state, msg, pci_dev);
if (virq < 0) {
perror("assigned_dev_update_msi: kvm_irqchip_add_msi_route");
return;
@@ -1028,7 +1015,7 @@ static void assigned_dev_update_msi_msg(PCIDevice *pci_dev)
}
kvm_irqchip_update_msi_route(kvm_state, assigned_dev->msi_virq[0],
- msi_get_message(pci_dev, 0));
+ msi_get_message(pci_dev, 0), pci_dev);
}
static bool assigned_dev_msix_masked(MSIXTableEntry *entry)
@@ -1094,7 +1081,7 @@ static int assigned_dev_update_msix_mmio(PCIDevice *pci_dev)
msg.address = entry->addr_lo | ((uint64_t)entry->addr_hi << 32);
msg.data = entry->data;
- r = kvm_irqchip_add_msi_route(kvm_state, msg);
+ r = kvm_irqchip_add_msi_route(kvm_state, msg, pci_dev);
if (r < 0) {
return r;
}
@@ -1494,7 +1481,7 @@ static int assigned_device_pci_cap_init(PCIDevice *pci_dev, Error **errp)
* error bits, leave the rest. */
status = pci_get_long(pci_dev->config + pos + PCI_X_STATUS);
status &= ~(PCI_X_STATUS_BUS | PCI_X_STATUS_DEVFN);
- status |= (pci_bus_num(pci_dev->bus) << 8) | pci_dev->devfn;
+ status |= pci_requester_id(pci_dev);
status &= ~(PCI_X_STATUS_SPL_DISC | PCI_X_STATUS_UNX_SPL |
PCI_X_STATUS_SPL_ERR);
pci_set_long(pci_dev->config + pos + PCI_X_STATUS, status);
@@ -1613,7 +1600,8 @@ static void assigned_dev_msix_mmio_write(void *opaque, hwaddr addr,
msg.data = entry->data;
ret = kvm_irqchip_update_msi_route(kvm_state,
- adev->msi_virq[i], msg);
+ adev->msi_virq[i], msg,
+ pdev);
if (ret) {
error_report("Error updating irq routing entry (%d)", ret);
}
@@ -1896,73 +1884,15 @@ static void assign_register_types(void)
type_init(assign_register_types)
-/*
- * Scan the assigned devices for the devices that have an option ROM, and then
- * load the corresponding ROM data to RAM. If an error occurs while loading an
- * option ROM, we just ignore that option ROM and continue with the next one.
- */
static void assigned_dev_load_option_rom(AssignedDevice *dev)
{
- char name[32], rom_file[64];
- FILE *fp;
- uint8_t val;
- struct stat st;
- void *ptr;
-
- /* If loading ROM from file, pci handles it */
- if (dev->dev.romfile || !dev->dev.rom_bar) {
- return;
- }
+ int size = 0;
- snprintf(rom_file, sizeof(rom_file),
- "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/rom",
- dev->host.domain, dev->host.bus, dev->host.slot,
- dev->host.function);
+ pci_assign_dev_load_option_rom(&dev->dev, OBJECT(dev), &size,
+ dev->host.domain, dev->host.bus,
+ dev->host.slot, dev->host.function);
- if (stat(rom_file, &st)) {
- return;
- }
-
- if (access(rom_file, F_OK)) {
- error_report("pci-assign: Insufficient privileges for %s", rom_file);
- return;
- }
-
- /* Write "1" to the ROM file to enable it */
- fp = fopen(rom_file, "r+");
- if (fp == NULL) {
- return;
+ if (!size) {
+ error_report("pci-assign: Invalid ROM.");
}
- val = 1;
- if (fwrite(&val, 1, 1, fp) != 1) {
- goto close_rom;
- }
- fseek(fp, 0, SEEK_SET);
-
- snprintf(name, sizeof(name), "%s.rom",
- object_get_typename(OBJECT(dev)));
- memory_region_init_ram(&dev->dev.rom, OBJECT(dev), name, st.st_size,
- &error_abort);
- vmstate_register_ram(&dev->dev.rom, &dev->dev.qdev);
- ptr = memory_region_get_ram_ptr(&dev->dev.rom);
- memset(ptr, 0xff, st.st_size);
-
- if (!fread(ptr, 1, st.st_size, fp)) {
- error_report("pci-assign: Cannot read from host %s", rom_file);
- error_printf("Device option ROM contents are probably invalid "
- "(check dmesg).\nSkip option ROM probe with rombar=0, "
- "or load from file with romfile=\n");
- goto close_rom;
- }
-
- pci_register_bar(&dev->dev, PCI_ROM_SLOT, 0, &dev->dev.rom);
- dev->dev.has_rom = true;
-close_rom:
- /* Write "0" to disable ROM */
- fseek(fp, 0, SEEK_SET);
- val = 0;
- if (!fwrite(&val, 1, 1, fp)) {
- DEBUG("%s\n", "Failed to disable pci-sysfs rom file");
- }
- fclose(fp);
}
diff --git a/qemu/hw/i386/kvmvapic.c b/qemu/hw/i386/kvmvapic.c
index c6d34b254..c69f37404 100644
--- a/qemu/hw/i386/kvmvapic.c
+++ b/qemu/hw/i386/kvmvapic.c
@@ -8,6 +8,7 @@
* (at your option) any later version. See the COPYING file in the
* top-level directory.
*/
+#include "qemu/osdep.h"
#include "sysemu/sysemu.h"
#include "sysemu/cpus.h"
#include "sysemu/kvm.h"
@@ -634,13 +635,18 @@ static int vapic_prepare(VAPICROMState *s)
static void vapic_write(void *opaque, hwaddr addr, uint64_t data,
unsigned int size)
{
- CPUState *cs = current_cpu;
- X86CPU *cpu = X86_CPU(cs);
- CPUX86State *env = &cpu->env;
- hwaddr rom_paddr;
VAPICROMState *s = opaque;
+ X86CPU *cpu;
+ CPUX86State *env;
+ hwaddr rom_paddr;
- cpu_synchronize_state(cs);
+ if (!current_cpu) {
+ return;
+ }
+
+ cpu_synchronize_state(current_cpu);
+ cpu = X86_CPU(current_cpu);
+ env = &cpu->env;
/*
* The VAPIC supports two PIO-based hypercalls, both via port 0x7E.
diff --git a/qemu/hw/i386/multiboot.c b/qemu/hw/i386/multiboot.c
index 1adbe9e25..387caa67d 100644
--- a/qemu/hw/i386/multiboot.c
+++ b/qemu/hw/i386/multiboot.c
@@ -22,6 +22,9 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "hw/nvram/fw_cfg.h"
#include "multiboot.h"
@@ -195,7 +198,8 @@ int load_multiboot(FWCfgState *fw_cfg,
}
kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
- &elf_low, &elf_high, 0, ELF_MACHINE, 0);
+ &elf_low, &elf_high, 0, I386_ELF_MACHINE,
+ 0, 0);
if (kernel_size < 0) {
fprintf(stderr, "Error while loading elf kernel\n");
exit(1);
diff --git a/qemu/hw/i386/pc.c b/qemu/hw/i386/pc.c
index 7661ea9cd..99437e0b7 100644
--- a/qemu/hw/i386/pc.c
+++ b/qemu/hw/i386/pc.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/char/serial.h"
@@ -33,7 +34,7 @@
#include "hw/pci/pci_bus.h"
#include "hw/nvram/fw_cfg.h"
#include "hw/timer/hpet.h"
-#include "hw/i386/smbios.h"
+#include "hw/smbios/smbios.h"
#include "hw/loader.h"
#include "elf.h"
#include "multiboot.h"
@@ -59,13 +60,13 @@
#include "qemu/error-report.h"
#include "hw/acpi/acpi.h"
#include "hw/acpi/cpu_hotplug.h"
-#include "hw/cpu/icc_bus.h"
#include "hw/boards.h"
#include "hw/pci/pci_host.h"
#include "acpi-build.h"
#include "hw/mem/pc-dimm.h"
#include "qapi/visitor.h"
#include "qapi-visit.h"
+#include "qom/cpu.h"
/* debug PC/ISA interrupts */
//#define DEBUG_IRQ
@@ -77,16 +78,6 @@
#define DPRINTF(fmt, ...)
#endif
-/* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables
- * (128K) and other BIOS datastructures (less than 4K reported to be used at
- * the moment, 32K should be enough for a while). */
-static unsigned acpi_data_size = 0x20000 + 0x8000;
-void pc_set_legacy_acpi_data_size(void)
-{
- acpi_data_size = 0x10000;
-}
-
-#define BIOS_CFG_IOPORT 0x510
#define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0)
#define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1)
#define FW_CFG_IRQ0_OVERRIDE (FW_CFG_ARCH_LOCAL + 2)
@@ -208,24 +199,24 @@ static void pic_irq_request(void *opaque, int irq, int level)
#define REG_EQUIPMENT_BYTE 0x14
-static int cmos_get_fd_drive_type(FDriveType fd0)
+int cmos_get_fd_drive_type(FloppyDriveType fd0)
{
int val;
switch (fd0) {
- case FDRIVE_DRV_144:
+ case FLOPPY_DRIVE_TYPE_144:
/* 1.44 Mb 3"5 drive */
val = 4;
break;
- case FDRIVE_DRV_288:
+ case FLOPPY_DRIVE_TYPE_288:
/* 2.88 Mb 3"5 drive */
val = 5;
break;
- case FDRIVE_DRV_120:
+ case FLOPPY_DRIVE_TYPE_120:
/* 1.2 Mb 5"5 drive */
val = 2;
break;
- case FDRIVE_DRV_NONE:
+ case FLOPPY_DRIVE_TYPE_NONE:
default:
val = 0;
break;
@@ -296,7 +287,8 @@ static void pc_boot_set(void *opaque, const char *boot_device, Error **errp)
static void pc_cmos_init_floppy(ISADevice *rtc_state, ISADevice *floppy)
{
int val, nb, i;
- FDriveType fd_type[2] = { FDRIVE_DRV_NONE, FDRIVE_DRV_NONE };
+ FloppyDriveType fd_type[2] = { FLOPPY_DRIVE_TYPE_NONE,
+ FLOPPY_DRIVE_TYPE_NONE };
/* floppy type */
if (floppy) {
@@ -310,10 +302,10 @@ static void pc_cmos_init_floppy(ISADevice *rtc_state, ISADevice *floppy)
val = rtc_get_memory(rtc_state, REG_EQUIPMENT_BYTE);
nb = 0;
- if (fd_type[0] < FDRIVE_DRV_NONE) {
+ if (fd_type[0] != FLOPPY_DRIVE_TYPE_NONE) {
nb++;
}
- if (fd_type[1] < FDRIVE_DRV_NONE) {
+ if (fd_type[1] != FLOPPY_DRIVE_TYPE_NONE) {
nb++;
}
switch (nb) {
@@ -369,6 +361,31 @@ static const char * const fdc_container_path[] = {
"/unattached", "/peripheral", "/peripheral-anon"
};
+/*
+ * Locate the FDC at IO address 0x3f0, in order to configure the CMOS registers
+ * and ACPI objects.
+ */
+ISADevice *pc_find_fdc0(void)
+{
+ int i;
+ Object *container;
+ CheckFdcState state = { 0 };
+
+ for (i = 0; i < ARRAY_SIZE(fdc_container_path); i++) {
+ container = container_get(qdev_get_machine(), fdc_container_path[i]);
+ object_child_foreach(container, check_fdc, &state);
+ }
+
+ if (state.multiple) {
+ error_report("warning: multiple floppy disk controllers with "
+ "iobase=0x3f0 have been found");
+ error_printf("the one being picked for CMOS setup might not reflect "
+ "your intent");
+ }
+
+ return state.floppy;
+}
+
static void pc_cmos_init_late(void *opaque)
{
pc_cmos_init_late_arg *arg = opaque;
@@ -377,8 +394,6 @@ static void pc_cmos_init_late(void *opaque)
int8_t heads, sectors;
int val;
int i, trans;
- Object *container;
- CheckFdcState state = { 0 };
val = 0;
if (ide_get_geometry(arg->idebus[0], 0,
@@ -408,46 +423,28 @@ static void pc_cmos_init_late(void *opaque)
}
rtc_set_memory(s, 0x39, val);
- /*
- * Locate the FDC at IO address 0x3f0, and configure the CMOS registers
- * accordingly.
- */
- for (i = 0; i < ARRAY_SIZE(fdc_container_path); i++) {
- container = container_get(qdev_get_machine(), fdc_container_path[i]);
- object_child_foreach(container, check_fdc, &state);
- }
-
- if (state.multiple) {
- error_report("warning: multiple floppy disk controllers with "
- "iobase=0x3f0 have been found;\n"
- "the one being picked for CMOS setup might not reflect "
- "your intent");
- }
- pc_cmos_init_floppy(s, state.floppy);
+ pc_cmos_init_floppy(s, pc_find_fdc0());
qemu_unregister_reset(pc_cmos_init_late, opaque);
}
-void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
- const char *boot_device, MachineState *machine,
+void pc_cmos_init(PCMachineState *pcms,
BusState *idebus0, BusState *idebus1,
ISADevice *s)
{
int val;
static pc_cmos_init_late_arg arg;
- PCMachineState *pc_machine = PC_MACHINE(machine);
- Error *local_err = NULL;
/* various important CMOS locations needed by PC/Bochs bios */
/* memory size */
/* base memory (first MiB) */
- val = MIN(ram_size / 1024, 640);
+ val = MIN(pcms->below_4g_mem_size / 1024, 640);
rtc_set_memory(s, 0x15, val);
rtc_set_memory(s, 0x16, val >> 8);
/* extended memory (next 64MiB) */
- if (ram_size > 1024 * 1024) {
- val = (ram_size - 1024 * 1024) / 1024;
+ if (pcms->below_4g_mem_size > 1024 * 1024) {
+ val = (pcms->below_4g_mem_size - 1024 * 1024) / 1024;
} else {
val = 0;
}
@@ -458,8 +455,8 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
rtc_set_memory(s, 0x30, val);
rtc_set_memory(s, 0x31, val >> 8);
/* memory between 16MiB and 4GiB */
- if (ram_size > 16 * 1024 * 1024) {
- val = (ram_size - 16 * 1024 * 1024) / 65536;
+ if (pcms->below_4g_mem_size > 16 * 1024 * 1024) {
+ val = (pcms->below_4g_mem_size - 16 * 1024 * 1024) / 65536;
} else {
val = 0;
}
@@ -468,7 +465,7 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
rtc_set_memory(s, 0x34, val);
rtc_set_memory(s, 0x35, val >> 8);
/* memory above 4GiB */
- val = above_4g_mem_size / 65536;
+ val = pcms->above_4g_mem_size / 65536;
rtc_set_memory(s, 0x5b, val);
rtc_set_memory(s, 0x5c, val >> 8);
rtc_set_memory(s, 0x5d, val >> 16);
@@ -476,19 +473,15 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
/* set the number of CPU */
rtc_set_memory(s, 0x5f, smp_cpus - 1);
- object_property_add_link(OBJECT(machine), "rtc_state",
+ object_property_add_link(OBJECT(pcms), "rtc_state",
TYPE_ISA_DEVICE,
- (Object **)&pc_machine->rtc,
+ (Object **)&pcms->rtc,
object_property_allow_set_link,
OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort);
- object_property_set_link(OBJECT(machine), OBJECT(s),
+ object_property_set_link(OBJECT(pcms), OBJECT(s),
"rtc_state", &error_abort);
- set_boot_dev(s, boot_device, &local_err);
- if (local_err) {
- error_report_err(local_err);
- exit(1);
- }
+ set_boot_dev(s, MACHINE(pcms)->boot_order, &error_fatal);
val = 0;
val |= 0x02; /* FPU is there */
@@ -706,28 +699,51 @@ static uint32_t x86_cpu_apic_id_from_index(unsigned int cpu_index)
}
}
-/* Calculates the limit to CPU APIC ID values
- *
- * This function returns the limit for the APIC ID value, so that all
- * CPU APIC IDs are < pc_apic_id_limit().
- *
- * This is used for FW_CFG_MAX_CPUS. See comments on bochs_bios_init().
- */
-static unsigned int pc_apic_id_limit(unsigned int max_cpus)
+static void pc_build_smbios(FWCfgState *fw_cfg)
{
- return x86_cpu_apic_id_from_index(max_cpus - 1) + 1;
+ uint8_t *smbios_tables, *smbios_anchor;
+ size_t smbios_tables_len, smbios_anchor_len;
+ struct smbios_phys_mem_area *mem_array;
+ unsigned i, array_count;
+
+ smbios_tables = smbios_get_table_legacy(&smbios_tables_len);
+ if (smbios_tables) {
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES,
+ smbios_tables, smbios_tables_len);
+ }
+
+ /* build the array of physical mem area from e820 table */
+ mem_array = g_malloc0(sizeof(*mem_array) * e820_get_num_entries());
+ for (i = 0, array_count = 0; i < e820_get_num_entries(); i++) {
+ uint64_t addr, len;
+
+ if (e820_get_entry(i, E820_RAM, &addr, &len)) {
+ mem_array[array_count].address = addr;
+ mem_array[array_count].length = len;
+ array_count++;
+ }
+ }
+ smbios_get_tables(mem_array, array_count,
+ &smbios_tables, &smbios_tables_len,
+ &smbios_anchor, &smbios_anchor_len);
+ g_free(mem_array);
+
+ if (smbios_anchor) {
+ fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-tables",
+ smbios_tables, smbios_tables_len);
+ fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-anchor",
+ smbios_anchor, smbios_anchor_len);
+ }
}
-static FWCfgState *bochs_bios_init(void)
+static FWCfgState *bochs_bios_init(AddressSpace *as, PCMachineState *pcms)
{
FWCfgState *fw_cfg;
- uint8_t *smbios_tables, *smbios_anchor;
- size_t smbios_tables_len, smbios_anchor_len;
uint64_t *numa_fw_cfg;
int i, j;
- unsigned int apic_id_limit = pc_apic_id_limit(max_cpus);
- fw_cfg = fw_cfg_init_io(BIOS_CFG_IOPORT);
+ fw_cfg = fw_cfg_init_io_dma(FW_CFG_IO_BASE, FW_CFG_IO_BASE + 4, as);
+
/* FW_CFG_MAX_CPUS is a bit confusing/problematic on x86:
*
* SeaBIOS needs FW_CFG_MAX_CPUS for CPU hotplug, but the CPU hotplug
@@ -742,26 +758,13 @@ static FWCfgState *bochs_bios_init(void)
* [1] The only kind of "CPU identifier" used between SeaBIOS and QEMU is
* the APIC ID, not the "CPU index"
*/
- fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)apic_id_limit);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)pcms->apic_id_limit);
fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
fw_cfg_add_bytes(fw_cfg, FW_CFG_ACPI_TABLES,
acpi_tables, acpi_tables_len);
fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, kvm_allows_irq0_override());
- smbios_tables = smbios_get_table_legacy(&smbios_tables_len);
- if (smbios_tables) {
- fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES,
- smbios_tables, smbios_tables_len);
- }
-
- smbios_get_tables(&smbios_tables, &smbios_tables_len,
- &smbios_anchor, &smbios_anchor_len);
- if (smbios_anchor) {
- fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-tables",
- smbios_tables, smbios_tables_len);
- fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-anchor",
- smbios_anchor, smbios_anchor_len);
- }
+ pc_build_smbios(fw_cfg);
fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE,
&e820_reserve, sizeof(e820_reserve));
@@ -773,11 +776,11 @@ static FWCfgState *bochs_bios_init(void)
* of nodes, one word for each VCPU->node and one word for each node to
* hold the amount of memory.
*/
- numa_fw_cfg = g_new0(uint64_t, 1 + apic_id_limit + nb_numa_nodes);
+ numa_fw_cfg = g_new0(uint64_t, 1 + pcms->apic_id_limit + nb_numa_nodes);
numa_fw_cfg[0] = cpu_to_le64(nb_numa_nodes);
for (i = 0; i < max_cpus; i++) {
unsigned int apic_id = x86_cpu_apic_id_from_index(i);
- assert(apic_id < apic_id_limit);
+ assert(apic_id < pcms->apic_id_limit);
for (j = 0; j < nb_numa_nodes; j++) {
if (test_bit(i, numa_info[j].node_cpu)) {
numa_fw_cfg[apic_id + 1] = cpu_to_le64(j);
@@ -786,10 +789,11 @@ static FWCfgState *bochs_bios_init(void)
}
}
for (i = 0; i < nb_numa_nodes; i++) {
- numa_fw_cfg[apic_id_limit + 1 + i] = cpu_to_le64(numa_info[i].node_mem);
+ numa_fw_cfg[pcms->apic_id_limit + 1 + i] =
+ cpu_to_le64(numa_info[i].node_mem);
}
fw_cfg_add_bytes(fw_cfg, FW_CFG_NUMA, numa_fw_cfg,
- (1 + apic_id_limit + nb_numa_nodes) *
+ (1 + pcms->apic_id_limit + nb_numa_nodes) *
sizeof(*numa_fw_cfg));
return fw_cfg;
@@ -809,11 +813,8 @@ static long get_file_size(FILE *f)
return size;
}
-static void load_linux(FWCfgState *fw_cfg,
- const char *kernel_filename,
- const char *initrd_filename,
- const char *kernel_cmdline,
- hwaddr max_ram_size)
+static void load_linux(PCMachineState *pcms,
+ FWCfgState *fw_cfg)
{
uint16_t protocol;
int setup_size, kernel_size, initrd_size = 0, cmdline_size;
@@ -822,6 +823,11 @@ static void load_linux(FWCfgState *fw_cfg,
hwaddr real_addr, prot_addr, cmdline_addr, initrd_addr = 0;
FILE *f;
char *vmode;
+ MachineState *machine = MACHINE(pcms);
+ PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
+ const char *kernel_filename = machine->kernel_filename;
+ const char *initrd_filename = machine->initrd_filename;
+ const char *kernel_cmdline = machine->kernel_cmdline;
/* Align to 16 bytes as a paranoia measure */
cmdline_size = (strlen(kernel_cmdline)+16) & ~15;
@@ -886,8 +892,8 @@ static void load_linux(FWCfgState *fw_cfg,
initrd_max = 0x37ffffff;
}
- if (initrd_max >= max_ram_size - acpi_data_size) {
- initrd_max = max_ram_size - acpi_data_size - 1;
+ if (initrd_max >= pcms->below_4g_mem_size - pcmc->acpi_data_size) {
+ initrd_max = pcms->below_4g_mem_size - pcmc->acpi_data_size - 1;
}
fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr);
@@ -965,6 +971,10 @@ static void load_linux(FWCfgState *fw_cfg,
setup_size = 4;
}
setup_size = (setup_size+1)*512;
+ if (setup_size > kernel_size) {
+ fprintf(stderr, "qemu: invalid kernel header\n");
+ exit(1);
+ }
kernel_size -= setup_size;
setup = g_malloc(setup_size);
@@ -1031,23 +1041,16 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level)
}
static X86CPU *pc_new_cpu(const char *cpu_model, int64_t apic_id,
- DeviceState *icc_bridge, Error **errp)
+ Error **errp)
{
X86CPU *cpu = NULL;
Error *local_err = NULL;
- if (icc_bridge == NULL) {
- error_setg(&local_err, "Invalid icc-bridge value");
- goto out;
- }
-
cpu = cpu_x86_create(cpu_model, &local_err);
if (local_err != NULL) {
goto out;
}
- qdev_set_parent_bus(DEVICE(cpu), qdev_get_child_bus(icc_bridge, "icc"));
-
object_property_set_int(OBJECT(cpu), apic_id, "apic-id", &local_err);
object_property_set_bool(OBJECT(cpu), true, "realized", &local_err);
@@ -1060,12 +1063,10 @@ out:
return cpu;
}
-static const char *current_cpu_model;
-
void pc_hot_add_cpu(const int64_t id, Error **errp)
{
- DeviceState *icc_bridge;
X86CPU *cpu;
+ MachineState *machine = MACHINE(qdev_get_machine());
int64_t apic_id = x86_cpu_apic_id_from_index(id);
Error *local_err = NULL;
@@ -1093,9 +1094,7 @@ void pc_hot_add_cpu(const int64_t id, Error **errp)
return;
}
- icc_bridge = DEVICE(object_resolve_path_type("icc-bridge",
- TYPE_ICC_BRIDGE, NULL));
- cpu = pc_new_cpu(current_cpu_model, apic_id, icc_bridge, &local_err);
+ cpu = pc_new_cpu(machine->cpu_model, apic_id, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
@@ -1103,45 +1102,46 @@ void pc_hot_add_cpu(const int64_t id, Error **errp)
object_unref(OBJECT(cpu));
}
-void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge)
+void pc_cpus_init(PCMachineState *pcms)
{
int i;
X86CPU *cpu = NULL;
- Error *error = NULL;
- unsigned long apic_id_limit;
+ MachineState *machine = MACHINE(pcms);
/* init CPUs */
- if (cpu_model == NULL) {
+ if (machine->cpu_model == NULL) {
#ifdef TARGET_X86_64
- cpu_model = "qemu64";
+ machine->cpu_model = "qemu64";
#else
- cpu_model = "qemu32";
+ machine->cpu_model = "qemu32";
#endif
}
- current_cpu_model = cpu_model;
- apic_id_limit = pc_apic_id_limit(max_cpus);
- if (apic_id_limit > ACPI_CPU_HOTPLUG_ID_LIMIT) {
- error_report("max_cpus is too large. APIC ID of last CPU is %lu",
- apic_id_limit - 1);
+ /* Calculates the limit to CPU APIC ID values
+ *
+ * Limit for the APIC ID value, so that all
+ * CPU APIC IDs are < pcms->apic_id_limit.
+ *
+ * This is used for FW_CFG_MAX_CPUS. See comments on bochs_bios_init().
+ */
+ pcms->apic_id_limit = x86_cpu_apic_id_from_index(max_cpus - 1) + 1;
+ if (pcms->apic_id_limit > ACPI_CPU_HOTPLUG_ID_LIMIT) {
+ error_report("max_cpus is too large. APIC ID of last CPU is %u",
+ pcms->apic_id_limit - 1);
exit(1);
}
- for (i = 0; i < smp_cpus; i++) {
- cpu = pc_new_cpu(cpu_model, x86_cpu_apic_id_from_index(i),
- icc_bridge, &error);
- if (error) {
- error_report_err(error);
- exit(1);
+ pcms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) +
+ sizeof(CPUArchId) * max_cpus);
+ for (i = 0; i < max_cpus; i++) {
+ pcms->possible_cpus->cpus[i].arch_id = x86_cpu_apic_id_from_index(i);
+ pcms->possible_cpus->len++;
+ if (i < smp_cpus) {
+ cpu = pc_new_cpu(machine->cpu_model, x86_cpu_apic_id_from_index(i),
+ &error_fatal);
+ pcms->possible_cpus->cpus[i].cpu = CPU(cpu);
+ object_unref(OBJECT(cpu));
}
- object_unref(OBJECT(cpu));
- }
-
- /* map APIC MMIO area if CPU has APIC */
- if (cpu && cpu->apic_state) {
- /* XXX: what if the base changes? */
- sysbus_mmio_map_overlap(SYS_BUS_DEVICE(icc_bridge), 0,
- APIC_DEFAULT_ADDRESS, 0x1000);
}
/* tell smbios about cpuid version and features */
@@ -1156,18 +1156,12 @@ typedef struct PcRomPciInfo {
uint64_t w64_max;
} PcRomPciInfo;
-typedef struct PcGuestInfoState {
- PcGuestInfo info;
- Notifier machine_done;
-} PcGuestInfoState;
-
static
-void pc_guest_info_machine_done(Notifier *notifier, void *data)
+void pc_machine_done(Notifier *notifier, void *data)
{
- PcGuestInfoState *guest_info_state = container_of(notifier,
- PcGuestInfoState,
- machine_done);
- PCIBus *bus = find_i440fx();
+ PCMachineState *pcms = container_of(notifier,
+ PCMachineState, machine_done);
+ PCIBus *bus = pcms->bus;
if (bus) {
int extra_hosts = 0;
@@ -1178,52 +1172,45 @@ void pc_guest_info_machine_done(Notifier *notifier, void *data)
extra_hosts++;
}
}
- if (extra_hosts && guest_info_state->info.fw_cfg) {
+ if (extra_hosts && pcms->fw_cfg) {
uint64_t *val = g_malloc(sizeof(*val));
*val = cpu_to_le64(extra_hosts);
- fw_cfg_add_file(guest_info_state->info.fw_cfg,
+ fw_cfg_add_file(pcms->fw_cfg,
"etc/extra-pci-roots", val, sizeof(*val));
}
}
- acpi_setup(&guest_info_state->info);
+ acpi_setup();
}
-PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size,
- ram_addr_t above_4g_mem_size)
+void pc_guest_info_init(PCMachineState *pcms)
{
- PcGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state);
- PcGuestInfo *guest_info = &guest_info_state->info;
int i, j;
- guest_info->ram_size_below_4g = below_4g_mem_size;
- guest_info->ram_size = below_4g_mem_size + above_4g_mem_size;
- guest_info->apic_id_limit = pc_apic_id_limit(max_cpus);
- guest_info->apic_xrupt_override = kvm_allows_irq0_override();
- guest_info->numa_nodes = nb_numa_nodes;
- guest_info->node_mem = g_malloc0(guest_info->numa_nodes *
- sizeof *guest_info->node_mem);
+ pcms->apic_xrupt_override = kvm_allows_irq0_override();
+ pcms->numa_nodes = nb_numa_nodes;
+ pcms->node_mem = g_malloc0(pcms->numa_nodes *
+ sizeof *pcms->node_mem);
for (i = 0; i < nb_numa_nodes; i++) {
- guest_info->node_mem[i] = numa_info[i].node_mem;
+ pcms->node_mem[i] = numa_info[i].node_mem;
}
- guest_info->node_cpu = g_malloc0(guest_info->apic_id_limit *
- sizeof *guest_info->node_cpu);
+ pcms->node_cpu = g_malloc0(pcms->apic_id_limit *
+ sizeof *pcms->node_cpu);
for (i = 0; i < max_cpus; i++) {
unsigned int apic_id = x86_cpu_apic_id_from_index(i);
- assert(apic_id < guest_info->apic_id_limit);
+ assert(apic_id < pcms->apic_id_limit);
for (j = 0; j < nb_numa_nodes; j++) {
if (test_bit(i, numa_info[j].node_cpu)) {
- guest_info->node_cpu[apic_id] = j;
+ pcms->node_cpu[apic_id] = j;
break;
}
}
}
- guest_info_state->machine_done.notify = pc_guest_info_machine_done;
- qemu_add_machine_init_done_notifier(&guest_info_state->machine_done);
- return guest_info;
+ pcms->machine_done.notify = pc_machine_done;
+ qemu_add_machine_init_done_notifier(&pcms->machine_done);
}
/* setup pci memory address space mapping into system address space */
@@ -1256,54 +1243,46 @@ void pc_acpi_init(const char *default_dsdt)
acpi_table_add_builtin(opts, &err);
if (err) {
- error_report("WARNING: failed to load %s: %s", filename,
- error_get_pretty(err));
- error_free(err);
+ error_reportf_err(err, "WARNING: failed to load %s: ",
+ filename);
}
g_free(filename);
}
}
-FWCfgState *xen_load_linux(const char *kernel_filename,
- const char *kernel_cmdline,
- const char *initrd_filename,
- ram_addr_t below_4g_mem_size,
- PcGuestInfo *guest_info)
+void xen_load_linux(PCMachineState *pcms)
{
int i;
FWCfgState *fw_cfg;
- assert(kernel_filename != NULL);
+ assert(MACHINE(pcms)->kernel_filename != NULL);
- fw_cfg = fw_cfg_init_io(BIOS_CFG_IOPORT);
+ fw_cfg = fw_cfg_init_io(FW_CFG_IO_BASE);
rom_set_fw(fw_cfg);
- load_linux(fw_cfg, kernel_filename, initrd_filename,
- kernel_cmdline, below_4g_mem_size);
+ load_linux(pcms, fw_cfg);
for (i = 0; i < nb_option_roms; i++) {
assert(!strcmp(option_rom[i].name, "linuxboot.bin") ||
!strcmp(option_rom[i].name, "multiboot.bin"));
rom_add_option(option_rom[i].name, option_rom[i].bootindex);
}
- guest_info->fw_cfg = fw_cfg;
- return fw_cfg;
+ pcms->fw_cfg = fw_cfg;
}
-FWCfgState *pc_memory_init(MachineState *machine,
- MemoryRegion *system_memory,
- ram_addr_t below_4g_mem_size,
- ram_addr_t above_4g_mem_size,
- MemoryRegion *rom_memory,
- MemoryRegion **ram_memory,
- PcGuestInfo *guest_info)
+void pc_memory_init(PCMachineState *pcms,
+ MemoryRegion *system_memory,
+ MemoryRegion *rom_memory,
+ MemoryRegion **ram_memory)
{
int linux_boot, i;
MemoryRegion *ram, *option_rom_mr;
MemoryRegion *ram_below_4g, *ram_above_4g;
FWCfgState *fw_cfg;
- PCMachineState *pcms = PC_MACHINE(machine);
+ MachineState *machine = MACHINE(pcms);
+ PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
- assert(machine->ram_size == below_4g_mem_size + above_4g_mem_size);
+ assert(machine->ram_size == pcms->below_4g_mem_size +
+ pcms->above_4g_mem_size);
linux_boot = (machine->kernel_filename != NULL);
@@ -1317,19 +1296,20 @@ FWCfgState *pc_memory_init(MachineState *machine,
*ram_memory = ram;
ram_below_4g = g_malloc(sizeof(*ram_below_4g));
memory_region_init_alias(ram_below_4g, NULL, "ram-below-4g", ram,
- 0, below_4g_mem_size);
+ 0, pcms->below_4g_mem_size);
memory_region_add_subregion(system_memory, 0, ram_below_4g);
- e820_add_entry(0, below_4g_mem_size, E820_RAM);
- if (above_4g_mem_size > 0) {
+ e820_add_entry(0, pcms->below_4g_mem_size, E820_RAM);
+ if (pcms->above_4g_mem_size > 0) {
ram_above_4g = g_malloc(sizeof(*ram_above_4g));
memory_region_init_alias(ram_above_4g, NULL, "ram-above-4g", ram,
- below_4g_mem_size, above_4g_mem_size);
+ pcms->below_4g_mem_size,
+ pcms->above_4g_mem_size);
memory_region_add_subregion(system_memory, 0x100000000ULL,
ram_above_4g);
- e820_add_entry(0x100000000ULL, above_4g_mem_size, E820_RAM);
+ e820_add_entry(0x100000000ULL, pcms->above_4g_mem_size, E820_RAM);
}
- if (!guest_info->has_reserved_memory &&
+ if (!pcmc->has_reserved_memory &&
(machine->ram_slots ||
(machine->maxram_size > machine->ram_size))) {
MachineClass *mc = MACHINE_GET_CLASS(machine);
@@ -1340,7 +1320,7 @@ FWCfgState *pc_memory_init(MachineState *machine,
}
/* initialize hotplug memory address space */
- if (guest_info->has_reserved_memory &&
+ if (pcmc->has_reserved_memory &&
(machine->ram_size < machine->maxram_size)) {
ram_addr_t hotplug_mem_size =
machine->maxram_size - machine->ram_size;
@@ -1359,9 +1339,9 @@ FWCfgState *pc_memory_init(MachineState *machine,
}
pcms->hotplug_memory.base =
- ROUND_UP(0x100000000ULL + above_4g_mem_size, 1ULL << 30);
+ ROUND_UP(0x100000000ULL + pcms->above_4g_mem_size, 1ULL << 30);
- if (pcms->enforce_aligned_dimm) {
+ if (pcmc->enforce_aligned_dimm) {
/* size hotplug region assuming 1G page max alignment per slot */
hotplug_mem_size += (1ULL << 30) * machine->ram_slots;
}
@@ -1380,36 +1360,41 @@ FWCfgState *pc_memory_init(MachineState *machine,
}
/* Initialize PC system firmware */
- pc_system_firmware_init(rom_memory, guest_info->isapc_ram_fw);
+ pc_system_firmware_init(rom_memory, !pcmc->pci_enabled);
option_rom_mr = g_malloc(sizeof(*option_rom_mr));
memory_region_init_ram(option_rom_mr, NULL, "pc.rom", PC_ROM_SIZE,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(option_rom_mr);
memory_region_add_subregion_overlap(rom_memory,
PC_ROM_MIN_VGA,
option_rom_mr,
1);
- fw_cfg = bochs_bios_init();
+ fw_cfg = bochs_bios_init(&address_space_memory, pcms);
+
rom_set_fw(fw_cfg);
- if (guest_info->has_reserved_memory && pcms->hotplug_memory.base) {
+ if (pcmc->has_reserved_memory && pcms->hotplug_memory.base) {
uint64_t *val = g_malloc(sizeof(*val));
- *val = cpu_to_le64(ROUND_UP(pcms->hotplug_memory.base, 0x1ULL << 30));
+ PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
+ uint64_t res_mem_end = pcms->hotplug_memory.base;
+
+ if (!pcmc->broken_reserved_end) {
+ res_mem_end += memory_region_size(&pcms->hotplug_memory.mr);
+ }
+ *val = cpu_to_le64(ROUND_UP(res_mem_end, 0x1ULL << 30));
fw_cfg_add_file(fw_cfg, "etc/reserved-memory-end", val, sizeof(*val));
}
if (linux_boot) {
- load_linux(fw_cfg, machine->kernel_filename, machine->initrd_filename,
- machine->kernel_cmdline, below_4g_mem_size);
+ load_linux(pcms, fw_cfg);
}
for (i = 0; i < nb_option_roms; i++) {
rom_add_option(option_rom[i].name, option_rom[i].bootindex);
}
- guest_info->fw_cfg = fw_cfg;
- return fw_cfg;
+ pcms->fw_cfg = fw_cfg;
}
qemu_irq pc_allocate_cpu_irq(void)
@@ -1421,6 +1406,7 @@ DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus)
{
DeviceState *dev = NULL;
+ rom_set_order_override(FW_CFG_ORDER_OVERRIDE_VGA);
if (pci_bus) {
PCIDevice *pcidev = pci_vga_init(pci_bus);
dev = pcidev ? &pcidev->qdev : NULL;
@@ -1428,18 +1414,10 @@ DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus)
ISADevice *isadev = isa_vga_init(isa_bus);
dev = isadev ? DEVICE(isadev) : NULL;
}
+ rom_reset_order_override();
return dev;
}
-static void cpu_request_exit(void *opaque, int irq, int level)
-{
- CPUState *cpu = current_cpu;
-
- if (cpu && level) {
- cpu_exit(cpu);
- }
-}
-
static const MemoryRegionOps ioport80_io_ops = {
.write = ioport80_write,
.read = ioport80_read,
@@ -1464,7 +1442,7 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
ISADevice **rtc_state,
bool create_fdctrl,
bool no_vmport,
- uint32 hpet_irqs)
+ uint32_t hpet_irqs)
{
int i;
DriveInfo *fd[MAX_FD];
@@ -1474,7 +1452,6 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
qemu_irq rtc_irq = NULL;
qemu_irq *a20_line;
ISADevice *i8042, *port92, *vmmouse, *pit = NULL;
- qemu_irq *cpu_exit_irq;
MemoryRegion *ioport80_io = g_new(MemoryRegion, 1);
MemoryRegion *ioportF0_io = g_new(MemoryRegion, 1);
@@ -1519,7 +1496,7 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
qemu_register_boot_set(pc_boot_set, *rtc_state);
if (!xen_enabled()) {
- if (kvm_irqchip_in_kernel()) {
+ if (kvm_pit_in_kernel()) {
pit = kvm_pit_init(isa_bus, 0x40);
} else {
pit = pit_init(isa_bus, 0x40, pit_isa_irq, pit_alt_irq);
@@ -1551,8 +1528,7 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
port92 = isa_create_simple(isa_bus, "port92");
port92_init(port92, &a20_line[1]);
- cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1);
- DMA_init(0, cpu_exit_irq);
+ DMA_init(isa_bus, 0);
for(i = 0; i < MAX_FD; i++) {
fd[i] = drive_get(IF_FLOPPY, 0, i);
@@ -1567,6 +1543,7 @@ void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus)
{
int i;
+ rom_set_order_override(FW_CFG_ORDER_OVERRIDE_NIC);
for (i = 0; i < nb_nics; i++) {
NICInfo *nd = &nd_table[i];
@@ -1576,6 +1553,7 @@ void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus)
pci_nic_init_nofail(nd, pci_bus, "e1000", NULL);
}
}
+ rom_reset_order_override();
}
void pc_pci_device_init(PCIBus *pci_bus)
@@ -1595,7 +1573,7 @@ void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name)
SysBusDevice *d;
unsigned int i;
- if (kvm_irqchip_in_kernel()) {
+ if (kvm_ioapic_in_kernel()) {
dev = qdev_create(NULL, "kvm-ioapic");
} else {
dev = qdev_create(NULL, "ioapic");
@@ -1619,12 +1597,13 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev,
HotplugHandlerClass *hhc;
Error *local_err = NULL;
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
+ PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
PCDIMMDevice *dimm = PC_DIMM(dev);
PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
MemoryRegion *mr = ddc->get_memory_region(dimm);
uint64_t align = TARGET_PAGE_SIZE;
- if (memory_region_get_alignment(mr) && pcms->enforce_aligned_dimm) {
+ if (memory_region_get_alignment(mr) && pcmc->enforce_aligned_dimm) {
align = memory_region_get_alignment(mr);
}
@@ -1689,9 +1668,19 @@ static void pc_dimm_unplug(HotplugHandler *hotplug_dev,
error_propagate(errp, local_err);
}
+static int pc_apic_cmp(const void *a, const void *b)
+{
+ CPUArchId *apic_a = (CPUArchId *)a;
+ CPUArchId *apic_b = (CPUArchId *)b;
+
+ return apic_a->arch_id - apic_b->arch_id;
+}
+
static void pc_cpu_plug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
+ CPUClass *cc = CPU_GET_CLASS(dev);
+ CPUArchId apic_id, *found_cpu;
HotplugHandlerClass *hhc;
Error *local_err = NULL;
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
@@ -1714,6 +1703,13 @@ static void pc_cpu_plug(HotplugHandler *hotplug_dev,
/* increment the number of CPUs */
rtc_set_memory(pcms->rtc, 0x5f, rtc_get_memory(pcms->rtc, 0x5f) + 1);
+
+ apic_id.arch_id = cc->get_arch_id(CPU(dev));
+ found_cpu = bsearch(&apic_id, pcms->possible_cpus->cpus,
+ pcms->possible_cpus->len, sizeof(*pcms->possible_cpus->cpus),
+ pc_apic_cmp);
+ assert(found_cpu);
+ found_cpu->cpu = CPU(dev);
out:
error_propagate(errp, local_err);
}
@@ -1765,42 +1761,43 @@ static HotplugHandler *pc_get_hotpug_handler(MachineState *machine,
}
static void
-pc_machine_get_hotplug_memory_region_size(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+pc_machine_get_hotplug_memory_region_size(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
{
PCMachineState *pcms = PC_MACHINE(obj);
int64_t value = memory_region_size(&pcms->hotplug_memory.mr);
- visit_type_int(v, &value, name, errp);
+ visit_type_int(v, name, &value, errp);
}
static void pc_machine_get_max_ram_below_4g(Object *obj, Visitor *v,
- void *opaque, const char *name,
- Error **errp)
+ const char *name, void *opaque,
+ Error **errp)
{
PCMachineState *pcms = PC_MACHINE(obj);
uint64_t value = pcms->max_ram_below_4g;
- visit_type_size(v, &value, name, errp);
+ visit_type_size(v, name, &value, errp);
}
static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v,
- void *opaque, const char *name,
- Error **errp)
+ const char *name, void *opaque,
+ Error **errp)
{
PCMachineState *pcms = PC_MACHINE(obj);
Error *error = NULL;
uint64_t value;
- visit_type_size(v, &value, name, &error);
+ visit_type_size(v, name, &value, &error);
if (error) {
error_propagate(errp, error);
return;
}
if (value > (1ULL << 32)) {
- error_set(&error, ERROR_CLASS_GENERIC_ERROR,
- "Machine option 'max-ram-below-4g=%"PRIu64
- "' expects size less than or equal to 4G", value);
+ error_setg(&error,
+ "Machine option 'max-ram-below-4g=%"PRIu64
+ "' expects size less than or equal to 4G", value);
error_propagate(errp, error);
return;
}
@@ -1814,21 +1811,21 @@ static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v,
pcms->max_ram_below_4g = value;
}
-static void pc_machine_get_vmport(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void pc_machine_get_vmport(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
PCMachineState *pcms = PC_MACHINE(obj);
OnOffAuto vmport = pcms->vmport;
- visit_type_OnOffAuto(v, &vmport, name, errp);
+ visit_type_OnOffAuto(v, name, &vmport, errp);
}
-static void pc_machine_set_vmport(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void pc_machine_set_vmport(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
PCMachineState *pcms = PC_MACHINE(obj);
- visit_type_OnOffAuto(v, &pcms->vmport, name, errp);
+ visit_type_OnOffAuto(v, name, &pcms->vmport, errp);
}
bool pc_machine_is_smm_enabled(PCMachineState *pcms)
@@ -1856,28 +1853,35 @@ bool pc_machine_is_smm_enabled(PCMachineState *pcms)
return false;
}
-static void pc_machine_get_smm(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void pc_machine_get_smm(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
PCMachineState *pcms = PC_MACHINE(obj);
OnOffAuto smm = pcms->smm;
- visit_type_OnOffAuto(v, &smm, name, errp);
+ visit_type_OnOffAuto(v, name, &smm, errp);
+}
+
+static void pc_machine_set_smm(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ PCMachineState *pcms = PC_MACHINE(obj);
+
+ visit_type_OnOffAuto(v, name, &pcms->smm, errp);
}
-static void pc_machine_set_smm(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static bool pc_machine_get_nvdimm(Object *obj, Error **errp)
{
PCMachineState *pcms = PC_MACHINE(obj);
- visit_type_OnOffAuto(v, &pcms->smm, name, errp);
+ return pcms->acpi_nvdimm_state.is_enabled;
}
-static bool pc_machine_get_aligned_dimm(Object *obj, Error **errp)
+static void pc_machine_set_nvdimm(Object *obj, bool value, Error **errp)
{
PCMachineState *pcms = PC_MACHINE(obj);
- return pcms->enforce_aligned_dimm;
+ pcms->acpi_nvdimm_state.is_enabled = value;
}
static void pc_machine_initfn(Object *obj)
@@ -1886,47 +1890,77 @@ static void pc_machine_initfn(Object *obj)
object_property_add(obj, PC_MACHINE_MEMHP_REGION_SIZE, "int",
pc_machine_get_hotplug_memory_region_size,
- NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, &error_abort);
pcms->max_ram_below_4g = 1ULL << 32; /* 4G */
object_property_add(obj, PC_MACHINE_MAX_RAM_BELOW_4G, "size",
pc_machine_get_max_ram_below_4g,
pc_machine_set_max_ram_below_4g,
- NULL, NULL, NULL);
+ NULL, NULL, &error_abort);
object_property_set_description(obj, PC_MACHINE_MAX_RAM_BELOW_4G,
"Maximum ram below the 4G boundary (32bit boundary)",
- NULL);
+ &error_abort);
pcms->smm = ON_OFF_AUTO_AUTO;
object_property_add(obj, PC_MACHINE_SMM, "OnOffAuto",
pc_machine_get_smm,
pc_machine_set_smm,
- NULL, NULL, NULL);
+ NULL, NULL, &error_abort);
object_property_set_description(obj, PC_MACHINE_SMM,
"Enable SMM (pc & q35)",
- NULL);
+ &error_abort);
pcms->vmport = ON_OFF_AUTO_AUTO;
object_property_add(obj, PC_MACHINE_VMPORT, "OnOffAuto",
pc_machine_get_vmport,
pc_machine_set_vmport,
- NULL, NULL, NULL);
+ NULL, NULL, &error_abort);
object_property_set_description(obj, PC_MACHINE_VMPORT,
"Enable vmport (pc & q35)",
- NULL);
+ &error_abort);
+
+ /* nvdimm is disabled on default. */
+ pcms->acpi_nvdimm_state.is_enabled = false;
+ object_property_add_bool(obj, PC_MACHINE_NVDIMM, pc_machine_get_nvdimm,
+ pc_machine_set_nvdimm, &error_abort);
+}
+
+static void pc_machine_reset(void)
+{
+ CPUState *cs;
+ X86CPU *cpu;
+
+ qemu_devices_reset();
+
+ /* Reset APIC after devices have been reset to cancel
+ * any changes that qemu_devices_reset() might have done.
+ */
+ CPU_FOREACH(cs) {
+ cpu = X86_CPU(cs);
- pcms->enforce_aligned_dimm = true;
- object_property_add_bool(obj, PC_MACHINE_ENFORCE_ALIGNED_DIMM,
- pc_machine_get_aligned_dimm,
- NULL, NULL);
+ if (cpu->apic_state) {
+ device_reset(cpu->apic_state);
+ }
+ }
}
static unsigned pc_cpu_index_to_socket_id(unsigned cpu_index)
{
- unsigned pkg_id, core_id, smt_id;
+ X86CPUTopoInfo topo;
x86_topo_ids_from_idx(smp_cores, smp_threads, cpu_index,
- &pkg_id, &core_id, &smt_id);
- return pkg_id;
+ &topo);
+ return topo.pkg_id;
+}
+
+static CPUArchIdList *pc_possible_cpu_arch_ids(MachineState *machine)
+{
+ PCMachineState *pcms = PC_MACHINE(machine);
+ int len = sizeof(CPUArchIdList) +
+ sizeof(CPUArchId) * (pcms->possible_cpus->len);
+ CPUArchIdList *list = g_malloc(len);
+
+ memcpy(list, pcms->possible_cpus, len);
+ return list;
}
static void pc_machine_class_init(ObjectClass *oc, void *data)
@@ -1936,8 +1970,26 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
pcmc->get_hotplug_handler = mc->get_hotplug_handler;
+ pcmc->pci_enabled = true;
+ pcmc->has_acpi_build = true;
+ pcmc->rsdp_in_ram = true;
+ pcmc->smbios_defaults = true;
+ pcmc->smbios_uuid_encoded = true;
+ pcmc->gigabyte_align = true;
+ pcmc->has_reserved_memory = true;
+ pcmc->kvmclock_enabled = true;
+ pcmc->enforce_aligned_dimm = true;
+ /* BIOS ACPI tables: 128K. Other BIOS datastructures: less than 4K reported
+ * to be used at the moment, 32K should be enough for a while. */
+ pcmc->acpi_data_size = 0x20000 + 0x8000;
+ pcmc->save_tsc_khz = true;
mc->get_hotplug_handler = pc_get_hotpug_handler;
mc->cpu_index_to_socket_id = pc_cpu_index_to_socket_id;
+ mc->possible_cpu_arch_ids = pc_possible_cpu_arch_ids;
+ mc->default_boot_order = "cad";
+ mc->hot_add_cpu = pc_hot_add_cpu;
+ mc->max_cpus = 255;
+ mc->reset = pc_machine_reset;
hc->plug = pc_machine_device_plug_cb;
hc->unplug_request = pc_machine_device_unplug_request_cb;
hc->unplug = pc_machine_device_unplug_cb;
diff --git a/qemu/hw/i386/pc_piix.c b/qemu/hw/i386/pc_piix.c
index a896624f8..7f50116bc 100644
--- a/qemu/hw/i386/pc_piix.c
+++ b/qemu/hw/i386/pc_piix.c
@@ -22,13 +22,14 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include <glib.h>
#include "hw/hw.h"
#include "hw/loader.h"
#include "hw/i386/pc.h"
#include "hw/i386/apic.h"
-#include "hw/i386/smbios.h"
+#include "hw/smbios/smbios.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_ids.h"
#include "hw/usb.h"
@@ -39,7 +40,6 @@
#include "hw/kvm/clock.h"
#include "sysemu/sysemu.h"
#include "hw/sysbus.h"
-#include "hw/cpu/icc_bus.h"
#include "sysemu/arch_init.h"
#include "sysemu/block-backend.h"
#include "hw/i2c/smbus.h"
@@ -50,9 +50,11 @@
#include "cpu.h"
#include "qemu/error-report.h"
#ifdef CONFIG_XEN
-# include <xen/hvm/hvm_info_table.h>
+#include <xen/hvm/hvm_info_table.h>
+#include "hw/xen/xen_pt.h"
#endif
#include "migration/migration.h"
+#include "kvm_i386.h"
#define MAX_IDE_BUS 2
@@ -60,29 +62,15 @@ static const int ide_iobase[MAX_IDE_BUS] = { 0x1f0, 0x170 };
static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 };
static const int ide_irq[MAX_IDE_BUS] = { 14, 15 };
-static bool pci_enabled = true;
-static bool has_acpi_build = true;
-static bool rsdp_in_ram = true;
-static int legacy_acpi_table_size;
-static bool smbios_defaults = true;
-static bool smbios_legacy_mode;
-static bool smbios_uuid_encoded = true;
-/* Make sure that guest addresses aligned at 1Gbyte boundaries get mapped to
- * host addresses aligned at 1Gbyte boundaries. This way we can use 1GByte
- * pages in the host.
- */
-static bool gigabyte_align = true;
-static bool has_reserved_memory = true;
-static bool kvmclock_enabled = true;
-
/* PC hardware initialisation */
-static void pc_init1(MachineState *machine)
+static void pc_init1(MachineState *machine,
+ const char *host_type, const char *pci_type)
{
- PCMachineState *pc_machine = PC_MACHINE(machine);
+ PCMachineState *pcms = PC_MACHINE(machine);
+ PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
MemoryRegion *system_memory = get_system_memory();
MemoryRegion *system_io = get_system_io();
int i;
- ram_addr_t below_4g_mem_size, above_4g_mem_size;
PCIBus *pci_bus;
ISABus *isa_bus;
PCII440FXState *i440fx_state;
@@ -97,8 +85,6 @@ static void pc_init1(MachineState *machine)
MemoryRegion *ram_memory;
MemoryRegion *pci_memory;
MemoryRegion *rom_memory;
- DeviceState *icc_bridge;
- PcGuestInfo *guest_info;
ram_addr_t lowmem;
/* Check whether RAM fits below 4G (leaving 1/2 GByte for IO memory).
@@ -109,7 +95,7 @@ static void pc_init1(MachineState *machine)
* breaking migration.
*/
if (machine->ram_size >= 0xe0000000) {
- lowmem = gigabyte_align ? 0xc0000000 : 0xe0000000;
+ lowmem = pcmc->gigabyte_align ? 0xc0000000 : 0xe0000000;
} else {
lowmem = 0xe0000000;
}
@@ -117,41 +103,35 @@ static void pc_init1(MachineState *machine)
/* Handle the machine opt max-ram-below-4g. It is basically doing
* min(qemu limit, user limit).
*/
- if (lowmem > pc_machine->max_ram_below_4g) {
- lowmem = pc_machine->max_ram_below_4g;
+ if (lowmem > pcms->max_ram_below_4g) {
+ lowmem = pcms->max_ram_below_4g;
if (machine->ram_size - lowmem > lowmem &&
lowmem & ((1ULL << 30) - 1)) {
error_report("Warning: Large machine and max_ram_below_4g(%"PRIu64
") not a multiple of 1G; possible bad performance.",
- pc_machine->max_ram_below_4g);
+ pcms->max_ram_below_4g);
}
}
if (machine->ram_size >= lowmem) {
- above_4g_mem_size = machine->ram_size - lowmem;
- below_4g_mem_size = lowmem;
+ pcms->above_4g_mem_size = machine->ram_size - lowmem;
+ pcms->below_4g_mem_size = lowmem;
} else {
- above_4g_mem_size = 0;
- below_4g_mem_size = machine->ram_size;
+ pcms->above_4g_mem_size = 0;
+ pcms->below_4g_mem_size = machine->ram_size;
}
- if (xen_enabled() && xen_hvm_init(&below_4g_mem_size, &above_4g_mem_size,
- &ram_memory) != 0) {
- fprintf(stderr, "xen hardware virtual machine initialisation failed\n");
- exit(1);
+ if (xen_enabled()) {
+ xen_hvm_init(pcms, &ram_memory);
}
- icc_bridge = qdev_create(NULL, TYPE_ICC_BRIDGE);
- object_property_add_child(qdev_get_machine(), "icc-bridge",
- OBJECT(icc_bridge), NULL);
-
- pc_cpus_init(machine->cpu_model, icc_bridge);
+ pc_cpus_init(pcms);
- if (kvm_enabled() && kvmclock_enabled) {
+ if (kvm_enabled() && pcmc->kvmclock_enabled) {
kvmclock_create();
}
- if (pci_enabled) {
+ if (pcmc->pci_enabled) {
pci_memory = g_new(MemoryRegion, 1);
memory_region_init(pci_memory, NULL, "pci", UINT64_MAX);
rom_memory = pci_memory;
@@ -160,60 +140,54 @@ static void pc_init1(MachineState *machine)
rom_memory = system_memory;
}
- guest_info = pc_guest_info_init(below_4g_mem_size, above_4g_mem_size);
-
- guest_info->has_acpi_build = has_acpi_build;
- guest_info->legacy_acpi_table_size = legacy_acpi_table_size;
+ pc_guest_info_init(pcms);
- guest_info->isapc_ram_fw = !pci_enabled;
- guest_info->has_reserved_memory = has_reserved_memory;
- guest_info->rsdp_in_ram = rsdp_in_ram;
-
- if (smbios_defaults) {
+ if (pcmc->smbios_defaults) {
MachineClass *mc = MACHINE_GET_CLASS(machine);
/* These values are guest ABI, do not change */
smbios_set_defaults("QEMU", "Standard PC (i440FX + PIIX, 1996)",
- mc->name, smbios_legacy_mode, smbios_uuid_encoded);
+ mc->name, pcmc->smbios_legacy_mode,
+ pcmc->smbios_uuid_encoded,
+ SMBIOS_ENTRY_POINT_21);
}
/* allocate ram and load rom/bios */
if (!xen_enabled()) {
- pc_memory_init(machine, system_memory,
- below_4g_mem_size, above_4g_mem_size,
- rom_memory, &ram_memory, guest_info);
+ pc_memory_init(pcms, system_memory,
+ rom_memory, &ram_memory);
} else if (machine->kernel_filename != NULL) {
/* For xen HVM direct kernel boot, load linux here */
- xen_load_linux(machine->kernel_filename,
- machine->kernel_cmdline,
- machine->initrd_filename,
- below_4g_mem_size,
- guest_info);
+ xen_load_linux(pcms);
}
gsi_state = g_malloc0(sizeof(*gsi_state));
- if (kvm_irqchip_in_kernel()) {
- kvm_pc_setup_irq_routing(pci_enabled);
+ if (kvm_ioapic_in_kernel()) {
+ kvm_pc_setup_irq_routing(pcmc->pci_enabled);
gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state,
GSI_NUM_PINS);
} else {
gsi = qemu_allocate_irqs(gsi_handler, gsi_state, GSI_NUM_PINS);
}
- if (pci_enabled) {
- pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, &isa_bus, gsi,
+ if (pcmc->pci_enabled) {
+ pci_bus = i440fx_init(host_type,
+ pci_type,
+ &i440fx_state, &piix3_devfn, &isa_bus, gsi,
system_memory, system_io, machine->ram_size,
- below_4g_mem_size,
- above_4g_mem_size,
+ pcms->below_4g_mem_size,
+ pcms->above_4g_mem_size,
pci_memory, ram_memory);
+ pcms->bus = pci_bus;
} else {
pci_bus = NULL;
i440fx_state = NULL;
- isa_bus = isa_bus_new(NULL, get_system_memory(), system_io);
+ isa_bus = isa_bus_new(NULL, get_system_memory(), system_io,
+ &error_abort);
no_hpet = 1;
}
isa_bus_irqs(isa_bus, gsi);
- if (kvm_irqchip_in_kernel()) {
+ if (kvm_pic_in_kernel()) {
i8259 = kvm_i8259_init(isa_bus);
} else if (xen_enabled()) {
i8259 = xen_interrupt_controller_init();
@@ -225,28 +199,27 @@ static void pc_init1(MachineState *machine)
gsi_state->i8259_irq[i] = i8259[i];
}
g_free(i8259);
- if (pci_enabled) {
+ if (pcmc->pci_enabled) {
ioapic_init_gsi(gsi_state, "i440fx");
}
- qdev_init_nofail(icc_bridge);
pc_register_ferr_irq(gsi[13]);
- pc_vga_init(isa_bus, pci_enabled ? pci_bus : NULL);
+ pc_vga_init(isa_bus, pcmc->pci_enabled ? pci_bus : NULL);
- assert(pc_machine->vmport != ON_OFF_AUTO_MAX);
- if (pc_machine->vmport == ON_OFF_AUTO_AUTO) {
- pc_machine->vmport = xen_enabled() ? ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON;
+ assert(pcms->vmport != ON_OFF_AUTO__MAX);
+ if (pcms->vmport == ON_OFF_AUTO_AUTO) {
+ pcms->vmport = xen_enabled() ? ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON;
}
/* init basic PC hardware */
pc_basic_device_init(isa_bus, gsi, &rtc_state, true,
- (pc_machine->vmport != ON_OFF_AUTO_ON), 0x4);
+ (pcms->vmport != ON_OFF_AUTO_ON), 0x4);
pc_nic_init(isa_bus, pci_bus);
ide_drive_get(hd, ARRAY_SIZE(hd));
- if (pci_enabled) {
+ if (pcmc->pci_enabled) {
PCIDevice *dev;
if (xen_enabled()) {
dev = pci_piix3_xen_ide_init(pci_bus, hd, piix3_devfn + 1);
@@ -271,14 +244,13 @@ static void pc_init1(MachineState *machine)
}
}
- pc_cmos_init(below_4g_mem_size, above_4g_mem_size, machine->boot_order,
- machine, idebus[0], idebus[1], rtc_state);
+ pc_cmos_init(pcms, idebus[0], idebus[1], rtc_state);
- if (pci_enabled && usb_enabled()) {
+ if (pcmc->pci_enabled && usb_enabled()) {
pci_create_simple(pci_bus, piix3_devfn + 2, "piix3-usb-uhci");
}
- if (pci_enabled && acpi_enabled) {
+ if (pcmc->pci_enabled && acpi_enabled) {
DeviceState *piix4_pm;
I2CBus *smbus;
@@ -286,24 +258,36 @@ static void pc_init1(MachineState *machine)
/* TODO: Populate SPD eeprom data. */
smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100,
gsi[9], smi_irq,
- pc_machine_is_smm_enabled(pc_machine),
+ pc_machine_is_smm_enabled(pcms),
&piix4_pm);
smbus_eeprom_init(smbus, 8, NULL, 0);
object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP,
TYPE_HOTPLUG_HANDLER,
- (Object **)&pc_machine->acpi_dev,
+ (Object **)&pcms->acpi_dev,
object_property_allow_set_link,
OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort);
object_property_set_link(OBJECT(machine), OBJECT(piix4_pm),
PC_MACHINE_ACPI_DEVICE_PROP, &error_abort);
}
- if (pci_enabled) {
+ if (pcmc->pci_enabled) {
pc_pci_device_init(pci_bus);
}
+
+ if (pcms->acpi_nvdimm_state.is_enabled) {
+ nvdimm_init_acpi_state(&pcms->acpi_nvdimm_state, system_io,
+ pcms->fw_cfg, OBJECT(pcms));
+ }
}
+/* Looking for a pc_compat_2_4() function? It doesn't exist.
+ * pc_compat_*() functions that run on machine-init time and
+ * change global QEMU state are deprecated. Please don't create
+ * one, and implement any pc-*-2.4 (and newer) compat code in
+ * HW_COMPAT_*, PC_COMPAT_*, or * pc_*_machine_options().
+ */
+
static void pc_compat_2_3(MachineState *machine)
{
PCMachineState *pcms = PC_MACHINE(machine);
@@ -318,80 +302,29 @@ static void pc_compat_2_3(MachineState *machine)
static void pc_compat_2_2(MachineState *machine)
{
pc_compat_2_3(machine);
- rsdp_in_ram = false;
- x86_cpu_compat_set_features("kvm64", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("kvm32", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Conroe", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Penryn", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Nehalem", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Westmere", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("SandyBridge", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Haswell", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Broadwell", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Opteron_G1", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Opteron_G2", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Opteron_G3", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Opteron_G4", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Opteron_G5", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Haswell", FEAT_1_ECX, 0, CPUID_EXT_F16C);
- x86_cpu_compat_set_features("Haswell", FEAT_1_ECX, 0, CPUID_EXT_RDRAND);
- x86_cpu_compat_set_features("Broadwell", FEAT_1_ECX, 0, CPUID_EXT_F16C);
- x86_cpu_compat_set_features("Broadwell", FEAT_1_ECX, 0, CPUID_EXT_RDRAND);
machine->suppress_vmdesc = true;
}
static void pc_compat_2_1(MachineState *machine)
{
- PCMachineState *pcms = PC_MACHINE(machine);
-
pc_compat_2_2(machine);
- smbios_uuid_encoded = false;
- x86_cpu_compat_set_features("coreduo", FEAT_1_ECX, CPUID_EXT_VMX, 0);
- x86_cpu_compat_set_features("core2duo", FEAT_1_ECX, CPUID_EXT_VMX, 0);
- x86_cpu_compat_kvm_no_autodisable(FEAT_8000_0001_ECX, CPUID_EXT3_SVM);
- pcms->enforce_aligned_dimm = false;
+ x86_cpu_change_kvm_default("svm", NULL);
}
static void pc_compat_2_0(MachineState *machine)
{
pc_compat_2_1(machine);
- /* This value depends on the actual DSDT and SSDT compiled into
- * the source QEMU; unfortunately it depends on the binary and
- * not on the machine type, so we cannot make pc-i440fx-1.7 work on
- * both QEMU 1.7 and QEMU 2.0.
- *
- * Large variations cause migration to fail for more than one
- * consecutive value of the "-smp" maxcpus option.
- *
- * For small variations of the kind caused by different iasl versions,
- * the 4k rounding usually leaves slack. However, there could be still
- * one or two values that break. For QEMU 1.7 and QEMU 2.0 the
- * slack is only ~10 bytes before one "-smp maxcpus" value breaks!
- *
- * 6652 is valid for QEMU 2.0, the right value for pc-i440fx-1.7 on
- * QEMU 1.7 it is 6414. For RHEL/CentOS 7.0 it is 6418.
- */
- legacy_acpi_table_size = 6652;
- smbios_legacy_mode = true;
- has_reserved_memory = false;
- pc_set_legacy_acpi_data_size();
}
static void pc_compat_1_7(MachineState *machine)
{
pc_compat_2_0(machine);
- smbios_defaults = false;
- gigabyte_align = false;
- option_rom_has_mr = true;
- legacy_acpi_table_size = 6414;
- x86_cpu_compat_kvm_no_autoenable(FEAT_1_ECX, CPUID_EXT_X2APIC);
+ x86_cpu_change_kvm_default("x2apic", NULL);
}
static void pc_compat_1_6(MachineState *machine)
{
pc_compat_1_7(machine);
- rom_file_has_mr = false;
- has_acpi_build = false;
}
static void pc_compat_1_5(MachineState *machine)
@@ -402,8 +335,6 @@ static void pc_compat_1_5(MachineState *machine)
static void pc_compat_1_4(MachineState *machine)
{
pc_compat_1_5(machine);
- x86_cpu_compat_set_features("n270", FEAT_1_ECX, 0, CPUID_EXT_MOVBE);
- x86_cpu_compat_set_features("Westmere", FEAT_1_ECX, 0, CPUID_EXT_PCLMULQDQ);
}
static void pc_compat_1_3(MachineState *machine)
@@ -416,40 +347,46 @@ static void pc_compat_1_3(MachineState *machine)
static void pc_compat_1_2(MachineState *machine)
{
pc_compat_1_3(machine);
- x86_cpu_compat_kvm_no_autoenable(FEAT_KVM, 1 << KVM_FEATURE_PV_EOI);
+ x86_cpu_change_kvm_default("kvm-pv-eoi", NULL);
}
/* PC compat function for pc-0.10 to pc-0.13 */
static void pc_compat_0_13(MachineState *machine)
{
pc_compat_1_2(machine);
- kvmclock_enabled = false;
}
static void pc_init_isa(MachineState *machine)
{
- pci_enabled = false;
- has_acpi_build = false;
- smbios_defaults = false;
- gigabyte_align = false;
- smbios_legacy_mode = true;
- has_reserved_memory = false;
- option_rom_has_mr = true;
- rom_file_has_mr = false;
if (!machine->cpu_model) {
machine->cpu_model = "486";
}
- x86_cpu_compat_kvm_no_autoenable(FEAT_KVM, 1 << KVM_FEATURE_PV_EOI);
+ x86_cpu_change_kvm_default("kvm-pv-eoi", NULL);
enable_compat_apic_id_mode();
- pc_init1(machine);
+ pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, TYPE_I440FX_PCI_DEVICE);
}
#ifdef CONFIG_XEN
+static void pc_xen_hvm_init_pci(MachineState *machine)
+{
+ const char *pci_type = has_igd_gfx_passthru ?
+ TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE : TYPE_I440FX_PCI_DEVICE;
+
+ pc_init1(machine,
+ TYPE_I440FX_PCI_HOST_BRIDGE,
+ pci_type);
+}
+
static void pc_xen_hvm_init(MachineState *machine)
{
PCIBus *bus;
- pc_init1(machine);
+ if (!xen_enabled()) {
+ error_report("xenfv machine requires the xen accelerator");
+ exit(1);
+ }
+
+ pc_xen_hvm_init_pci(machine);
bus = pci_find_primary_bus();
if (bus != NULL) {
@@ -465,27 +402,55 @@ static void pc_xen_hvm_init(MachineState *machine)
if (compat) { \
compat(machine); \
} \
- pc_init1(machine); \
+ pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \
+ TYPE_I440FX_PCI_DEVICE); \
} \
DEFINE_PC_MACHINE(suffix, name, pc_init_##suffix, optionfn)
static void pc_i440fx_machine_options(MachineClass *m)
{
- pc_default_machine_options(m);
m->family = "pc_piix";
m->desc = "Standard PC (i440FX + PIIX, 1996)";
m->hot_add_cpu = pc_hot_add_cpu;
+ m->default_machine_opts = "firmware=bios-256k.bin";
+ m->default_display = "std";
}
-static void pc_i440fx_2_4_machine_options(MachineClass *m)
+static void pc_i440fx_2_6_machine_options(MachineClass *m)
{
pc_i440fx_machine_options(m);
- m->default_machine_opts = "firmware=bios-256k.bin";
- m->default_display = "std";
m->alias = "pc";
m->is_default = 1;
}
+DEFINE_I440FX_MACHINE(v2_6, "pc-i440fx-2.6", NULL,
+ pc_i440fx_2_6_machine_options);
+
+
+static void pc_i440fx_2_5_machine_options(MachineClass *m)
+{
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
+ pc_i440fx_2_6_machine_options(m);
+ m->alias = NULL;
+ m->is_default = 0;
+ pcmc->save_tsc_khz = false;
+ m->legacy_fw_cfg_order = 1;
+ SET_MACHINE_COMPAT(m, PC_COMPAT_2_5);
+}
+
+DEFINE_I440FX_MACHINE(v2_5, "pc-i440fx-2.5", NULL,
+ pc_i440fx_2_5_machine_options);
+
+
+static void pc_i440fx_2_4_machine_options(MachineClass *m)
+{
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
+ pc_i440fx_2_5_machine_options(m);
+ m->hw_version = "2.4.0";
+ pcmc->broken_reserved_end = true;
+ SET_MACHINE_COMPAT(m, PC_COMPAT_2_4);
+}
+
DEFINE_I440FX_MACHINE(v2_4, "pc-i440fx-2.4", NULL,
pc_i440fx_2_4_machine_options)
@@ -493,8 +458,7 @@ DEFINE_I440FX_MACHINE(v2_4, "pc-i440fx-2.4", NULL,
static void pc_i440fx_2_3_machine_options(MachineClass *m)
{
pc_i440fx_2_4_machine_options(m);
- m->alias = NULL;
- m->is_default = 0;
+ m->hw_version = "2.3.0";
SET_MACHINE_COMPAT(m, PC_COMPAT_2_3);
}
@@ -504,8 +468,11 @@ DEFINE_I440FX_MACHINE(v2_3, "pc-i440fx-2.3", pc_compat_2_3,
static void pc_i440fx_2_2_machine_options(MachineClass *m)
{
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
pc_i440fx_2_3_machine_options(m);
+ m->hw_version = "2.2.0";
SET_MACHINE_COMPAT(m, PC_COMPAT_2_2);
+ pcmc->rsdp_in_ram = false;
}
DEFINE_I440FX_MACHINE(v2_2, "pc-i440fx-2.2", pc_compat_2_2,
@@ -514,9 +481,13 @@ DEFINE_I440FX_MACHINE(v2_2, "pc-i440fx-2.2", pc_compat_2_2,
static void pc_i440fx_2_1_machine_options(MachineClass *m)
{
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
pc_i440fx_2_2_machine_options(m);
+ m->hw_version = "2.1.0";
m->default_display = NULL;
SET_MACHINE_COMPAT(m, PC_COMPAT_2_1);
+ pcmc->smbios_uuid_encoded = false;
+ pcmc->enforce_aligned_dimm = false;
}
DEFINE_I440FX_MACHINE(v2_1, "pc-i440fx-2.1", pc_compat_2_1,
@@ -526,8 +497,30 @@ DEFINE_I440FX_MACHINE(v2_1, "pc-i440fx-2.1", pc_compat_2_1,
static void pc_i440fx_2_0_machine_options(MachineClass *m)
{
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
pc_i440fx_2_1_machine_options(m);
+ m->hw_version = "2.0.0";
SET_MACHINE_COMPAT(m, PC_COMPAT_2_0);
+ pcmc->smbios_legacy_mode = true;
+ pcmc->has_reserved_memory = false;
+ /* This value depends on the actual DSDT and SSDT compiled into
+ * the source QEMU; unfortunately it depends on the binary and
+ * not on the machine type, so we cannot make pc-i440fx-1.7 work on
+ * both QEMU 1.7 and QEMU 2.0.
+ *
+ * Large variations cause migration to fail for more than one
+ * consecutive value of the "-smp" maxcpus option.
+ *
+ * For small variations of the kind caused by different iasl versions,
+ * the 4k rounding usually leaves slack. However, there could be still
+ * one or two values that break. For QEMU 1.7 and QEMU 2.0 the
+ * slack is only ~10 bytes before one "-smp maxcpus" value breaks!
+ *
+ * 6652 is valid for QEMU 2.0, the right value for pc-i440fx-1.7 on
+ * QEMU 1.7 it is 6414. For RHEL/CentOS 7.0 it is 6418.
+ */
+ pcmc->legacy_acpi_table_size = 6652;
+ pcmc->acpi_data_size = 0x10000;
}
DEFINE_I440FX_MACHINE(v2_0, "pc-i440fx-2.0", pc_compat_2_0,
@@ -536,9 +529,15 @@ DEFINE_I440FX_MACHINE(v2_0, "pc-i440fx-2.0", pc_compat_2_0,
static void pc_i440fx_1_7_machine_options(MachineClass *m)
{
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
pc_i440fx_2_0_machine_options(m);
+ m->hw_version = "1.7.0";
m->default_machine_opts = NULL;
+ m->option_rom_has_mr = true;
SET_MACHINE_COMPAT(m, PC_COMPAT_1_7);
+ pcmc->smbios_defaults = false;
+ pcmc->gigabyte_align = false;
+ pcmc->legacy_acpi_table_size = 6414;
}
DEFINE_I440FX_MACHINE(v1_7, "pc-i440fx-1.7", pc_compat_1_7,
@@ -547,8 +546,12 @@ DEFINE_I440FX_MACHINE(v1_7, "pc-i440fx-1.7", pc_compat_1_7,
static void pc_i440fx_1_6_machine_options(MachineClass *m)
{
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
pc_i440fx_1_7_machine_options(m);
+ m->hw_version = "1.6.0";
+ m->rom_file_has_mr = false;
SET_MACHINE_COMPAT(m, PC_COMPAT_1_6);
+ pcmc->has_acpi_build = false;
}
DEFINE_I440FX_MACHINE(v1_6, "pc-i440fx-1.6", pc_compat_1_6,
@@ -558,6 +561,7 @@ DEFINE_I440FX_MACHINE(v1_6, "pc-i440fx-1.6", pc_compat_1_6,
static void pc_i440fx_1_5_machine_options(MachineClass *m)
{
pc_i440fx_1_6_machine_options(m);
+ m->hw_version = "1.5.0";
SET_MACHINE_COMPAT(m, PC_COMPAT_1_5);
}
@@ -568,6 +572,7 @@ DEFINE_I440FX_MACHINE(v1_5, "pc-i440fx-1.5", pc_compat_1_5,
static void pc_i440fx_1_4_machine_options(MachineClass *m)
{
pc_i440fx_1_5_machine_options(m);
+ m->hw_version = "1.4.0";
m->hot_add_cpu = NULL;
SET_MACHINE_COMPAT(m, PC_COMPAT_1_4);
}
@@ -600,6 +605,7 @@ DEFINE_I440FX_MACHINE(v1_4, "pc-i440fx-1.4", pc_compat_1_4,
static void pc_i440fx_1_3_machine_options(MachineClass *m)
{
pc_i440fx_1_4_machine_options(m);
+ m->hw_version = "1.3.0";
SET_MACHINE_COMPAT(m, PC_COMPAT_1_3);
}
@@ -638,6 +644,7 @@ DEFINE_I440FX_MACHINE(v1_3, "pc-1.3", pc_compat_1_3,
static void pc_i440fx_1_2_machine_options(MachineClass *m)
{
pc_i440fx_1_3_machine_options(m);
+ m->hw_version = "1.2.0";
SET_MACHINE_COMPAT(m, PC_COMPAT_1_2);
}
@@ -680,6 +687,7 @@ DEFINE_I440FX_MACHINE(v1_2, "pc-1.2", pc_compat_1_2,
static void pc_i440fx_1_1_machine_options(MachineClass *m)
{
pc_i440fx_1_2_machine_options(m);
+ m->hw_version = "1.1.0";
SET_MACHINE_COMPAT(m, PC_COMPAT_1_1);
}
@@ -797,9 +805,11 @@ DEFINE_I440FX_MACHINE(v0_14, "pc-0.14", pc_compat_1_2,
static void pc_i440fx_0_13_machine_options(MachineClass *m)
{
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
pc_i440fx_0_14_machine_options(m);
m->hw_version = "0.13";
SET_MACHINE_COMPAT(m, PC_COMPAT_0_13);
+ pcmc->kvmclock_enabled = false;
}
DEFINE_I440FX_MACHINE(v0_13, "pc-0.13", pc_compat_0_13,
@@ -906,12 +916,132 @@ static void pc_i440fx_0_10_machine_options(MachineClass *m)
DEFINE_I440FX_MACHINE(v0_10, "pc-0.10", pc_compat_0_13,
pc_i440fx_0_10_machine_options);
+typedef struct {
+ uint16_t gpu_device_id;
+ uint16_t pch_device_id;
+ uint8_t pch_revision_id;
+} IGDDeviceIDInfo;
+
+/* In real world different GPU should have different PCH. But actually
+ * the different PCH DIDs likely map to different PCH SKUs. We do the
+ * same thing for the GPU. For PCH, the different SKUs are going to be
+ * all the same silicon design and implementation, just different
+ * features turn on and off with fuses. The SW interfaces should be
+ * consistent across all SKUs in a given family (eg LPT). But just same
+ * features may not be supported.
+ *
+ * Most of these different PCH features probably don't matter to the
+ * Gfx driver, but obviously any difference in display port connections
+ * will so it should be fine with any PCH in case of passthrough.
+ *
+ * So currently use one PCH version, 0x8c4e, to cover all HSW(Haswell)
+ * scenarios, 0x9cc3 for BDW(Broadwell).
+ */
+static const IGDDeviceIDInfo igd_combo_id_infos[] = {
+ /* HSW Classic */
+ {0x0402, 0x8c4e, 0x04}, /* HSWGT1D, HSWD_w7 */
+ {0x0406, 0x8c4e, 0x04}, /* HSWGT1M, HSWM_w7 */
+ {0x0412, 0x8c4e, 0x04}, /* HSWGT2D, HSWD_w7 */
+ {0x0416, 0x8c4e, 0x04}, /* HSWGT2M, HSWM_w7 */
+ {0x041E, 0x8c4e, 0x04}, /* HSWGT15D, HSWD_w7 */
+ /* HSW ULT */
+ {0x0A06, 0x8c4e, 0x04}, /* HSWGT1UT, HSWM_w7 */
+ {0x0A16, 0x8c4e, 0x04}, /* HSWGT2UT, HSWM_w7 */
+ {0x0A26, 0x8c4e, 0x06}, /* HSWGT3UT, HSWM_w7 */
+ {0x0A2E, 0x8c4e, 0x04}, /* HSWGT3UT28W, HSWM_w7 */
+ {0x0A1E, 0x8c4e, 0x04}, /* HSWGT2UX, HSWM_w7 */
+ {0x0A0E, 0x8c4e, 0x04}, /* HSWGT1ULX, HSWM_w7 */
+ /* HSW CRW */
+ {0x0D26, 0x8c4e, 0x04}, /* HSWGT3CW, HSWM_w7 */
+ {0x0D22, 0x8c4e, 0x04}, /* HSWGT3CWDT, HSWD_w7 */
+ /* HSW Server */
+ {0x041A, 0x8c4e, 0x04}, /* HSWSVGT2, HSWD_w7 */
+ /* HSW SRVR */
+ {0x040A, 0x8c4e, 0x04}, /* HSWSVGT1, HSWD_w7 */
+ /* BSW */
+ {0x1606, 0x9cc3, 0x03}, /* BDWULTGT1, BDWM_w7 */
+ {0x1616, 0x9cc3, 0x03}, /* BDWULTGT2, BDWM_w7 */
+ {0x1626, 0x9cc3, 0x03}, /* BDWULTGT3, BDWM_w7 */
+ {0x160E, 0x9cc3, 0x03}, /* BDWULXGT1, BDWM_w7 */
+ {0x161E, 0x9cc3, 0x03}, /* BDWULXGT2, BDWM_w7 */
+ {0x1602, 0x9cc3, 0x03}, /* BDWHALOGT1, BDWM_w7 */
+ {0x1612, 0x9cc3, 0x03}, /* BDWHALOGT2, BDWM_w7 */
+ {0x1622, 0x9cc3, 0x03}, /* BDWHALOGT3, BDWM_w7 */
+ {0x162B, 0x9cc3, 0x03}, /* BDWHALO28W, BDWM_w7 */
+ {0x162A, 0x9cc3, 0x03}, /* BDWGT3WRKS, BDWM_w7 */
+ {0x162D, 0x9cc3, 0x03}, /* BDWGT3SRVR, BDWM_w7 */
+};
+
+static void isa_bridge_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ dc->desc = "ISA bridge faked to support IGD PT";
+ k->vendor_id = PCI_VENDOR_ID_INTEL;
+ k->class_id = PCI_CLASS_BRIDGE_ISA;
+};
+
+static TypeInfo isa_bridge_info = {
+ .name = "igd-passthrough-isa-bridge",
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(PCIDevice),
+ .class_init = isa_bridge_class_init,
+};
+
+static void pt_graphics_register_types(void)
+{
+ type_register_static(&isa_bridge_info);
+}
+type_init(pt_graphics_register_types)
+
+void igd_passthrough_isa_bridge_create(PCIBus *bus, uint16_t gpu_dev_id)
+{
+ struct PCIDevice *bridge_dev;
+ int i, num;
+ uint16_t pch_dev_id = 0xffff;
+ uint8_t pch_rev_id;
+
+ num = ARRAY_SIZE(igd_combo_id_infos);
+ for (i = 0; i < num; i++) {
+ if (gpu_dev_id == igd_combo_id_infos[i].gpu_device_id) {
+ pch_dev_id = igd_combo_id_infos[i].pch_device_id;
+ pch_rev_id = igd_combo_id_infos[i].pch_revision_id;
+ }
+ }
+
+ if (pch_dev_id == 0xffff) {
+ return;
+ }
+
+ /* Currently IGD drivers always need to access PCH by 1f.0. */
+ bridge_dev = pci_create_simple(bus, PCI_DEVFN(0x1f, 0),
+ "igd-passthrough-isa-bridge");
+
+ /*
+ * Note that vendor id is always PCI_VENDOR_ID_INTEL.
+ */
+ if (!bridge_dev) {
+ fprintf(stderr, "set igd-passthrough-isa-bridge failed!\n");
+ return;
+ }
+ pci_config_set_device_id(bridge_dev->config, pch_dev_id);
+ pci_config_set_revision(bridge_dev->config, pch_rev_id);
+}
static void isapc_machine_options(MachineClass *m)
{
- pc_common_machine_options(m);
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
m->desc = "ISA-only PC";
m->max_cpus = 1;
+ m->option_rom_has_mr = true;
+ m->rom_file_has_mr = false;
+ pcmc->pci_enabled = false;
+ pcmc->has_acpi_build = false;
+ pcmc->smbios_defaults = false;
+ pcmc->gigabyte_align = false;
+ pcmc->smbios_legacy_mode = true;
+ pcmc->has_reserved_memory = false;
}
DEFINE_PC_MACHINE(isapc, "isapc", pc_init_isa,
@@ -921,7 +1051,6 @@ DEFINE_PC_MACHINE(isapc, "isapc", pc_init_isa,
#ifdef CONFIG_XEN
static void xenfv_machine_options(MachineClass *m)
{
- pc_common_machine_options(m);
m->desc = "Xen Fully-virtualized PC";
m->max_cpus = HVM_MAX_VCPUS;
m->default_machine_opts = "accel=xen";
diff --git a/qemu/hw/i386/pc_q35.c b/qemu/hw/i386/pc_q35.c
index 974aead5a..04aae8958 100644
--- a/qemu/hw/i386/pc_q35.c
+++ b/qemu/hw/i386/pc_q35.c
@@ -27,6 +27,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/loader.h"
#include "sysemu/arch_init.h"
@@ -38,54 +39,40 @@
#include "hw/kvm/clock.h"
#include "hw/pci-host/q35.h"
#include "exec/address-spaces.h"
+#include "hw/i386/pc.h"
#include "hw/i386/ich9.h"
-#include "hw/i386/smbios.h"
+#include "hw/smbios/smbios.h"
#include "hw/ide/pci.h"
#include "hw/ide/ahci.h"
#include "hw/usb.h"
-#include "hw/cpu/icc_bus.h"
#include "qemu/error-report.h"
#include "migration/migration.h"
/* ICH9 AHCI has 6 ports */
#define MAX_SATA_PORTS 6
-static bool has_acpi_build = true;
-static bool rsdp_in_ram = true;
-static bool smbios_defaults = true;
-static bool smbios_legacy_mode;
-static bool smbios_uuid_encoded = true;
-/* Make sure that guest addresses aligned at 1Gbyte boundaries get mapped to
- * host addresses aligned at 1Gbyte boundaries. This way we can use 1GByte
- * pages in the host.
- */
-static bool gigabyte_align = true;
-static bool has_reserved_memory = true;
-
/* PC hardware initialisation */
static void pc_q35_init(MachineState *machine)
{
- PCMachineState *pc_machine = PC_MACHINE(machine);
- ram_addr_t below_4g_mem_size, above_4g_mem_size;
+ PCMachineState *pcms = PC_MACHINE(machine);
+ PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
Q35PCIHost *q35_host;
PCIHostState *phb;
PCIBus *host_bus;
PCIDevice *lpc;
BusState *idebus[MAX_SATA_PORTS];
ISADevice *rtc_state;
+ MemoryRegion *system_io = get_system_io();
MemoryRegion *pci_memory;
MemoryRegion *rom_memory;
MemoryRegion *ram_memory;
GSIState *gsi_state;
ISABus *isa_bus;
- int pci_enabled = 1;
qemu_irq *gsi;
qemu_irq *i8259;
int i;
ICH9LPCState *ich9_lpc;
PCIDevice *ahci;
- DeviceState *icc_bridge;
- PcGuestInfo *guest_info;
ram_addr_t lowmem;
DriveInfo *hd[MAX_SATA_PORTS];
MachineClass *mc = MACHINE_GET_CLASS(machine);
@@ -96,11 +83,9 @@ static void pc_q35_init(MachineState *machine)
* If it doesn't, we need to split it in chunks below and above 4G.
* In any case, try to make sure that guest addresses aligned at
* 1G boundaries get mapped to host addresses aligned at 1G boundaries.
- * For old machine types, use whatever split we used historically to avoid
- * breaking migration.
*/
if (machine->ram_size >= 0xb0000000) {
- lowmem = gigabyte_align ? 0x80000000 : 0xb0000000;
+ lowmem = 0x80000000;
} else {
lowmem = 0xb0000000;
}
@@ -108,41 +93,34 @@ static void pc_q35_init(MachineState *machine)
/* Handle the machine opt max-ram-below-4g. It is basically doing
* min(qemu limit, user limit).
*/
- if (lowmem > pc_machine->max_ram_below_4g) {
- lowmem = pc_machine->max_ram_below_4g;
+ if (lowmem > pcms->max_ram_below_4g) {
+ lowmem = pcms->max_ram_below_4g;
if (machine->ram_size - lowmem > lowmem &&
lowmem & ((1ULL << 30) - 1)) {
error_report("Warning: Large machine and max_ram_below_4g(%"PRIu64
") not a multiple of 1G; possible bad performance.",
- pc_machine->max_ram_below_4g);
+ pcms->max_ram_below_4g);
}
}
if (machine->ram_size >= lowmem) {
- above_4g_mem_size = machine->ram_size - lowmem;
- below_4g_mem_size = lowmem;
+ pcms->above_4g_mem_size = machine->ram_size - lowmem;
+ pcms->below_4g_mem_size = lowmem;
} else {
- above_4g_mem_size = 0;
- below_4g_mem_size = machine->ram_size;
+ pcms->above_4g_mem_size = 0;
+ pcms->below_4g_mem_size = machine->ram_size;
}
- if (xen_enabled() && xen_hvm_init(&below_4g_mem_size, &above_4g_mem_size,
- &ram_memory) != 0) {
- fprintf(stderr, "xen hardware virtual machine initialisation failed\n");
- exit(1);
+ if (xen_enabled()) {
+ xen_hvm_init(pcms, &ram_memory);
}
- icc_bridge = qdev_create(NULL, TYPE_ICC_BRIDGE);
- object_property_add_child(qdev_get_machine(), "icc-bridge",
- OBJECT(icc_bridge), NULL);
-
- pc_cpus_init(machine->cpu_model, icc_bridge);
- pc_acpi_init("q35-acpi-dsdt.aml");
+ pc_cpus_init(pcms);
kvmclock_create();
/* pci enabled */
- if (pci_enabled) {
+ if (pcmc->pci_enabled) {
pci_memory = g_new(MemoryRegion, 1);
memory_region_init(pci_memory, NULL, "pci", UINT64_MAX);
rom_memory = pci_memory;
@@ -151,34 +129,26 @@ static void pc_q35_init(MachineState *machine)
rom_memory = get_system_memory();
}
- guest_info = pc_guest_info_init(below_4g_mem_size, above_4g_mem_size);
- guest_info->isapc_ram_fw = false;
- guest_info->has_acpi_build = has_acpi_build;
- guest_info->has_reserved_memory = has_reserved_memory;
- guest_info->rsdp_in_ram = rsdp_in_ram;
-
- /* Migration was not supported in 2.0 for Q35, so do not bother
- * with this hack (see hw/i386/acpi-build.c).
- */
- guest_info->legacy_acpi_table_size = 0;
+ pc_guest_info_init(pcms);
- if (smbios_defaults) {
+ if (pcmc->smbios_defaults) {
/* These values are guest ABI, do not change */
smbios_set_defaults("QEMU", "Standard PC (Q35 + ICH9, 2009)",
- mc->name, smbios_legacy_mode, smbios_uuid_encoded);
+ mc->name, pcmc->smbios_legacy_mode,
+ pcmc->smbios_uuid_encoded,
+ SMBIOS_ENTRY_POINT_21);
}
/* allocate ram and load rom/bios */
if (!xen_enabled()) {
- pc_memory_init(machine, get_system_memory(),
- below_4g_mem_size, above_4g_mem_size,
- rom_memory, &ram_memory, guest_info);
+ pc_memory_init(pcms, get_system_memory(),
+ rom_memory, &ram_memory);
}
/* irq lines */
gsi_state = g_malloc0(sizeof(*gsi_state));
- if (kvm_irqchip_in_kernel()) {
- kvm_pc_setup_irq_routing(pci_enabled);
+ if (kvm_ioapic_in_kernel()) {
+ kvm_pc_setup_irq_routing(pcmc->pci_enabled);
gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state,
GSI_NUM_PINS);
} else {
@@ -192,14 +162,14 @@ static void pc_q35_init(MachineState *machine)
q35_host->mch.ram_memory = ram_memory;
q35_host->mch.pci_address_space = pci_memory;
q35_host->mch.system_memory = get_system_memory();
- q35_host->mch.address_space_io = get_system_io();
- q35_host->mch.below_4g_mem_size = below_4g_mem_size;
- q35_host->mch.above_4g_mem_size = above_4g_mem_size;
- q35_host->mch.guest_info = guest_info;
+ q35_host->mch.address_space_io = system_io;
+ q35_host->mch.below_4g_mem_size = pcms->below_4g_mem_size;
+ q35_host->mch.above_4g_mem_size = pcms->above_4g_mem_size;
/* pci */
qdev_init_nofail(DEVICE(q35_host));
phb = PCI_HOST_BRIDGE(q35_host);
host_bus = phb->bus;
+ pcms->bus = phb->bus;
/* create ISA bus */
lpc = pci_create_simple_multifunction(host_bus, PCI_DEVFN(ICH9_LPC_DEV,
ICH9_LPC_FUNC), true,
@@ -207,7 +177,7 @@ static void pc_q35_init(MachineState *machine)
object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP,
TYPE_HOTPLUG_HANDLER,
- (Object **)&pc_machine->acpi_dev,
+ (Object **)&pcms->acpi_dev,
object_property_allow_set_link,
OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort);
object_property_set_link(OBJECT(machine), OBJECT(lpc),
@@ -224,7 +194,7 @@ static void pc_q35_init(MachineState *machine)
/*end early*/
isa_bus_irqs(isa_bus, gsi);
- if (kvm_irqchip_in_kernel()) {
+ if (kvm_pic_in_kernel()) {
i8259 = kvm_i8259_init(isa_bus);
} else if (xen_enabled()) {
i8259 = xen_interrupt_controller_init();
@@ -235,24 +205,23 @@ static void pc_q35_init(MachineState *machine)
for (i = 0; i < ISA_NUM_IRQS; i++) {
gsi_state->i8259_irq[i] = i8259[i];
}
- if (pci_enabled) {
+ if (pcmc->pci_enabled) {
ioapic_init_gsi(gsi_state, "q35");
}
- qdev_init_nofail(icc_bridge);
pc_register_ferr_irq(gsi[13]);
- assert(pc_machine->vmport != ON_OFF_AUTO_MAX);
- if (pc_machine->vmport == ON_OFF_AUTO_AUTO) {
- pc_machine->vmport = xen_enabled() ? ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON;
+ assert(pcms->vmport != ON_OFF_AUTO__MAX);
+ if (pcms->vmport == ON_OFF_AUTO_AUTO) {
+ pcms->vmport = xen_enabled() ? ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON;
}
/* init basic PC hardware */
pc_basic_device_init(isa_bus, gsi, &rtc_state, !mc->no_floppy,
- (pc_machine->vmport != ON_OFF_AUTO_ON), 0xff0104);
+ (pcms->vmport != ON_OFF_AUTO_ON), 0xff0104);
/* connect pm stuff to lpc */
- ich9_lpc_pm_init(lpc, pc_machine_is_smm_enabled(pc_machine), !mc->no_tco);
+ ich9_lpc_pm_init(lpc, pc_machine_is_smm_enabled(pcms));
/* ahci and SATA device, for q35 1 ahci controller is built-in */
ahci = pci_create_simple_multifunction(host_bus,
@@ -276,99 +245,19 @@ static void pc_q35_init(MachineState *machine)
0xb100),
8, NULL, 0);
- pc_cmos_init(below_4g_mem_size, above_4g_mem_size, machine->boot_order,
- machine, idebus[0], idebus[1], rtc_state);
+ pc_cmos_init(pcms, idebus[0], idebus[1], rtc_state);
/* the rest devices to which pci devfn is automatically assigned */
pc_vga_init(isa_bus, host_bus);
pc_nic_init(isa_bus, host_bus);
- if (pci_enabled) {
+ if (pcmc->pci_enabled) {
pc_pci_device_init(host_bus);
}
-}
-static void pc_compat_2_3(MachineState *machine)
-{
- PCMachineState *pcms = PC_MACHINE(machine);
- savevm_skip_section_footers();
- if (kvm_enabled()) {
- pcms->smm = ON_OFF_AUTO_OFF;
+ if (pcms->acpi_nvdimm_state.is_enabled) {
+ nvdimm_init_acpi_state(&pcms->acpi_nvdimm_state, system_io,
+ pcms->fw_cfg, OBJECT(pcms));
}
- global_state_set_optional();
- savevm_skip_configuration();
-}
-
-static void pc_compat_2_2(MachineState *machine)
-{
- pc_compat_2_3(machine);
- rsdp_in_ram = false;
- x86_cpu_compat_set_features("kvm64", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("kvm32", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Conroe", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Penryn", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Nehalem", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Westmere", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("SandyBridge", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Haswell", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Broadwell", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Opteron_G1", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Opteron_G2", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Opteron_G3", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Opteron_G4", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Opteron_G5", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Haswell", FEAT_1_ECX, 0, CPUID_EXT_F16C);
- x86_cpu_compat_set_features("Haswell", FEAT_1_ECX, 0, CPUID_EXT_RDRAND);
- x86_cpu_compat_set_features("Broadwell", FEAT_1_ECX, 0, CPUID_EXT_F16C);
- x86_cpu_compat_set_features("Broadwell", FEAT_1_ECX, 0, CPUID_EXT_RDRAND);
- machine->suppress_vmdesc = true;
-}
-
-static void pc_compat_2_1(MachineState *machine)
-{
- PCMachineState *pcms = PC_MACHINE(machine);
-
- pc_compat_2_2(machine);
- pcms->enforce_aligned_dimm = false;
- smbios_uuid_encoded = false;
- x86_cpu_compat_set_features("coreduo", FEAT_1_ECX, CPUID_EXT_VMX, 0);
- x86_cpu_compat_set_features("core2duo", FEAT_1_ECX, CPUID_EXT_VMX, 0);
- x86_cpu_compat_kvm_no_autodisable(FEAT_8000_0001_ECX, CPUID_EXT3_SVM);
-}
-
-static void pc_compat_2_0(MachineState *machine)
-{
- pc_compat_2_1(machine);
- smbios_legacy_mode = true;
- has_reserved_memory = false;
- pc_set_legacy_acpi_data_size();
-}
-
-static void pc_compat_1_7(MachineState *machine)
-{
- pc_compat_2_0(machine);
- smbios_defaults = false;
- gigabyte_align = false;
- option_rom_has_mr = true;
- x86_cpu_compat_kvm_no_autoenable(FEAT_1_ECX, CPUID_EXT_X2APIC);
-}
-
-static void pc_compat_1_6(MachineState *machine)
-{
- pc_compat_1_7(machine);
- rom_file_has_mr = false;
- has_acpi_build = false;
-}
-
-static void pc_compat_1_5(MachineState *machine)
-{
- pc_compat_1_6(machine);
-}
-
-static void pc_compat_1_4(MachineState *machine)
-{
- pc_compat_1_5(machine);
- x86_cpu_compat_set_features("n270", FEAT_1_ECX, 0, CPUID_EXT_MOVBE);
- x86_cpu_compat_set_features("Westmere", FEAT_1_ECX, 0, CPUID_EXT_PCLMULQDQ);
}
#define DEFINE_Q35_MACHINE(suffix, name, compatfn, optionfn) \
@@ -385,108 +274,45 @@ static void pc_compat_1_4(MachineState *machine)
static void pc_q35_machine_options(MachineClass *m)
{
- pc_default_machine_options(m);
m->family = "pc_q35";
m->desc = "Standard PC (Q35 + ICH9, 2009)";
m->hot_add_cpu = pc_hot_add_cpu;
m->units_per_default_bus = 1;
-}
-
-static void pc_q35_2_4_machine_options(MachineClass *m)
-{
- pc_q35_machine_options(m);
m->default_machine_opts = "firmware=bios-256k.bin";
m->default_display = "std";
m->no_floppy = 1;
- m->no_tco = 0;
- m->alias = "q35";
}
-DEFINE_Q35_MACHINE(v2_4, "pc-q35-2.4", NULL,
- pc_q35_2_4_machine_options);
-
-
-static void pc_q35_2_3_machine_options(MachineClass *m)
-{
- pc_q35_2_4_machine_options(m);
- m->no_floppy = 0;
- m->no_tco = 1;
- m->alias = NULL;
- SET_MACHINE_COMPAT(m, PC_COMPAT_2_3);
-}
-
-DEFINE_Q35_MACHINE(v2_3, "pc-q35-2.3", pc_compat_2_3,
- pc_q35_2_3_machine_options);
-
-
-static void pc_q35_2_2_machine_options(MachineClass *m)
-{
- pc_q35_2_3_machine_options(m);
- SET_MACHINE_COMPAT(m, PC_COMPAT_2_2);
-}
-
-DEFINE_Q35_MACHINE(v2_2, "pc-q35-2.2", pc_compat_2_2,
- pc_q35_2_2_machine_options);
-
-
-static void pc_q35_2_1_machine_options(MachineClass *m)
-{
- pc_q35_2_2_machine_options(m);
- m->default_display = NULL;
- SET_MACHINE_COMPAT(m, PC_COMPAT_2_1);
-}
-
-DEFINE_Q35_MACHINE(v2_1, "pc-q35-2.1", pc_compat_2_1,
- pc_q35_2_1_machine_options);
-
-
-static void pc_q35_2_0_machine_options(MachineClass *m)
-{
- pc_q35_2_1_machine_options(m);
- SET_MACHINE_COMPAT(m, PC_COMPAT_2_0);
-}
-
-DEFINE_Q35_MACHINE(v2_0, "pc-q35-2.0", pc_compat_2_0,
- pc_q35_2_0_machine_options);
-
-
-static void pc_q35_1_7_machine_options(MachineClass *m)
-{
- pc_q35_2_0_machine_options(m);
- m->default_machine_opts = NULL;
- SET_MACHINE_COMPAT(m, PC_COMPAT_1_7);
-}
-
-DEFINE_Q35_MACHINE(v1_7, "pc-q35-1.7", pc_compat_1_7,
- pc_q35_1_7_machine_options);
-
-
-static void pc_q35_1_6_machine_options(MachineClass *m)
+static void pc_q35_2_6_machine_options(MachineClass *m)
{
pc_q35_machine_options(m);
- SET_MACHINE_COMPAT(m, PC_COMPAT_1_6);
+ m->alias = "q35";
}
-DEFINE_Q35_MACHINE(v1_6, "pc-q35-1.6", pc_compat_1_6,
- pc_q35_1_6_machine_options);
+DEFINE_Q35_MACHINE(v2_6, "pc-q35-2.6", NULL,
+ pc_q35_2_6_machine_options);
-
-static void pc_q35_1_5_machine_options(MachineClass *m)
+static void pc_q35_2_5_machine_options(MachineClass *m)
{
- pc_q35_1_6_machine_options(m);
- SET_MACHINE_COMPAT(m, PC_COMPAT_1_5);
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
+ pc_q35_2_6_machine_options(m);
+ m->alias = NULL;
+ pcmc->save_tsc_khz = false;
+ m->legacy_fw_cfg_order = 1;
+ SET_MACHINE_COMPAT(m, PC_COMPAT_2_5);
}
-DEFINE_Q35_MACHINE(v1_5, "pc-q35-1.5", pc_compat_1_5,
- pc_q35_1_5_machine_options);
+DEFINE_Q35_MACHINE(v2_5, "pc-q35-2.5", NULL,
+ pc_q35_2_5_machine_options);
-
-static void pc_q35_1_4_machine_options(MachineClass *m)
+static void pc_q35_2_4_machine_options(MachineClass *m)
{
- pc_q35_1_5_machine_options(m);
- m->hot_add_cpu = NULL;
- SET_MACHINE_COMPAT(m, PC_COMPAT_1_4);
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
+ pc_q35_2_5_machine_options(m);
+ m->hw_version = "2.4.0";
+ pcmc->broken_reserved_end = true;
+ SET_MACHINE_COMPAT(m, PC_COMPAT_2_4);
}
-DEFINE_Q35_MACHINE(v1_4, "pc-q35-1.4", pc_compat_1_4,
- pc_q35_1_4_machine_options);
+DEFINE_Q35_MACHINE(v2_4, "pc-q35-2.4", NULL,
+ pc_q35_2_4_machine_options);
diff --git a/qemu/hw/i386/pc_sysfw.c b/qemu/hw/i386/pc_sysfw.c
index 662d99768..f915ad0a3 100644
--- a/qemu/hw/i386/pc_sysfw.c
+++ b/qemu/hw/i386/pc_sysfw.c
@@ -23,6 +23,8 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "sysemu/block-backend.h"
#include "qemu/error-report.h"
#include "hw/sysbus.h"
@@ -56,7 +58,7 @@ static void pc_isa_bios_init(MemoryRegion *rom_memory,
isa_bios_size = MIN(flash_size, 128 * 1024);
isa_bios = g_malloc(sizeof(*isa_bios));
memory_region_init_ram(isa_bios, NULL, "isa-bios", isa_bios_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(isa_bios);
memory_region_add_subregion_overlap(rom_memory,
0x100000 - isa_bios_size,
@@ -193,7 +195,7 @@ static void old_pc_system_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw)
goto bios_error;
}
bios = g_malloc(sizeof(*bios));
- memory_region_init_ram(bios, NULL, "pc.bios", bios_size, &error_abort);
+ memory_region_init_ram(bios, NULL, "pc.bios", bios_size, &error_fatal);
vmstate_register_ram_global(bios);
if (!isapc_ram_fw) {
memory_region_set_readonly(bios, true);
diff --git a/qemu/hw/i386/pci-assign-load-rom.c b/qemu/hw/i386/pci-assign-load-rom.c
new file mode 100644
index 000000000..4bbb08c95
--- /dev/null
+++ b/qemu/hw/i386/pci-assign-load-rom.c
@@ -0,0 +1,85 @@
+/*
+ * This is splited from hw/i386/kvm/pci-assign.c
+ */
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "hw/i386/pc.h"
+#include "qemu/error-report.h"
+#include "ui/console.h"
+#include "hw/loader.h"
+#include "monitor/monitor.h"
+#include "qemu/range.h"
+#include "sysemu/sysemu.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci-assign.h"
+
+/*
+ * Scan the assigned devices for the devices that have an option ROM, and then
+ * load the corresponding ROM data to RAM. If an error occurs while loading an
+ * option ROM, we just ignore that option ROM and continue with the next one.
+ */
+void *pci_assign_dev_load_option_rom(PCIDevice *dev, struct Object *owner,
+ int *size, unsigned int domain,
+ unsigned int bus, unsigned int slot,
+ unsigned int function)
+{
+ char name[32], rom_file[64];
+ FILE *fp;
+ uint8_t val;
+ struct stat st;
+ void *ptr = NULL;
+
+ /* If loading ROM from file, pci handles it */
+ if (dev->romfile || !dev->rom_bar) {
+ return NULL;
+ }
+
+ snprintf(rom_file, sizeof(rom_file),
+ "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/rom",
+ domain, bus, slot, function);
+
+ if (stat(rom_file, &st)) {
+ return NULL;
+ }
+
+ /* Write "1" to the ROM file to enable it */
+ fp = fopen(rom_file, "r+");
+ if (fp == NULL) {
+ error_report("pci-assign: Cannot open %s: %s", rom_file, strerror(errno));
+ return NULL;
+ }
+ val = 1;
+ if (fwrite(&val, 1, 1, fp) != 1) {
+ goto close_rom;
+ }
+ fseek(fp, 0, SEEK_SET);
+
+ snprintf(name, sizeof(name), "%s.rom", object_get_typename(owner));
+ memory_region_init_ram(&dev->rom, owner, name, st.st_size, &error_abort);
+ vmstate_register_ram(&dev->rom, &dev->qdev);
+ ptr = memory_region_get_ram_ptr(&dev->rom);
+ memset(ptr, 0xff, st.st_size);
+
+ if (!fread(ptr, 1, st.st_size, fp)) {
+ error_report("pci-assign: Cannot read from host %s", rom_file);
+ error_printf("Device option ROM contents are probably invalid "
+ "(check dmesg).\nSkip option ROM probe with rombar=0, "
+ "or load from file with romfile=\n");
+ goto close_rom;
+ }
+
+ pci_register_bar(dev, PCI_ROM_SLOT, 0, &dev->rom);
+ dev->has_rom = true;
+ *size = st.st_size;
+close_rom:
+ /* Write "0" to disable ROM */
+ fseek(fp, 0, SEEK_SET);
+ val = 0;
+ if (!fwrite(&val, 1, 1, fp)) {
+ DEBUG("%s\n", "Failed to disable pci-sysfs rom file");
+ }
+ fclose(fp);
+
+ return ptr;
+}
diff --git a/qemu/hw/i386/q35-acpi-dsdt.dsl b/qemu/hw/i386/q35-acpi-dsdt.dsl
deleted file mode 100644
index 16eaca3fa..000000000
--- a/qemu/hw/i386/q35-acpi-dsdt.dsl
+++ /dev/null
@@ -1,435 +0,0 @@
-/*
- * Bochs/QEMU ACPI DSDT ASL definition
- *
- * Copyright (c) 2006 Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * 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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-/*
- * Copyright (c) 2010 Isaku Yamahata
- * yamahata at valinux co jp
- * Based on acpi-dsdt.dsl, but heavily modified for q35 chipset.
- */
-
-ACPI_EXTRACT_ALL_CODE Q35AcpiDsdtAmlCode
-
-DefinitionBlock (
- "q35-acpi-dsdt.aml",// Output Filename
- "DSDT", // Signature
- 0x01, // DSDT Compliance Revision
- "BXPC", // OEMID
- "BXDSDT", // TABLE ID
- 0x2 // OEM Revision
- )
-{
-
-#include "acpi-dsdt-dbug.dsl"
-
- Scope(\_SB) {
- OperationRegion(PCST, SystemIO, 0xae00, 0x0c)
- OperationRegion(PCSB, SystemIO, 0xae0c, 0x01)
- Field(PCSB, AnyAcc, NoLock, WriteAsZeros) {
- PCIB, 8,
- }
- }
-
-
-/****************************************************************
- * PCI Bus definition
- ****************************************************************/
- Scope(\_SB) {
- Device(PCI0) {
- Name(_HID, EisaId("PNP0A08"))
- Name(_CID, EisaId("PNP0A03"))
- Name(_ADR, 0x00)
- Name(_UID, 1)
-
- External(ISA, DeviceObj)
-
- // _OSC: based on sample of ACPI3.0b spec
- Name(SUPP, 0) // PCI _OSC Support Field value
- Name(CTRL, 0) // PCI _OSC Control Field value
- Method(_OSC, 4) {
- // Create DWORD-addressable fields from the Capabilities Buffer
- CreateDWordField(Arg3, 0, CDW1)
-
- // Check for proper UUID
- If (LEqual(Arg0, ToUUID("33DB4D5B-1FF7-401C-9657-7441C03DD766"))) {
- // Create DWORD-addressable fields from the Capabilities Buffer
- CreateDWordField(Arg3, 4, CDW2)
- CreateDWordField(Arg3, 8, CDW3)
-
- // Save Capabilities DWORD2 & 3
- Store(CDW2, SUPP)
- Store(CDW3, CTRL)
-
- // Always allow native PME, AER (no dependencies)
- // Never allow SHPC (no SHPC controller in this system)
- And(CTRL, 0x1D, CTRL)
-
-#if 0 // For now, nothing to do
- If (Not(And(CDW1, 1))) { // Query flag clear?
- // Disable GPEs for features granted native control.
- If (And(CTRL, 0x01)) { // Hot plug control granted?
- Store(0, HPCE) // clear the hot plug SCI enable bit
- Store(1, HPCS) // clear the hot plug SCI status bit
- }
- If (And(CTRL, 0x04)) { // PME control granted?
- Store(0, PMCE) // clear the PME SCI enable bit
- Store(1, PMCS) // clear the PME SCI status bit
- }
- If (And(CTRL, 0x10)) { // OS restoring PCI Express cap structure?
- // Set status to not restore PCI Express cap structure
- // upon resume from S3
- Store(1, S3CR)
- }
- }
-#endif
- If (LNotEqual(Arg1, One)) {
- // Unknown revision
- Or(CDW1, 0x08, CDW1)
- }
- If (LNotEqual(CDW3, CTRL)) {
- // Capabilities bits were masked
- Or(CDW1, 0x10, CDW1)
- }
- // Update DWORD3 in the buffer
- Store(CTRL, CDW3)
- } Else {
- Or(CDW1, 4, CDW1) // Unrecognized UUID
- }
- Return (Arg3)
- }
- }
- }
-
-#include "acpi-dsdt-hpet.dsl"
-
-
-/****************************************************************
- * LPC ISA bridge
- ****************************************************************/
-
- Scope(\_SB.PCI0) {
- /* PCI D31:f0 LPC ISA bridge */
- Device(ISA) {
- Name (_ADR, 0x001F0000) // _ADR: Address
-
- /* ICH9 PCI to ISA irq remapping */
- OperationRegion(PIRQ, PCI_Config, 0x60, 0x0C)
-
- OperationRegion(LPCD, PCI_Config, 0x80, 0x2)
- Field(LPCD, AnyAcc, NoLock, Preserve) {
- COMA, 3,
- , 1,
- COMB, 3,
-
- Offset(0x01),
- LPTD, 2,
- , 2,
- FDCD, 2
- }
- OperationRegion(LPCE, PCI_Config, 0x82, 0x2)
- Field(LPCE, AnyAcc, NoLock, Preserve) {
- CAEN, 1,
- CBEN, 1,
- LPEN, 1,
- FDEN, 1
- }
- }
- }
-
-#include "acpi-dsdt-isa.dsl"
-
-
-/****************************************************************
- * PCI IRQs
- ****************************************************************/
-
- /* Zero => PIC mode, One => APIC Mode */
- Name(\PICF, Zero)
- Method(\_PIC, 1, NotSerialized) {
- Store(Arg0, \PICF)
- }
-
- Scope(\_SB) {
- Scope(PCI0) {
-#define prt_slot_lnk(nr, lnk0, lnk1, lnk2, lnk3) \
- Package() { nr##ffff, 0, lnk0, 0 }, \
- Package() { nr##ffff, 1, lnk1, 0 }, \
- Package() { nr##ffff, 2, lnk2, 0 }, \
- Package() { nr##ffff, 3, lnk3, 0 }
-
-#define prt_slot_lnkA(nr) prt_slot_lnk(nr, LNKA, LNKB, LNKC, LNKD)
-#define prt_slot_lnkB(nr) prt_slot_lnk(nr, LNKB, LNKC, LNKD, LNKA)
-#define prt_slot_lnkC(nr) prt_slot_lnk(nr, LNKC, LNKD, LNKA, LNKB)
-#define prt_slot_lnkD(nr) prt_slot_lnk(nr, LNKD, LNKA, LNKB, LNKC)
-
-#define prt_slot_lnkE(nr) prt_slot_lnk(nr, LNKE, LNKF, LNKG, LNKH)
-#define prt_slot_lnkF(nr) prt_slot_lnk(nr, LNKF, LNKG, LNKH, LNKE)
-#define prt_slot_lnkG(nr) prt_slot_lnk(nr, LNKG, LNKH, LNKE, LNKF)
-#define prt_slot_lnkH(nr) prt_slot_lnk(nr, LNKH, LNKE, LNKF, LNKG)
-
- Name(PRTP, package() {
- prt_slot_lnkE(0x0000),
- prt_slot_lnkF(0x0001),
- prt_slot_lnkG(0x0002),
- prt_slot_lnkH(0x0003),
- prt_slot_lnkE(0x0004),
- prt_slot_lnkF(0x0005),
- prt_slot_lnkG(0x0006),
- prt_slot_lnkH(0x0007),
- prt_slot_lnkE(0x0008),
- prt_slot_lnkF(0x0009),
- prt_slot_lnkG(0x000a),
- prt_slot_lnkH(0x000b),
- prt_slot_lnkE(0x000c),
- prt_slot_lnkF(0x000d),
- prt_slot_lnkG(0x000e),
- prt_slot_lnkH(0x000f),
- prt_slot_lnkE(0x0010),
- prt_slot_lnkF(0x0011),
- prt_slot_lnkG(0x0012),
- prt_slot_lnkH(0x0013),
- prt_slot_lnkE(0x0014),
- prt_slot_lnkF(0x0015),
- prt_slot_lnkG(0x0016),
- prt_slot_lnkH(0x0017),
- prt_slot_lnkE(0x0018),
-
- /* INTA -> PIRQA for slot 25 - 31
- see the default value of D<N>IR */
- prt_slot_lnkA(0x0019),
- prt_slot_lnkA(0x001a),
- prt_slot_lnkA(0x001b),
- prt_slot_lnkA(0x001c),
- prt_slot_lnkA(0x001d),
-
- /* PCIe->PCI bridge. use PIRQ[E-H] */
- prt_slot_lnkE(0x001e),
-
- prt_slot_lnkA(0x001f)
- })
-
-#define prt_slot_gsi(nr, gsi0, gsi1, gsi2, gsi3) \
- Package() { nr##ffff, 0, gsi0, 0 }, \
- Package() { nr##ffff, 1, gsi1, 0 }, \
- Package() { nr##ffff, 2, gsi2, 0 }, \
- Package() { nr##ffff, 3, gsi3, 0 }
-
-#define prt_slot_gsiA(nr) prt_slot_gsi(nr, GSIA, GSIB, GSIC, GSID)
-#define prt_slot_gsiB(nr) prt_slot_gsi(nr, GSIB, GSIC, GSID, GSIA)
-#define prt_slot_gsiC(nr) prt_slot_gsi(nr, GSIC, GSID, GSIA, GSIB)
-#define prt_slot_gsiD(nr) prt_slot_gsi(nr, GSID, GSIA, GSIB, GSIC)
-
-#define prt_slot_gsiE(nr) prt_slot_gsi(nr, GSIE, GSIF, GSIG, GSIH)
-#define prt_slot_gsiF(nr) prt_slot_gsi(nr, GSIF, GSIG, GSIH, GSIE)
-#define prt_slot_gsiG(nr) prt_slot_gsi(nr, GSIG, GSIH, GSIE, GSIF)
-#define prt_slot_gsiH(nr) prt_slot_gsi(nr, GSIH, GSIE, GSIF, GSIG)
-
- Name(PRTA, package() {
- prt_slot_gsiE(0x0000),
- prt_slot_gsiF(0x0001),
- prt_slot_gsiG(0x0002),
- prt_slot_gsiH(0x0003),
- prt_slot_gsiE(0x0004),
- prt_slot_gsiF(0x0005),
- prt_slot_gsiG(0x0006),
- prt_slot_gsiH(0x0007),
- prt_slot_gsiE(0x0008),
- prt_slot_gsiF(0x0009),
- prt_slot_gsiG(0x000a),
- prt_slot_gsiH(0x000b),
- prt_slot_gsiE(0x000c),
- prt_slot_gsiF(0x000d),
- prt_slot_gsiG(0x000e),
- prt_slot_gsiH(0x000f),
- prt_slot_gsiE(0x0010),
- prt_slot_gsiF(0x0011),
- prt_slot_gsiG(0x0012),
- prt_slot_gsiH(0x0013),
- prt_slot_gsiE(0x0014),
- prt_slot_gsiF(0x0015),
- prt_slot_gsiG(0x0016),
- prt_slot_gsiH(0x0017),
- prt_slot_gsiE(0x0018),
-
- /* INTA -> PIRQA for slot 25 - 31, but 30
- see the default value of D<N>IR */
- prt_slot_gsiA(0x0019),
- prt_slot_gsiA(0x001a),
- prt_slot_gsiA(0x001b),
- prt_slot_gsiA(0x001c),
- prt_slot_gsiA(0x001d),
-
- /* PCIe->PCI bridge. use PIRQ[E-H] */
- prt_slot_gsiE(0x001e),
-
- prt_slot_gsiA(0x001f)
- })
-
- Method(_PRT, 0, NotSerialized) {
- /* PCI IRQ routing table, example from ACPI 2.0a specification,
- section 6.2.8.1 */
- /* Note: we provide the same info as the PCI routing
- table of the Bochs BIOS */
- If (LEqual(\PICF, Zero)) {
- Return (PRTP)
- } Else {
- Return (PRTA)
- }
- }
- }
-
- Field(PCI0.ISA.PIRQ, ByteAcc, NoLock, Preserve) {
- PRQA, 8,
- PRQB, 8,
- PRQC, 8,
- PRQD, 8,
-
- Offset(0x08),
- PRQE, 8,
- PRQF, 8,
- PRQG, 8,
- PRQH, 8
- }
-
- Method(IQST, 1, NotSerialized) {
- // _STA method - get status
- If (And(0x80, Arg0)) {
- Return (0x09)
- }
- Return (0x0B)
- }
- Method(IQCR, 1, Serialized) {
- // _CRS method - get current settings
- Name(PRR0, ResourceTemplate() {
- Interrupt(, Level, ActiveHigh, Shared) { 0 }
- })
- CreateDWordField(PRR0, 0x05, PRRI)
- Store(And(Arg0, 0x0F), PRRI)
- Return (PRR0)
- }
-
-#define define_link(link, uid, reg) \
- Device(link) { \
- Name(_HID, EISAID("PNP0C0F")) \
- Name(_UID, uid) \
- Name(_PRS, ResourceTemplate() { \
- Interrupt(, Level, ActiveHigh, Shared) { \
- 5, 10, 11 \
- } \
- }) \
- Method(_STA, 0, NotSerialized) { \
- Return (IQST(reg)) \
- } \
- Method(_DIS, 0, NotSerialized) { \
- Or(reg, 0x80, reg) \
- } \
- Method(_CRS, 0, NotSerialized) { \
- Return (IQCR(reg)) \
- } \
- Method(_SRS, 1, NotSerialized) { \
- CreateDWordField(Arg0, 0x05, PRRI) \
- Store(PRRI, reg) \
- } \
- }
-
- define_link(LNKA, 0, PRQA)
- define_link(LNKB, 1, PRQB)
- define_link(LNKC, 2, PRQC)
- define_link(LNKD, 3, PRQD)
- define_link(LNKE, 4, PRQE)
- define_link(LNKF, 5, PRQF)
- define_link(LNKG, 6, PRQG)
- define_link(LNKH, 7, PRQH)
-
-#define define_gsi_link(link, uid, gsi) \
- Device(link) { \
- Name(_HID, EISAID("PNP0C0F")) \
- Name(_UID, uid) \
- Name(_PRS, ResourceTemplate() { \
- Interrupt(, Level, ActiveHigh, Shared) { \
- gsi \
- } \
- }) \
- Name(_CRS, ResourceTemplate() { \
- Interrupt(, Level, ActiveHigh, Shared) { \
- gsi \
- } \
- }) \
- Method(_SRS, 1, NotSerialized) { \
- } \
- }
-
- define_gsi_link(GSIA, 0, 0x10)
- define_gsi_link(GSIB, 0, 0x11)
- define_gsi_link(GSIC, 0, 0x12)
- define_gsi_link(GSID, 0, 0x13)
- define_gsi_link(GSIE, 0, 0x14)
- define_gsi_link(GSIF, 0, 0x15)
- define_gsi_link(GSIG, 0, 0x16)
- define_gsi_link(GSIH, 0, 0x17)
- }
-
-#include "hw/acpi/pc-hotplug.h"
-#define CPU_STATUS_BASE ICH9_CPU_HOTPLUG_IO_BASE
-#include "acpi-dsdt-cpu-hotplug.dsl"
-#include "acpi-dsdt-mem-hotplug.dsl"
-
-
-/****************************************************************
- * General purpose events
- ****************************************************************/
- Scope(\_GPE) {
- Name(_HID, "ACPI0006")
-
- Method(_L00) {
- }
- Method(_L01) {
- }
- Method(_E02) {
- // CPU hotplug event
- \_SB.PRSC()
- }
- Method(_E03) {
- // Memory hotplug event
- \_SB.PCI0.MEMORY_HOTPLUG_DEVICE.MEMORY_SLOT_SCAN_METHOD()
- }
- Method(_L04) {
- }
- Method(_L05) {
- }
- Method(_L06) {
- }
- Method(_L07) {
- }
- Method(_L08) {
- }
- Method(_L09) {
- }
- Method(_L0A) {
- }
- Method(_L0B) {
- }
- Method(_L0C) {
- }
- Method(_L0D) {
- }
- Method(_L0E) {
- }
- Method(_L0F) {
- }
- }
-}
diff --git a/qemu/hw/i386/q35-acpi-dsdt.hex.generated b/qemu/hw/i386/q35-acpi-dsdt.hex.generated
deleted file mode 100644
index ed9a2cc8e..000000000
--- a/qemu/hw/i386/q35-acpi-dsdt.hex.generated
+++ /dev/null
@@ -1,7610 +0,0 @@
-static unsigned char Q35AcpiDsdtAmlCode[] = {
-0x44,
-0x53,
-0x44,
-0x54,
-0xb8,
-0x1d,
-0x0,
-0x0,
-0x1,
-0x35,
-0x42,
-0x58,
-0x50,
-0x43,
-0x0,
-0x0,
-0x42,
-0x58,
-0x44,
-0x53,
-0x44,
-0x54,
-0x0,
-0x0,
-0x2,
-0x0,
-0x0,
-0x0,
-0x49,
-0x4e,
-0x54,
-0x4c,
-0x7,
-0x11,
-0x14,
-0x20,
-0x10,
-0x49,
-0x4,
-0x5c,
-0x0,
-0x5b,
-0x80,
-0x44,
-0x42,
-0x47,
-0x5f,
-0x1,
-0xb,
-0x2,
-0x4,
-0x1,
-0x5b,
-0x81,
-0xb,
-0x44,
-0x42,
-0x47,
-0x5f,
-0x1,
-0x44,
-0x42,
-0x47,
-0x42,
-0x8,
-0x14,
-0x2c,
-0x44,
-0x42,
-0x55,
-0x47,
-0x1,
-0x98,
-0x68,
-0x60,
-0x96,
-0x60,
-0x60,
-0x74,
-0x87,
-0x60,
-0x1,
-0x61,
-0x70,
-0x0,
-0x62,
-0xa2,
-0x10,
-0x95,
-0x62,
-0x61,
-0x70,
-0x83,
-0x88,
-0x60,
-0x62,
-0x0,
-0x44,
-0x42,
-0x47,
-0x42,
-0x75,
-0x62,
-0x70,
-0xa,
-0xa,
-0x44,
-0x42,
-0x47,
-0x42,
-0x10,
-0x29,
-0x5f,
-0x53,
-0x42,
-0x5f,
-0x5b,
-0x80,
-0x50,
-0x43,
-0x53,
-0x54,
-0x1,
-0xb,
-0x0,
-0xae,
-0xa,
-0xc,
-0x5b,
-0x80,
-0x50,
-0x43,
-0x53,
-0x42,
-0x1,
-0xb,
-0xc,
-0xae,
-0x1,
-0x5b,
-0x81,
-0xb,
-0x50,
-0x43,
-0x53,
-0x42,
-0x40,
-0x50,
-0x43,
-0x49,
-0x42,
-0x8,
-0x10,
-0x4f,
-0xc,
-0x5f,
-0x53,
-0x42,
-0x5f,
-0x5b,
-0x82,
-0x47,
-0xc,
-0x50,
-0x43,
-0x49,
-0x30,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xa,
-0x8,
-0x8,
-0x5f,
-0x43,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xa,
-0x3,
-0x8,
-0x5f,
-0x41,
-0x44,
-0x52,
-0x0,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0x1,
-0x8,
-0x53,
-0x55,
-0x50,
-0x50,
-0x0,
-0x8,
-0x43,
-0x54,
-0x52,
-0x4c,
-0x0,
-0x14,
-0x44,
-0x9,
-0x5f,
-0x4f,
-0x53,
-0x43,
-0x4,
-0x8a,
-0x6b,
-0x0,
-0x43,
-0x44,
-0x57,
-0x31,
-0xa0,
-0x46,
-0x7,
-0x93,
-0x68,
-0x11,
-0x13,
-0xa,
-0x10,
-0x5b,
-0x4d,
-0xdb,
-0x33,
-0xf7,
-0x1f,
-0x1c,
-0x40,
-0x96,
-0x57,
-0x74,
-0x41,
-0xc0,
-0x3d,
-0xd7,
-0x66,
-0x8a,
-0x6b,
-0xa,
-0x4,
-0x43,
-0x44,
-0x57,
-0x32,
-0x8a,
-0x6b,
-0xa,
-0x8,
-0x43,
-0x44,
-0x57,
-0x33,
-0x70,
-0x43,
-0x44,
-0x57,
-0x32,
-0x53,
-0x55,
-0x50,
-0x50,
-0x70,
-0x43,
-0x44,
-0x57,
-0x33,
-0x43,
-0x54,
-0x52,
-0x4c,
-0x7b,
-0x43,
-0x54,
-0x52,
-0x4c,
-0xa,
-0x1d,
-0x43,
-0x54,
-0x52,
-0x4c,
-0xa0,
-0x10,
-0x92,
-0x93,
-0x69,
-0x1,
-0x7d,
-0x43,
-0x44,
-0x57,
-0x31,
-0xa,
-0x8,
-0x43,
-0x44,
-0x57,
-0x31,
-0xa0,
-0x16,
-0x92,
-0x93,
-0x43,
-0x44,
-0x57,
-0x33,
-0x43,
-0x54,
-0x52,
-0x4c,
-0x7d,
-0x43,
-0x44,
-0x57,
-0x31,
-0xa,
-0x10,
-0x43,
-0x44,
-0x57,
-0x31,
-0x70,
-0x43,
-0x54,
-0x52,
-0x4c,
-0x43,
-0x44,
-0x57,
-0x33,
-0xa1,
-0xc,
-0x7d,
-0x43,
-0x44,
-0x57,
-0x31,
-0xa,
-0x4,
-0x43,
-0x44,
-0x57,
-0x31,
-0xa4,
-0x6b,
-0x10,
-0x4d,
-0x8,
-0x5f,
-0x53,
-0x42,
-0x5f,
-0x5b,
-0x82,
-0x45,
-0x8,
-0x48,
-0x50,
-0x45,
-0x54,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0x1,
-0x3,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0x0,
-0x5b,
-0x80,
-0x48,
-0x50,
-0x54,
-0x4d,
-0x0,
-0xc,
-0x0,
-0x0,
-0xd0,
-0xfe,
-0xb,
-0x0,
-0x4,
-0x5b,
-0x81,
-0x10,
-0x48,
-0x50,
-0x54,
-0x4d,
-0x13,
-0x56,
-0x45,
-0x4e,
-0x44,
-0x20,
-0x50,
-0x52,
-0x44,
-0x5f,
-0x20,
-0x14,
-0x36,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0x70,
-0x56,
-0x45,
-0x4e,
-0x44,
-0x60,
-0x70,
-0x50,
-0x52,
-0x44,
-0x5f,
-0x61,
-0x7a,
-0x60,
-0xa,
-0x10,
-0x60,
-0xa0,
-0xc,
-0x91,
-0x93,
-0x60,
-0x0,
-0x93,
-0x60,
-0xb,
-0xff,
-0xff,
-0xa4,
-0x0,
-0xa0,
-0xe,
-0x91,
-0x93,
-0x61,
-0x0,
-0x94,
-0x61,
-0xc,
-0x0,
-0xe1,
-0xf5,
-0x5,
-0xa4,
-0x0,
-0xa4,
-0xa,
-0xf,
-0x8,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x11,
-0x11,
-0xa,
-0xe,
-0x86,
-0x9,
-0x0,
-0x0,
-0x0,
-0x0,
-0xd0,
-0xfe,
-0x0,
-0x4,
-0x0,
-0x0,
-0x79,
-0x0,
-0x10,
-0x4c,
-0x7,
-0x2e,
-0x5f,
-0x53,
-0x42,
-0x5f,
-0x50,
-0x43,
-0x49,
-0x30,
-0x5b,
-0x82,
-0x4f,
-0x6,
-0x49,
-0x53,
-0x41,
-0x5f,
-0x8,
-0x5f,
-0x41,
-0x44,
-0x52,
-0xc,
-0x0,
-0x0,
-0x1f,
-0x0,
-0x5b,
-0x80,
-0x50,
-0x49,
-0x52,
-0x51,
-0x2,
-0xa,
-0x60,
-0xa,
-0xc,
-0x5b,
-0x80,
-0x4c,
-0x50,
-0x43,
-0x44,
-0x2,
-0xa,
-0x80,
-0xa,
-0x2,
-0x5b,
-0x81,
-0x20,
-0x4c,
-0x50,
-0x43,
-0x44,
-0x0,
-0x43,
-0x4f,
-0x4d,
-0x41,
-0x3,
-0x0,
-0x1,
-0x43,
-0x4f,
-0x4d,
-0x42,
-0x3,
-0x0,
-0x1,
-0x4c,
-0x50,
-0x54,
-0x44,
-0x2,
-0x0,
-0x2,
-0x46,
-0x44,
-0x43,
-0x44,
-0x2,
-0x5b,
-0x80,
-0x4c,
-0x50,
-0x43,
-0x45,
-0x2,
-0xa,
-0x82,
-0xa,
-0x2,
-0x5b,
-0x81,
-0x1a,
-0x4c,
-0x50,
-0x43,
-0x45,
-0x0,
-0x43,
-0x41,
-0x45,
-0x4e,
-0x1,
-0x43,
-0x42,
-0x45,
-0x4e,
-0x1,
-0x4c,
-0x50,
-0x45,
-0x4e,
-0x1,
-0x46,
-0x44,
-0x45,
-0x4e,
-0x1,
-0x10,
-0x4c,
-0x1b,
-0x2f,
-0x3,
-0x5f,
-0x53,
-0x42,
-0x5f,
-0x50,
-0x43,
-0x49,
-0x30,
-0x49,
-0x53,
-0x41,
-0x5f,
-0x5b,
-0x82,
-0x2d,
-0x52,
-0x54,
-0x43,
-0x5f,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xb,
-0x0,
-0x8,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x11,
-0x18,
-0xa,
-0x15,
-0x47,
-0x1,
-0x70,
-0x0,
-0x70,
-0x0,
-0x10,
-0x2,
-0x22,
-0x0,
-0x1,
-0x47,
-0x1,
-0x72,
-0x0,
-0x72,
-0x0,
-0x2,
-0x6,
-0x79,
-0x0,
-0x5b,
-0x82,
-0x37,
-0x4b,
-0x42,
-0x44,
-0x5f,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0x3,
-0x3,
-0x14,
-0x9,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0xa4,
-0xa,
-0xf,
-0x8,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x11,
-0x18,
-0xa,
-0x15,
-0x47,
-0x1,
-0x60,
-0x0,
-0x60,
-0x0,
-0x1,
-0x1,
-0x47,
-0x1,
-0x64,
-0x0,
-0x64,
-0x0,
-0x1,
-0x1,
-0x22,
-0x2,
-0x0,
-0x79,
-0x0,
-0x5b,
-0x82,
-0x27,
-0x4d,
-0x4f,
-0x55,
-0x5f,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xf,
-0x13,
-0x14,
-0x9,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0xa4,
-0xa,
-0xf,
-0x8,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x11,
-0x8,
-0xa,
-0x5,
-0x22,
-0x0,
-0x10,
-0x79,
-0x0,
-0x5b,
-0x82,
-0x4a,
-0x4,
-0x46,
-0x44,
-0x43,
-0x30,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0x7,
-0x0,
-0x14,
-0x18,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0x70,
-0x46,
-0x44,
-0x45,
-0x4e,
-0x60,
-0xa0,
-0x6,
-0x93,
-0x60,
-0x0,
-0xa4,
-0x0,
-0xa1,
-0x4,
-0xa4,
-0xa,
-0xf,
-0x8,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x11,
-0x1b,
-0xa,
-0x18,
-0x47,
-0x1,
-0xf2,
-0x3,
-0xf2,
-0x3,
-0x0,
-0x4,
-0x47,
-0x1,
-0xf7,
-0x3,
-0xf7,
-0x3,
-0x0,
-0x1,
-0x22,
-0x40,
-0x0,
-0x2a,
-0x4,
-0x0,
-0x79,
-0x0,
-0x5b,
-0x82,
-0x3e,
-0x4c,
-0x50,
-0x54,
-0x5f,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0x4,
-0x0,
-0x14,
-0x18,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0x70,
-0x4c,
-0x50,
-0x45,
-0x4e,
-0x60,
-0xa0,
-0x6,
-0x93,
-0x60,
-0x0,
-0xa4,
-0x0,
-0xa1,
-0x4,
-0xa4,
-0xa,
-0xf,
-0x8,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x11,
-0x10,
-0xa,
-0xd,
-0x47,
-0x1,
-0x78,
-0x3,
-0x78,
-0x3,
-0x8,
-0x8,
-0x22,
-0x80,
-0x0,
-0x79,
-0x0,
-0x5b,
-0x82,
-0x45,
-0x4,
-0x43,
-0x4f,
-0x4d,
-0x31,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0x5,
-0x1,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0x1,
-0x14,
-0x18,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0x70,
-0x43,
-0x41,
-0x45,
-0x4e,
-0x60,
-0xa0,
-0x6,
-0x93,
-0x60,
-0x0,
-0xa4,
-0x0,
-0xa1,
-0x4,
-0xa4,
-0xa,
-0xf,
-0x8,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x11,
-0x10,
-0xa,
-0xd,
-0x47,
-0x1,
-0xf8,
-0x3,
-0xf8,
-0x3,
-0x0,
-0x8,
-0x22,
-0x10,
-0x0,
-0x79,
-0x0,
-0x5b,
-0x82,
-0x46,
-0x4,
-0x43,
-0x4f,
-0x4d,
-0x32,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0x5,
-0x1,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0xa,
-0x2,
-0x14,
-0x18,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0x70,
-0x43,
-0x42,
-0x45,
-0x4e,
-0x60,
-0xa0,
-0x6,
-0x93,
-0x60,
-0x0,
-0xa4,
-0x0,
-0xa1,
-0x4,
-0xa4,
-0xa,
-0xf,
-0x8,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x11,
-0x10,
-0xa,
-0xd,
-0x47,
-0x1,
-0xf8,
-0x2,
-0xf8,
-0x2,
-0x0,
-0x8,
-0x22,
-0x8,
-0x0,
-0x79,
-0x0,
-0x8,
-0x50,
-0x49,
-0x43,
-0x46,
-0x0,
-0x14,
-0xc,
-0x5f,
-0x50,
-0x49,
-0x43,
-0x1,
-0x70,
-0x68,
-0x50,
-0x49,
-0x43,
-0x46,
-0x10,
-0x8e,
-0x55,
-0x1,
-0x5f,
-0x53,
-0x42,
-0x5f,
-0x10,
-0x43,
-0xea,
-0x50,
-0x43,
-0x49,
-0x30,
-0x8,
-0x50,
-0x52,
-0x54,
-0x50,
-0x12,
-0x4b,
-0x73,
-0x80,
-0x12,
-0xb,
-0x4,
-0xb,
-0xff,
-0xff,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xb,
-0x4,
-0xb,
-0xff,
-0xff,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xc,
-0x4,
-0xb,
-0xff,
-0xff,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xc,
-0x4,
-0xb,
-0xff,
-0xff,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x2,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x2,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x2,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x2,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x3,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x3,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x3,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x3,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x4,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x4,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x4,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x4,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x5,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x5,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x5,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x5,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x6,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x6,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x6,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x6,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x7,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x7,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x7,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x7,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x8,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x8,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x8,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x8,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x9,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x9,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x9,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x9,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0xa,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0xa,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0xa,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0xa,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0xb,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0xb,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0xb,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0xb,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0xc,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0xc,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0xc,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0xc,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0xd,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0xd,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0xd,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0xd,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0xe,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0xe,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0xe,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0xe,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0xf,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0xf,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0xf,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0xf,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x10,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x10,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x10,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x10,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x11,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x11,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x11,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x11,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x12,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x12,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x12,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x12,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x13,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x13,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x13,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x13,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x14,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x14,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x14,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x14,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x15,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x15,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x15,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x15,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x16,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x16,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x16,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x16,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x17,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x17,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x17,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x17,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x18,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x18,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x18,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x18,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x19,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x41,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x19,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x42,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x19,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x43,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x19,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x44,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1a,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x41,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1a,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x42,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1a,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x43,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1a,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x44,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1b,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x41,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1b,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x42,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1b,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x43,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1b,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x44,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1c,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x41,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1c,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x42,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1c,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x43,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1c,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x44,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1d,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x41,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1d,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x42,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1d,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x43,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1d,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x44,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1e,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1e,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1e,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1e,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1f,
-0x0,
-0x0,
-0x4c,
-0x4e,
-0x4b,
-0x41,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1f,
-0x0,
-0x1,
-0x4c,
-0x4e,
-0x4b,
-0x42,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1f,
-0x0,
-0xa,
-0x2,
-0x4c,
-0x4e,
-0x4b,
-0x43,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1f,
-0x0,
-0xa,
-0x3,
-0x4c,
-0x4e,
-0x4b,
-0x44,
-0x0,
-0x8,
-0x50,
-0x52,
-0x54,
-0x41,
-0x12,
-0x4b,
-0x73,
-0x80,
-0x12,
-0xb,
-0x4,
-0xb,
-0xff,
-0xff,
-0x0,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xb,
-0x4,
-0xb,
-0xff,
-0xff,
-0x1,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xc,
-0x4,
-0xb,
-0xff,
-0xff,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xc,
-0x4,
-0xb,
-0xff,
-0xff,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x2,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x2,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x2,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x2,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x3,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x3,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x3,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x3,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x4,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x4,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x4,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x4,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x5,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x5,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x5,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x5,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x6,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x6,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x6,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x6,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x7,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x7,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x7,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x7,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x8,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x8,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x8,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x8,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x9,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x9,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x9,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x9,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0xa,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0xa,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0xa,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0xa,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0xb,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0xb,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0xb,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0xb,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0xc,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0xc,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0xc,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0xc,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0xd,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0xd,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0xd,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0xd,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0xe,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0xe,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0xe,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0xe,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0xf,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0xf,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0xf,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0xf,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x10,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x10,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x10,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x10,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x11,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x11,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x11,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x11,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x12,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x12,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x12,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x12,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x13,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x13,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x13,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x13,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x14,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x14,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x14,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x14,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x15,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x15,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x15,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x15,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x16,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x16,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x16,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x16,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x17,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x17,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x17,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x17,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x18,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x18,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x18,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x18,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x19,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x41,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x19,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x42,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x19,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x43,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x19,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x44,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1a,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x41,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1a,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x42,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1a,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x43,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1a,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x44,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1b,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x41,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1b,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x42,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1b,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x43,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1b,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x44,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1c,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x41,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1c,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x42,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1c,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x43,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1c,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x44,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1d,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x41,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1d,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x42,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1d,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x43,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1d,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x44,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1e,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x45,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1e,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x46,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1e,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x47,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1e,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x48,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1f,
-0x0,
-0x0,
-0x47,
-0x53,
-0x49,
-0x41,
-0x0,
-0x12,
-0xd,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1f,
-0x0,
-0x1,
-0x47,
-0x53,
-0x49,
-0x42,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1f,
-0x0,
-0xa,
-0x2,
-0x47,
-0x53,
-0x49,
-0x43,
-0x0,
-0x12,
-0xe,
-0x4,
-0xc,
-0xff,
-0xff,
-0x1f,
-0x0,
-0xa,
-0x3,
-0x47,
-0x53,
-0x49,
-0x44,
-0x0,
-0x14,
-0x1a,
-0x5f,
-0x50,
-0x52,
-0x54,
-0x0,
-0xa0,
-0xc,
-0x93,
-0x50,
-0x49,
-0x43,
-0x46,
-0x0,
-0xa4,
-0x50,
-0x52,
-0x54,
-0x50,
-0xa1,
-0x6,
-0xa4,
-0x50,
-0x52,
-0x54,
-0x41,
-0x5b,
-0x81,
-0x3a,
-0x2f,
-0x3,
-0x50,
-0x43,
-0x49,
-0x30,
-0x49,
-0x53,
-0x41,
-0x5f,
-0x50,
-0x49,
-0x52,
-0x51,
-0x1,
-0x50,
-0x52,
-0x51,
-0x41,
-0x8,
-0x50,
-0x52,
-0x51,
-0x42,
-0x8,
-0x50,
-0x52,
-0x51,
-0x43,
-0x8,
-0x50,
-0x52,
-0x51,
-0x44,
-0x8,
-0x0,
-0x20,
-0x50,
-0x52,
-0x51,
-0x45,
-0x8,
-0x50,
-0x52,
-0x51,
-0x46,
-0x8,
-0x50,
-0x52,
-0x51,
-0x47,
-0x8,
-0x50,
-0x52,
-0x51,
-0x48,
-0x8,
-0x14,
-0x13,
-0x49,
-0x51,
-0x53,
-0x54,
-0x1,
-0xa0,
-0x9,
-0x7b,
-0xa,
-0x80,
-0x68,
-0x0,
-0xa4,
-0xa,
-0x9,
-0xa4,
-0xa,
-0xb,
-0x14,
-0x34,
-0x49,
-0x51,
-0x43,
-0x52,
-0x9,
-0x8,
-0x50,
-0x52,
-0x52,
-0x30,
-0x11,
-0xe,
-0xa,
-0xb,
-0x89,
-0x6,
-0x0,
-0x9,
-0x1,
-0x0,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x8a,
-0x50,
-0x52,
-0x52,
-0x30,
-0xa,
-0x5,
-0x50,
-0x52,
-0x52,
-0x49,
-0x70,
-0x7b,
-0x68,
-0xa,
-0xf,
-0x0,
-0x50,
-0x52,
-0x52,
-0x49,
-0xa4,
-0x50,
-0x52,
-0x52,
-0x30,
-0x5b,
-0x82,
-0x4c,
-0x7,
-0x4c,
-0x4e,
-0x4b,
-0x41,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xc,
-0xf,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0x0,
-0x8,
-0x5f,
-0x50,
-0x52,
-0x53,
-0x11,
-0x16,
-0xa,
-0x13,
-0x89,
-0xe,
-0x0,
-0x9,
-0x3,
-0x5,
-0x0,
-0x0,
-0x0,
-0xa,
-0x0,
-0x0,
-0x0,
-0xb,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x14,
-0xf,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0xa4,
-0x49,
-0x51,
-0x53,
-0x54,
-0x50,
-0x52,
-0x51,
-0x41,
-0x14,
-0x11,
-0x5f,
-0x44,
-0x49,
-0x53,
-0x0,
-0x7d,
-0x50,
-0x52,
-0x51,
-0x41,
-0xa,
-0x80,
-0x50,
-0x52,
-0x51,
-0x41,
-0x14,
-0xf,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x0,
-0xa4,
-0x49,
-0x51,
-0x43,
-0x52,
-0x50,
-0x52,
-0x51,
-0x41,
-0x14,
-0x17,
-0x5f,
-0x53,
-0x52,
-0x53,
-0x1,
-0x8a,
-0x68,
-0xa,
-0x5,
-0x50,
-0x52,
-0x52,
-0x49,
-0x70,
-0x50,
-0x52,
-0x52,
-0x49,
-0x50,
-0x52,
-0x51,
-0x41,
-0x5b,
-0x82,
-0x4c,
-0x7,
-0x4c,
-0x4e,
-0x4b,
-0x42,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xc,
-0xf,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0x1,
-0x8,
-0x5f,
-0x50,
-0x52,
-0x53,
-0x11,
-0x16,
-0xa,
-0x13,
-0x89,
-0xe,
-0x0,
-0x9,
-0x3,
-0x5,
-0x0,
-0x0,
-0x0,
-0xa,
-0x0,
-0x0,
-0x0,
-0xb,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x14,
-0xf,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0xa4,
-0x49,
-0x51,
-0x53,
-0x54,
-0x50,
-0x52,
-0x51,
-0x42,
-0x14,
-0x11,
-0x5f,
-0x44,
-0x49,
-0x53,
-0x0,
-0x7d,
-0x50,
-0x52,
-0x51,
-0x42,
-0xa,
-0x80,
-0x50,
-0x52,
-0x51,
-0x42,
-0x14,
-0xf,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x0,
-0xa4,
-0x49,
-0x51,
-0x43,
-0x52,
-0x50,
-0x52,
-0x51,
-0x42,
-0x14,
-0x17,
-0x5f,
-0x53,
-0x52,
-0x53,
-0x1,
-0x8a,
-0x68,
-0xa,
-0x5,
-0x50,
-0x52,
-0x52,
-0x49,
-0x70,
-0x50,
-0x52,
-0x52,
-0x49,
-0x50,
-0x52,
-0x51,
-0x42,
-0x5b,
-0x82,
-0x4d,
-0x7,
-0x4c,
-0x4e,
-0x4b,
-0x43,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xc,
-0xf,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0xa,
-0x2,
-0x8,
-0x5f,
-0x50,
-0x52,
-0x53,
-0x11,
-0x16,
-0xa,
-0x13,
-0x89,
-0xe,
-0x0,
-0x9,
-0x3,
-0x5,
-0x0,
-0x0,
-0x0,
-0xa,
-0x0,
-0x0,
-0x0,
-0xb,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x14,
-0xf,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0xa4,
-0x49,
-0x51,
-0x53,
-0x54,
-0x50,
-0x52,
-0x51,
-0x43,
-0x14,
-0x11,
-0x5f,
-0x44,
-0x49,
-0x53,
-0x0,
-0x7d,
-0x50,
-0x52,
-0x51,
-0x43,
-0xa,
-0x80,
-0x50,
-0x52,
-0x51,
-0x43,
-0x14,
-0xf,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x0,
-0xa4,
-0x49,
-0x51,
-0x43,
-0x52,
-0x50,
-0x52,
-0x51,
-0x43,
-0x14,
-0x17,
-0x5f,
-0x53,
-0x52,
-0x53,
-0x1,
-0x8a,
-0x68,
-0xa,
-0x5,
-0x50,
-0x52,
-0x52,
-0x49,
-0x70,
-0x50,
-0x52,
-0x52,
-0x49,
-0x50,
-0x52,
-0x51,
-0x43,
-0x5b,
-0x82,
-0x4d,
-0x7,
-0x4c,
-0x4e,
-0x4b,
-0x44,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xc,
-0xf,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0xa,
-0x3,
-0x8,
-0x5f,
-0x50,
-0x52,
-0x53,
-0x11,
-0x16,
-0xa,
-0x13,
-0x89,
-0xe,
-0x0,
-0x9,
-0x3,
-0x5,
-0x0,
-0x0,
-0x0,
-0xa,
-0x0,
-0x0,
-0x0,
-0xb,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x14,
-0xf,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0xa4,
-0x49,
-0x51,
-0x53,
-0x54,
-0x50,
-0x52,
-0x51,
-0x44,
-0x14,
-0x11,
-0x5f,
-0x44,
-0x49,
-0x53,
-0x0,
-0x7d,
-0x50,
-0x52,
-0x51,
-0x44,
-0xa,
-0x80,
-0x50,
-0x52,
-0x51,
-0x44,
-0x14,
-0xf,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x0,
-0xa4,
-0x49,
-0x51,
-0x43,
-0x52,
-0x50,
-0x52,
-0x51,
-0x44,
-0x14,
-0x17,
-0x5f,
-0x53,
-0x52,
-0x53,
-0x1,
-0x8a,
-0x68,
-0xa,
-0x5,
-0x50,
-0x52,
-0x52,
-0x49,
-0x70,
-0x50,
-0x52,
-0x52,
-0x49,
-0x50,
-0x52,
-0x51,
-0x44,
-0x5b,
-0x82,
-0x4d,
-0x7,
-0x4c,
-0x4e,
-0x4b,
-0x45,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xc,
-0xf,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0xa,
-0x4,
-0x8,
-0x5f,
-0x50,
-0x52,
-0x53,
-0x11,
-0x16,
-0xa,
-0x13,
-0x89,
-0xe,
-0x0,
-0x9,
-0x3,
-0x5,
-0x0,
-0x0,
-0x0,
-0xa,
-0x0,
-0x0,
-0x0,
-0xb,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x14,
-0xf,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0xa4,
-0x49,
-0x51,
-0x53,
-0x54,
-0x50,
-0x52,
-0x51,
-0x45,
-0x14,
-0x11,
-0x5f,
-0x44,
-0x49,
-0x53,
-0x0,
-0x7d,
-0x50,
-0x52,
-0x51,
-0x45,
-0xa,
-0x80,
-0x50,
-0x52,
-0x51,
-0x45,
-0x14,
-0xf,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x0,
-0xa4,
-0x49,
-0x51,
-0x43,
-0x52,
-0x50,
-0x52,
-0x51,
-0x45,
-0x14,
-0x17,
-0x5f,
-0x53,
-0x52,
-0x53,
-0x1,
-0x8a,
-0x68,
-0xa,
-0x5,
-0x50,
-0x52,
-0x52,
-0x49,
-0x70,
-0x50,
-0x52,
-0x52,
-0x49,
-0x50,
-0x52,
-0x51,
-0x45,
-0x5b,
-0x82,
-0x4d,
-0x7,
-0x4c,
-0x4e,
-0x4b,
-0x46,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xc,
-0xf,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0xa,
-0x5,
-0x8,
-0x5f,
-0x50,
-0x52,
-0x53,
-0x11,
-0x16,
-0xa,
-0x13,
-0x89,
-0xe,
-0x0,
-0x9,
-0x3,
-0x5,
-0x0,
-0x0,
-0x0,
-0xa,
-0x0,
-0x0,
-0x0,
-0xb,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x14,
-0xf,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0xa4,
-0x49,
-0x51,
-0x53,
-0x54,
-0x50,
-0x52,
-0x51,
-0x46,
-0x14,
-0x11,
-0x5f,
-0x44,
-0x49,
-0x53,
-0x0,
-0x7d,
-0x50,
-0x52,
-0x51,
-0x46,
-0xa,
-0x80,
-0x50,
-0x52,
-0x51,
-0x46,
-0x14,
-0xf,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x0,
-0xa4,
-0x49,
-0x51,
-0x43,
-0x52,
-0x50,
-0x52,
-0x51,
-0x46,
-0x14,
-0x17,
-0x5f,
-0x53,
-0x52,
-0x53,
-0x1,
-0x8a,
-0x68,
-0xa,
-0x5,
-0x50,
-0x52,
-0x52,
-0x49,
-0x70,
-0x50,
-0x52,
-0x52,
-0x49,
-0x50,
-0x52,
-0x51,
-0x46,
-0x5b,
-0x82,
-0x4d,
-0x7,
-0x4c,
-0x4e,
-0x4b,
-0x47,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xc,
-0xf,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0xa,
-0x6,
-0x8,
-0x5f,
-0x50,
-0x52,
-0x53,
-0x11,
-0x16,
-0xa,
-0x13,
-0x89,
-0xe,
-0x0,
-0x9,
-0x3,
-0x5,
-0x0,
-0x0,
-0x0,
-0xa,
-0x0,
-0x0,
-0x0,
-0xb,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x14,
-0xf,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0xa4,
-0x49,
-0x51,
-0x53,
-0x54,
-0x50,
-0x52,
-0x51,
-0x47,
-0x14,
-0x11,
-0x5f,
-0x44,
-0x49,
-0x53,
-0x0,
-0x7d,
-0x50,
-0x52,
-0x51,
-0x47,
-0xa,
-0x80,
-0x50,
-0x52,
-0x51,
-0x47,
-0x14,
-0xf,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x0,
-0xa4,
-0x49,
-0x51,
-0x43,
-0x52,
-0x50,
-0x52,
-0x51,
-0x47,
-0x14,
-0x17,
-0x5f,
-0x53,
-0x52,
-0x53,
-0x1,
-0x8a,
-0x68,
-0xa,
-0x5,
-0x50,
-0x52,
-0x52,
-0x49,
-0x70,
-0x50,
-0x52,
-0x52,
-0x49,
-0x50,
-0x52,
-0x51,
-0x47,
-0x5b,
-0x82,
-0x4d,
-0x7,
-0x4c,
-0x4e,
-0x4b,
-0x48,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xc,
-0xf,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0xa,
-0x7,
-0x8,
-0x5f,
-0x50,
-0x52,
-0x53,
-0x11,
-0x16,
-0xa,
-0x13,
-0x89,
-0xe,
-0x0,
-0x9,
-0x3,
-0x5,
-0x0,
-0x0,
-0x0,
-0xa,
-0x0,
-0x0,
-0x0,
-0xb,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x14,
-0xf,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0xa4,
-0x49,
-0x51,
-0x53,
-0x54,
-0x50,
-0x52,
-0x51,
-0x48,
-0x14,
-0x11,
-0x5f,
-0x44,
-0x49,
-0x53,
-0x0,
-0x7d,
-0x50,
-0x52,
-0x51,
-0x48,
-0xa,
-0x80,
-0x50,
-0x52,
-0x51,
-0x48,
-0x14,
-0xf,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x0,
-0xa4,
-0x49,
-0x51,
-0x43,
-0x52,
-0x50,
-0x52,
-0x51,
-0x48,
-0x14,
-0x17,
-0x5f,
-0x53,
-0x52,
-0x53,
-0x1,
-0x8a,
-0x68,
-0xa,
-0x5,
-0x50,
-0x52,
-0x52,
-0x49,
-0x70,
-0x50,
-0x52,
-0x52,
-0x49,
-0x50,
-0x52,
-0x51,
-0x48,
-0x5b,
-0x82,
-0x45,
-0x4,
-0x47,
-0x53,
-0x49,
-0x41,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xc,
-0xf,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0x0,
-0x8,
-0x5f,
-0x50,
-0x52,
-0x53,
-0x11,
-0xe,
-0xa,
-0xb,
-0x89,
-0x6,
-0x0,
-0x9,
-0x1,
-0x10,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x8,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x11,
-0xe,
-0xa,
-0xb,
-0x89,
-0x6,
-0x0,
-0x9,
-0x1,
-0x10,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x53,
-0x52,
-0x53,
-0x1,
-0x5b,
-0x82,
-0x45,
-0x4,
-0x47,
-0x53,
-0x49,
-0x42,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xc,
-0xf,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0x0,
-0x8,
-0x5f,
-0x50,
-0x52,
-0x53,
-0x11,
-0xe,
-0xa,
-0xb,
-0x89,
-0x6,
-0x0,
-0x9,
-0x1,
-0x11,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x8,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x11,
-0xe,
-0xa,
-0xb,
-0x89,
-0x6,
-0x0,
-0x9,
-0x1,
-0x11,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x53,
-0x52,
-0x53,
-0x1,
-0x5b,
-0x82,
-0x45,
-0x4,
-0x47,
-0x53,
-0x49,
-0x43,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xc,
-0xf,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0x0,
-0x8,
-0x5f,
-0x50,
-0x52,
-0x53,
-0x11,
-0xe,
-0xa,
-0xb,
-0x89,
-0x6,
-0x0,
-0x9,
-0x1,
-0x12,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x8,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x11,
-0xe,
-0xa,
-0xb,
-0x89,
-0x6,
-0x0,
-0x9,
-0x1,
-0x12,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x53,
-0x52,
-0x53,
-0x1,
-0x5b,
-0x82,
-0x45,
-0x4,
-0x47,
-0x53,
-0x49,
-0x44,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xc,
-0xf,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0x0,
-0x8,
-0x5f,
-0x50,
-0x52,
-0x53,
-0x11,
-0xe,
-0xa,
-0xb,
-0x89,
-0x6,
-0x0,
-0x9,
-0x1,
-0x13,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x8,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x11,
-0xe,
-0xa,
-0xb,
-0x89,
-0x6,
-0x0,
-0x9,
-0x1,
-0x13,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x53,
-0x52,
-0x53,
-0x1,
-0x5b,
-0x82,
-0x45,
-0x4,
-0x47,
-0x53,
-0x49,
-0x45,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xc,
-0xf,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0x0,
-0x8,
-0x5f,
-0x50,
-0x52,
-0x53,
-0x11,
-0xe,
-0xa,
-0xb,
-0x89,
-0x6,
-0x0,
-0x9,
-0x1,
-0x14,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x8,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x11,
-0xe,
-0xa,
-0xb,
-0x89,
-0x6,
-0x0,
-0x9,
-0x1,
-0x14,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x53,
-0x52,
-0x53,
-0x1,
-0x5b,
-0x82,
-0x45,
-0x4,
-0x47,
-0x53,
-0x49,
-0x46,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xc,
-0xf,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0x0,
-0x8,
-0x5f,
-0x50,
-0x52,
-0x53,
-0x11,
-0xe,
-0xa,
-0xb,
-0x89,
-0x6,
-0x0,
-0x9,
-0x1,
-0x15,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x8,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x11,
-0xe,
-0xa,
-0xb,
-0x89,
-0x6,
-0x0,
-0x9,
-0x1,
-0x15,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x53,
-0x52,
-0x53,
-0x1,
-0x5b,
-0x82,
-0x45,
-0x4,
-0x47,
-0x53,
-0x49,
-0x47,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xc,
-0xf,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0x0,
-0x8,
-0x5f,
-0x50,
-0x52,
-0x53,
-0x11,
-0xe,
-0xa,
-0xb,
-0x89,
-0x6,
-0x0,
-0x9,
-0x1,
-0x16,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x8,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x11,
-0xe,
-0xa,
-0xb,
-0x89,
-0x6,
-0x0,
-0x9,
-0x1,
-0x16,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x53,
-0x52,
-0x53,
-0x1,
-0x5b,
-0x82,
-0x45,
-0x4,
-0x47,
-0x53,
-0x49,
-0x48,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xc,
-0xf,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0x0,
-0x8,
-0x5f,
-0x50,
-0x52,
-0x53,
-0x11,
-0xe,
-0xa,
-0xb,
-0x89,
-0x6,
-0x0,
-0x9,
-0x1,
-0x17,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x8,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x11,
-0xe,
-0xa,
-0xb,
-0x89,
-0x6,
-0x0,
-0x9,
-0x1,
-0x17,
-0x0,
-0x0,
-0x0,
-0x79,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x53,
-0x52,
-0x53,
-0x1,
-0x10,
-0x4d,
-0xc,
-0x5f,
-0x53,
-0x42,
-0x5f,
-0x14,
-0x35,
-0x43,
-0x50,
-0x4d,
-0x41,
-0x1,
-0x70,
-0x83,
-0x88,
-0x43,
-0x50,
-0x4f,
-0x4e,
-0x68,
-0x0,
-0x60,
-0x70,
-0x11,
-0xb,
-0xa,
-0x8,
-0x0,
-0x8,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x61,
-0x70,
-0x68,
-0x88,
-0x61,
-0xa,
-0x2,
-0x0,
-0x70,
-0x68,
-0x88,
-0x61,
-0xa,
-0x3,
-0x0,
-0x70,
-0x60,
-0x88,
-0x61,
-0xa,
-0x4,
-0x0,
-0xa4,
-0x61,
-0x14,
-0x1a,
-0x43,
-0x50,
-0x53,
-0x54,
-0x1,
-0x70,
-0x83,
-0x88,
-0x43,
-0x50,
-0x4f,
-0x4e,
-0x68,
-0x0,
-0x60,
-0xa0,
-0x5,
-0x60,
-0xa4,
-0xa,
-0xf,
-0xa1,
-0x3,
-0xa4,
-0x0,
-0x14,
-0xa,
-0x43,
-0x50,
-0x45,
-0x4a,
-0x2,
-0x5b,
-0x22,
-0xa,
-0xc8,
-0x14,
-0x4a,
-0x6,
-0x50,
-0x52,
-0x53,
-0x43,
-0x0,
-0x70,
-0x50,
-0x52,
-0x53,
-0x5f,
-0x65,
-0x70,
-0x0,
-0x62,
-0x70,
-0x0,
-0x60,
-0xa2,
-0x46,
-0x5,
-0x95,
-0x60,
-0x87,
-0x43,
-0x50,
-0x4f,
-0x4e,
-0x70,
-0x83,
-0x88,
-0x43,
-0x50,
-0x4f,
-0x4e,
-0x60,
-0x0,
-0x61,
-0xa0,
-0xa,
-0x7b,
-0x60,
-0xa,
-0x7,
-0x0,
-0x7a,
-0x62,
-0x1,
-0x62,
-0xa1,
-0xc,
-0x70,
-0x83,
-0x88,
-0x65,
-0x7a,
-0x60,
-0xa,
-0x3,
-0x0,
-0x0,
-0x62,
-0x70,
-0x7b,
-0x62,
-0x1,
-0x0,
-0x63,
-0xa0,
-0x22,
-0x92,
-0x93,
-0x61,
-0x63,
-0x70,
-0x63,
-0x88,
-0x43,
-0x50,
-0x4f,
-0x4e,
-0x60,
-0x0,
-0xa0,
-0xa,
-0x93,
-0x63,
-0x1,
-0x4e,
-0x54,
-0x46,
-0x59,
-0x60,
-0x1,
-0xa1,
-0x8,
-0x4e,
-0x54,
-0x46,
-0x59,
-0x60,
-0xa,
-0x3,
-0x75,
-0x60,
-0x10,
-0x44,
-0x2a,
-0x2e,
-0x5f,
-0x53,
-0x42,
-0x5f,
-0x50,
-0x43,
-0x49,
-0x30,
-0x5b,
-0x82,
-0x47,
-0x29,
-0x4d,
-0x48,
-0x50,
-0x44,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xd,
-0x50,
-0x4e,
-0x50,
-0x30,
-0x41,
-0x30,
-0x36,
-0x0,
-0x8,
-0x5f,
-0x55,
-0x49,
-0x44,
-0xd,
-0x4d,
-0x65,
-0x6d,
-0x6f,
-0x72,
-0x79,
-0x20,
-0x68,
-0x6f,
-0x74,
-0x70,
-0x6c,
-0x75,
-0x67,
-0x20,
-0x72,
-0x65,
-0x73,
-0x6f,
-0x75,
-0x72,
-0x63,
-0x65,
-0x73,
-0x0,
-0x14,
-0x13,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0xa0,
-0x9,
-0x93,
-0x4d,
-0x44,
-0x4e,
-0x52,
-0x0,
-0xa4,
-0x0,
-0xa4,
-0xa,
-0xb,
-0x5b,
-0x1,
-0x4d,
-0x4c,
-0x43,
-0x4b,
-0x0,
-0x14,
-0x4a,
-0x4,
-0x4d,
-0x53,
-0x43,
-0x4e,
-0x0,
-0xa0,
-0x9,
-0x93,
-0x4d,
-0x44,
-0x4e,
-0x52,
-0x0,
-0xa4,
-0x0,
-0x70,
-0x0,
-0x60,
-0x5b,
-0x23,
-0x4d,
-0x4c,
-0x43,
-0x4b,
-0xff,
-0xff,
-0xa2,
-0x25,
-0x95,
-0x60,
-0x4d,
-0x44,
-0x4e,
-0x52,
-0x70,
-0x60,
-0x4d,
-0x53,
-0x45,
-0x4c,
-0xa0,
-0x13,
-0x93,
-0x4d,
-0x49,
-0x4e,
-0x53,
-0x1,
-0x4d,
-0x54,
-0x46,
-0x59,
-0x60,
-0x1,
-0x70,
-0x1,
-0x4d,
-0x49,
-0x4e,
-0x53,
-0x72,
-0x60,
-0x1,
-0x60,
-0x5b,
-0x27,
-0x4d,
-0x4c,
-0x43,
-0x4b,
-0xa4,
-0x1,
-0x14,
-0x2d,
-0x4d,
-0x52,
-0x53,
-0x54,
-0x1,
-0x70,
-0x0,
-0x60,
-0x5b,
-0x23,
-0x4d,
-0x4c,
-0x43,
-0x4b,
-0xff,
-0xff,
-0x70,
-0x99,
-0x68,
-0x0,
-0x4d,
-0x53,
-0x45,
-0x4c,
-0xa0,
-0xb,
-0x93,
-0x4d,
-0x45,
-0x53,
-0x5f,
-0x1,
-0x70,
-0xa,
-0xf,
-0x60,
-0x5b,
-0x27,
-0x4d,
-0x4c,
-0x43,
-0x4b,
-0xa4,
-0x60,
-0x14,
-0x41,
-0x18,
-0x4d,
-0x43,
-0x52,
-0x53,
-0x9,
-0x5b,
-0x23,
-0x4d,
-0x4c,
-0x43,
-0x4b,
-0xff,
-0xff,
-0x70,
-0x99,
-0x68,
-0x0,
-0x4d,
-0x53,
-0x45,
-0x4c,
-0x8,
-0x4d,
-0x52,
-0x36,
-0x34,
-0x11,
-0x33,
-0xa,
-0x30,
-0x8a,
-0x2b,
-0x0,
-0x0,
-0xc,
-0x3,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0xfe,
-0xff,
-0xff,
-0xff,
-0xff,
-0xff,
-0xff,
-0xff,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0xff,
-0xff,
-0xff,
-0xff,
-0xff,
-0xff,
-0xff,
-0xff,
-0x79,
-0x0,
-0x8a,
-0x4d,
-0x52,
-0x36,
-0x34,
-0xa,
-0xe,
-0x4d,
-0x49,
-0x4e,
-0x4c,
-0x8a,
-0x4d,
-0x52,
-0x36,
-0x34,
-0xa,
-0x12,
-0x4d,
-0x49,
-0x4e,
-0x48,
-0x8a,
-0x4d,
-0x52,
-0x36,
-0x34,
-0xa,
-0x26,
-0x4c,
-0x45,
-0x4e,
-0x4c,
-0x8a,
-0x4d,
-0x52,
-0x36,
-0x34,
-0xa,
-0x2a,
-0x4c,
-0x45,
-0x4e,
-0x48,
-0x8a,
-0x4d,
-0x52,
-0x36,
-0x34,
-0xa,
-0x16,
-0x4d,
-0x41,
-0x58,
-0x4c,
-0x8a,
-0x4d,
-0x52,
-0x36,
-0x34,
-0xa,
-0x1a,
-0x4d,
-0x41,
-0x58,
-0x48,
-0x70,
-0x4d,
-0x52,
-0x42,
-0x48,
-0x4d,
-0x49,
-0x4e,
-0x48,
-0x70,
-0x4d,
-0x52,
-0x42,
-0x4c,
-0x4d,
-0x49,
-0x4e,
-0x4c,
-0x70,
-0x4d,
-0x52,
-0x4c,
-0x48,
-0x4c,
-0x45,
-0x4e,
-0x48,
-0x70,
-0x4d,
-0x52,
-0x4c,
-0x4c,
-0x4c,
-0x45,
-0x4e,
-0x4c,
-0x72,
-0x4d,
-0x49,
-0x4e,
-0x4c,
-0x4c,
-0x45,
-0x4e,
-0x4c,
-0x4d,
-0x41,
-0x58,
-0x4c,
-0x72,
-0x4d,
-0x49,
-0x4e,
-0x48,
-0x4c,
-0x45,
-0x4e,
-0x48,
-0x4d,
-0x41,
-0x58,
-0x48,
-0xa0,
-0x14,
-0x95,
-0x4d,
-0x41,
-0x58,
-0x4c,
-0x4d,
-0x49,
-0x4e,
-0x4c,
-0x72,
-0x4d,
-0x41,
-0x58,
-0x48,
-0x1,
-0x4d,
-0x41,
-0x58,
-0x48,
-0xa0,
-0x11,
-0x95,
-0x4d,
-0x41,
-0x58,
-0x4c,
-0x1,
-0x74,
-0x4d,
-0x41,
-0x58,
-0x48,
-0x1,
-0x4d,
-0x41,
-0x58,
-0x48,
-0x74,
-0x4d,
-0x41,
-0x58,
-0x4c,
-0x1,
-0x4d,
-0x41,
-0x58,
-0x4c,
-0xa0,
-0x44,
-0x7,
-0x93,
-0x4d,
-0x41,
-0x58,
-0x48,
-0x0,
-0x8,
-0x4d,
-0x52,
-0x33,
-0x32,
-0x11,
-0x1f,
-0xa,
-0x1c,
-0x87,
-0x17,
-0x0,
-0x0,
-0xc,
-0x3,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0x0,
-0xfe,
-0xff,
-0xff,
-0xff,
-0x0,
-0x0,
-0x0,
-0x0,
-0xff,
-0xff,
-0xff,
-0xff,
-0x79,
-0x0,
-0x8a,
-0x4d,
-0x52,
-0x33,
-0x32,
-0xa,
-0xa,
-0x4d,
-0x49,
-0x4e,
-0x5f,
-0x8a,
-0x4d,
-0x52,
-0x33,
-0x32,
-0xa,
-0xe,
-0x4d,
-0x41,
-0x58,
-0x5f,
-0x8a,
-0x4d,
-0x52,
-0x33,
-0x32,
-0xa,
-0x16,
-0x4c,
-0x45,
-0x4e,
-0x5f,
-0x70,
-0x4d,
-0x49,
-0x4e,
-0x4c,
-0x4d,
-0x49,
-0x4e,
-0x5f,
-0x70,
-0x4d,
-0x41,
-0x58,
-0x4c,
-0x4d,
-0x41,
-0x58,
-0x5f,
-0x70,
-0x4c,
-0x45,
-0x4e,
-0x4c,
-0x4c,
-0x45,
-0x4e,
-0x5f,
-0x5b,
-0x27,
-0x4d,
-0x4c,
-0x43,
-0x4b,
-0xa4,
-0x4d,
-0x52,
-0x33,
-0x32,
-0x5b,
-0x27,
-0x4d,
-0x4c,
-0x43,
-0x4b,
-0xa4,
-0x4d,
-0x52,
-0x36,
-0x34,
-0x14,
-0x24,
-0x4d,
-0x50,
-0x58,
-0x4d,
-0x1,
-0x5b,
-0x23,
-0x4d,
-0x4c,
-0x43,
-0x4b,
-0xff,
-0xff,
-0x70,
-0x99,
-0x68,
-0x0,
-0x4d,
-0x53,
-0x45,
-0x4c,
-0x70,
-0x4d,
-0x50,
-0x58,
-0x5f,
-0x60,
-0x5b,
-0x27,
-0x4d,
-0x4c,
-0x43,
-0x4b,
-0xa4,
-0x60,
-0x14,
-0x28,
-0x4d,
-0x4f,
-0x53,
-0x54,
-0x4,
-0x5b,
-0x23,
-0x4d,
-0x4c,
-0x43,
-0x4b,
-0xff,
-0xff,
-0x70,
-0x99,
-0x68,
-0x0,
-0x4d,
-0x53,
-0x45,
-0x4c,
-0x70,
-0x69,
-0x4d,
-0x4f,
-0x45,
-0x56,
-0x70,
-0x6a,
-0x4d,
-0x4f,
-0x53,
-0x43,
-0x5b,
-0x27,
-0x4d,
-0x4c,
-0x43,
-0x4b,
-0x10,
-0x42,
-0xa,
-0x5f,
-0x47,
-0x50,
-0x45,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xd,
-0x41,
-0x43,
-0x50,
-0x49,
-0x30,
-0x30,
-0x30,
-0x36,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x30,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x31,
-0x0,
-0x14,
-0x10,
-0x5f,
-0x45,
-0x30,
-0x32,
-0x0,
-0x5c,
-0x2e,
-0x5f,
-0x53,
-0x42,
-0x5f,
-0x50,
-0x52,
-0x53,
-0x43,
-0x14,
-0x19,
-0x5f,
-0x45,
-0x30,
-0x33,
-0x0,
-0x5c,
-0x2f,
-0x4,
-0x5f,
-0x53,
-0x42,
-0x5f,
-0x50,
-0x43,
-0x49,
-0x30,
-0x4d,
-0x48,
-0x50,
-0x44,
-0x4d,
-0x53,
-0x43,
-0x4e,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x34,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x35,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x36,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x37,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x38,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x39,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x41,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x42,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x43,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x44,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x45,
-0x0,
-0x14,
-0x6,
-0x5f,
-0x4c,
-0x30,
-0x46,
-0x0
-};
diff --git a/qemu/hw/i386/xen/xen_apic.c b/qemu/hw/i386/xen/xen_apic.c
index f5acd6a09..21d68ee04 100644
--- a/qemu/hw/i386/xen/xen_apic.c
+++ b/qemu/hw/i386/xen/xen_apic.c
@@ -9,6 +9,7 @@
* 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 "hw/i386/apic_internal.h"
#include "hw/pci/msi.h"
#include "hw/xen/xen.h"
@@ -43,11 +44,7 @@ static void xen_apic_realize(DeviceState *dev, Error **errp)
s->vapic_control = 0;
memory_region_init_io(&s->io_memory, OBJECT(s), &xen_apic_io_ops, s,
"xen-apic-msi", APIC_SPACE_SIZE);
-
-#if defined(CONFIG_XEN_CTRL_INTERFACE_VERSION) \
- && CONFIG_XEN_CTRL_INTERFACE_VERSION >= 420
- msi_supported = true;
-#endif
+ msi_nonbroken = true;
}
static void xen_apic_set_base(APICCommonState *s, uint64_t val)
diff --git a/qemu/hw/i386/xen/xen_platform.c b/qemu/hw/i386/xen/xen_platform.c
index 28b324a6f..aa7839324 100644
--- a/qemu/hw/i386/xen/xen_platform.c
+++ b/qemu/hw/i386/xen/xen_platform.c
@@ -23,8 +23,8 @@
* THE SOFTWARE.
*/
-#include <assert.h>
-
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/ide.h"
@@ -35,6 +35,7 @@
#include "trace.h"
#include "exec/address-spaces.h"
#include "sysemu/block-backend.h"
+#include "qemu/error-report.h"
#include <xenguest.h>
@@ -384,11 +385,17 @@ static const VMStateDescription vmstate_xen_platform = {
}
};
-static int xen_platform_initfn(PCIDevice *dev)
+static void xen_platform_realize(PCIDevice *dev, Error **errp)
{
PCIXenPlatformState *d = XEN_PLATFORM(dev);
uint8_t *pci_conf;
+ /* Device will crash on reset if xen is not initialized */
+ if (!xen_enabled()) {
+ error_setg(errp, "xen-platform device requires the Xen accelerator");
+ return;
+ }
+
pci_conf = dev->config;
pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
@@ -406,8 +413,6 @@ static int xen_platform_initfn(PCIDevice *dev)
&d->mmio_bar);
platform_fixed_ioport_init(d);
-
- return 0;
}
static void platform_reset(DeviceState *dev)
@@ -422,7 +427,7 @@ static void xen_platform_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
- k->init = xen_platform_initfn;
+ k->realize = xen_platform_realize;
k->vendor_id = PCI_VENDOR_ID_XEN;
k->device_id = PCI_DEVICE_ID_XEN_PLATFORM;
k->class_id = PCI_CLASS_OTHERS << 8 | 0x80;
diff --git a/qemu/hw/i386/xen/xen_pvdevice.c b/qemu/hw/i386/xen/xen_pvdevice.c
index c2189473b..c093b3445 100644
--- a/qemu/hw/i386/xen/xen_pvdevice.c
+++ b/qemu/hw/i386/xen/xen_pvdevice.c
@@ -29,6 +29,8 @@
* SUCH DAMAGE.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "trace.h"
@@ -69,14 +71,16 @@ static const MemoryRegionOps xen_pv_mmio_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
-static int xen_pv_init(PCIDevice *pci_dev)
+static void xen_pv_realize(PCIDevice *pci_dev, Error **errp)
{
XenPVDevice *d = XEN_PV_DEVICE(pci_dev);
uint8_t *pci_conf;
/* device-id property must always be supplied */
- if (d->device_id == 0xffff)
- return -1;
+ if (d->device_id == 0xffff) {
+ error_setg(errp, "Device ID invalid, it must always be supplied");
+ return;
+ }
pci_conf = pci_dev->config;
@@ -97,8 +101,6 @@ static int xen_pv_init(PCIDevice *pci_dev)
pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_MEM_PREFETCH,
&d->mmio);
-
- return 0;
}
static Property xen_pv_props[] = {
@@ -114,7 +116,7 @@ static void xen_pv_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
- k->init = xen_pv_init;
+ k->realize = xen_pv_realize;
k->class_id = PCI_CLASS_SYSTEM_OTHER;
dc->desc = "Xen PV Device";
dc->props = xen_pv_props;
diff --git a/qemu/hw/ide/ahci.c b/qemu/hw/ide/ahci.c
index 48749c1dc..f244bc01c 100644
--- a/qemu/hw/ide/ahci.c
+++ b/qemu/hw/ide/ahci.c
@@ -21,11 +21,11 @@
*
*/
+#include "qemu/osdep.h"
#include <hw/hw.h>
#include <hw/pci/msi.h>
#include <hw/i386/pc.h>
#include <hw/pci/pci.h>
-#include <hw/sysbus.h>
#include "qemu/error-report.h"
#include "sysemu/block-backend.h"
@@ -47,10 +47,9 @@ do { \
static void check_cmd(AHCIState *s, int port);
static int handle_cmd(AHCIState *s, int port, uint8_t slot);
static void ahci_reset_port(AHCIState *s, int port);
-static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis);
+static bool ahci_write_fis_d2h(AHCIDevice *ad);
static void ahci_init_d2h(AHCIDevice *ad);
static int ahci_dma_prepare_buf(IDEDMA *dma, int32_t limit);
-static void ahci_commit_buf(IDEDMA *dma, uint32_t tx_bytes);
static bool ahci_map_clb_address(AHCIDevice *ad);
static bool ahci_map_fis_address(AHCIDevice *ad);
static void ahci_unmap_clb_address(AHCIDevice *ad);
@@ -122,9 +121,9 @@ static uint32_t ahci_port_read(AHCIState *s, int port, int offset)
static void ahci_irq_raise(AHCIState *s, AHCIDevice *dev)
{
- AHCIPCIState *d = container_of(s, AHCIPCIState, ahci);
- PCIDevice *pci_dev =
- (PCIDevice *)object_dynamic_cast(OBJECT(d), TYPE_PCI_DEVICE);
+ DeviceState *dev_state = s->container;
+ PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state),
+ TYPE_PCI_DEVICE);
DPRINTF(0, "raise irq\n");
@@ -137,9 +136,9 @@ static void ahci_irq_raise(AHCIState *s, AHCIDevice *dev)
static void ahci_irq_lower(AHCIState *s, AHCIDevice *dev)
{
- AHCIPCIState *d = container_of(s, AHCIPCIState, ahci);
- PCIDevice *pci_dev =
- (PCIDevice *)object_dynamic_cast(OBJECT(d), TYPE_PCI_DEVICE);
+ DeviceState *dev_state = s->container;
+ PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state),
+ TYPE_PCI_DEVICE);
DPRINTF(0, "lower irq\n");
@@ -200,52 +199,38 @@ static void map_page(AddressSpace *as, uint8_t **ptr, uint64_t addr,
* Check the cmd register to see if we should start or stop
* the DMA or FIS RX engines.
*
- * @ad: Device to engage.
- * @allow_stop: Allow device to transition from started to stopped?
- * 'no' is useful for migration post_load, which does not expect a transition.
+ * @ad: Device to dis/engage.
*
* @return 0 on success, -1 on error.
*/
-static int ahci_cond_start_engines(AHCIDevice *ad, bool allow_stop)
+static int ahci_cond_start_engines(AHCIDevice *ad)
{
AHCIPortRegs *pr = &ad->port_regs;
-
- if (pr->cmd & PORT_CMD_START) {
- if (ahci_map_clb_address(ad)) {
- pr->cmd |= PORT_CMD_LIST_ON;
- } else {
+ bool cmd_start = pr->cmd & PORT_CMD_START;
+ bool cmd_on = pr->cmd & PORT_CMD_LIST_ON;
+ bool fis_start = pr->cmd & PORT_CMD_FIS_RX;
+ bool fis_on = pr->cmd & PORT_CMD_FIS_ON;
+
+ if (cmd_start && !cmd_on) {
+ if (!ahci_map_clb_address(ad)) {
+ pr->cmd &= ~PORT_CMD_START;
error_report("AHCI: Failed to start DMA engine: "
"bad command list buffer address");
return -1;
}
- } else if (pr->cmd & PORT_CMD_LIST_ON) {
- if (allow_stop) {
- ahci_unmap_clb_address(ad);
- pr->cmd = pr->cmd & ~(PORT_CMD_LIST_ON);
- } else {
- error_report("AHCI: DMA engine should be off, "
- "but appears to still be running");
- return -1;
- }
+ } else if (!cmd_start && cmd_on) {
+ ahci_unmap_clb_address(ad);
}
- if (pr->cmd & PORT_CMD_FIS_RX) {
- if (ahci_map_fis_address(ad)) {
- pr->cmd |= PORT_CMD_FIS_ON;
- } else {
+ if (fis_start && !fis_on) {
+ if (!ahci_map_fis_address(ad)) {
+ pr->cmd &= ~PORT_CMD_FIS_RX;
error_report("AHCI: Failed to start FIS receive engine: "
"bad FIS receive buffer address");
return -1;
}
- } else if (pr->cmd & PORT_CMD_FIS_ON) {
- if (allow_stop) {
- ahci_unmap_fis_address(ad);
- pr->cmd = pr->cmd & ~(PORT_CMD_FIS_ON);
- } else {
- error_report("AHCI: FIS receive engine should be off, "
- "but appears to still be running");
- return -1;
- }
+ } else if (!fis_start && fis_on) {
+ ahci_unmap_fis_address(ad);
}
return 0;
@@ -287,8 +272,8 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val)
pr->cmd = (pr->cmd & PORT_CMD_RO_MASK) |
(val & ~(PORT_CMD_RO_MASK|PORT_CMD_ICC_MASK));
- /* Check FIS RX and CLB engines, allow transition to false: */
- ahci_cond_start_engines(&s->dev[port], true);
+ /* Check FIS RX and CLB engines */
+ ahci_cond_start_engines(&s->dev[port]);
/* XXX usually the FIS would be pending on the bus here and
issuing deferred until the OS enables FIS receival.
@@ -297,7 +282,6 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val)
if ((pr->cmd & PORT_CMD_FIS_ON) &&
!s->dev[port].init_d2h_sent) {
ahci_init_d2h(&s->dev[port]);
- s->dev[port].init_d2h_sent = true;
}
check_cmd(s, port);
@@ -381,17 +365,23 @@ static uint64_t ahci_mem_read(void *opaque, hwaddr addr, unsigned size)
int ofst = addr - aligned;
uint64_t lo = ahci_mem_read_32(opaque, aligned);
uint64_t hi;
+ uint64_t val;
/* if < 8 byte read does not cross 4 byte boundary */
if (ofst + size <= 4) {
- return lo >> (ofst * 8);
+ val = lo >> (ofst * 8);
+ } else {
+ g_assert_cmpint(size, >, 1);
+
+ /* If the 64bit read is unaligned, we will produce undefined
+ * results. AHCI does not support unaligned 64bit reads. */
+ hi = ahci_mem_read_32(opaque, aligned + 4);
+ val = (hi << 32 | lo) >> (ofst * 8);
}
- g_assert_cmpint(size, >, 1);
- /* If the 64bit read is unaligned, we will produce undefined
- * results. AHCI does not support unaligned 64bit reads. */
- hi = ahci_mem_read_32(opaque, aligned + 4);
- return (hi << 32 | lo) >> (ofst * 8);
+ DPRINTF(-1, "addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 ", size=%d\n",
+ addr, val, size);
+ return val;
}
@@ -400,6 +390,9 @@ static void ahci_mem_write(void *opaque, hwaddr addr,
{
AHCIState *s = opaque;
+ DPRINTF(-1, "addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 ", size=%d\n",
+ addr, val, size);
+
/* Only aligned reads are allowed on AHCI */
if (addr & 3) {
fprintf(stderr, "ahci: Mis-aligned write to addr 0x"
@@ -540,20 +533,33 @@ static void ahci_check_cmd_bh(void *opaque)
static void ahci_init_d2h(AHCIDevice *ad)
{
- uint8_t init_fis[20];
IDEState *ide_state = &ad->port.ifs[0];
+ AHCIPortRegs *pr = &ad->port_regs;
- memset(init_fis, 0, sizeof(init_fis));
-
- init_fis[4] = 1;
- init_fis[12] = 1;
+ if (ad->init_d2h_sent) {
+ return;
+ }
- if (ide_state->drive_kind == IDE_CD) {
- init_fis[5] = ide_state->lcyl;
- init_fis[6] = ide_state->hcyl;
+ if (ahci_write_fis_d2h(ad)) {
+ ad->init_d2h_sent = true;
+ /* We're emulating receiving the first Reg H2D Fis from the device;
+ * Update the SIG register, but otherwise proceed as normal. */
+ pr->sig = ((uint32_t)ide_state->hcyl << 24) |
+ (ide_state->lcyl << 16) |
+ (ide_state->sector << 8) |
+ (ide_state->nsector & 0xFF);
}
+}
- ahci_write_fis_d2h(ad, init_fis);
+static void ahci_set_signature(AHCIDevice *ad, uint32_t sig)
+{
+ IDEState *s = &ad->port.ifs[0];
+ s->hcyl = sig >> 24 & 0xFF;
+ s->lcyl = sig >> 16 & 0xFF;
+ s->sector = sig >> 8 & 0xFF;
+ s->nsector = sig & 0xFF;
+
+ DPRINTF(ad->port_no, "set hcyl:lcyl:sect:nsect = 0x%08x\n", sig);
}
static void ahci_reset_port(AHCIState *s, int port)
@@ -604,17 +610,11 @@ static void ahci_reset_port(AHCIState *s, int port)
}
s->dev[port].port_state = STATE_RUN;
- if (!ide_state->blk) {
- pr->sig = 0;
- ide_state->status = SEEK_STAT | WRERR_STAT;
- } else if (ide_state->drive_kind == IDE_CD) {
- pr->sig = SATA_SIGNATURE_CDROM;
- ide_state->lcyl = 0x14;
- ide_state->hcyl = 0xeb;
- DPRINTF(port, "set lcyl = %d\n", ide_state->lcyl);
+ if (ide_state->drive_kind == IDE_CD) {
+ ahci_set_signature(d, SATA_SIGNATURE_CDROM);\
ide_state->status = SEEK_STAT | WRERR_STAT | READY_STAT;
} else {
- pr->sig = SATA_SIGNATURE_DISK;
+ ahci_set_signature(d, SATA_SIGNATURE_DISK);
ide_state->status = SEEK_STAT | WRERR_STAT;
}
@@ -643,11 +643,22 @@ static bool ahci_map_fis_address(AHCIDevice *ad)
AHCIPortRegs *pr = &ad->port_regs;
map_page(ad->hba->as, &ad->res_fis,
((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256);
- return ad->res_fis != NULL;
+ if (ad->res_fis != NULL) {
+ pr->cmd |= PORT_CMD_FIS_ON;
+ return true;
+ }
+
+ pr->cmd &= ~PORT_CMD_FIS_ON;
+ return false;
}
static void ahci_unmap_fis_address(AHCIDevice *ad)
{
+ if (ad->res_fis == NULL) {
+ DPRINTF(ad->port_no, "Attempt to unmap NULL FIS address\n");
+ return;
+ }
+ ad->port_regs.cmd &= ~PORT_CMD_FIS_ON;
dma_memory_unmap(ad->hba->as, ad->res_fis, 256,
DMA_DIRECTION_FROM_DEVICE, 256);
ad->res_fis = NULL;
@@ -659,11 +670,22 @@ static bool ahci_map_clb_address(AHCIDevice *ad)
ad->cur_cmd = NULL;
map_page(ad->hba->as, &ad->lst,
((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024);
- return ad->lst != NULL;
+ if (ad->lst != NULL) {
+ pr->cmd |= PORT_CMD_LIST_ON;
+ return true;
+ }
+
+ pr->cmd &= ~PORT_CMD_LIST_ON;
+ return false;
}
static void ahci_unmap_clb_address(AHCIDevice *ad)
{
+ if (ad->lst == NULL) {
+ DPRINTF(ad->port_no, "Attempt to unmap NULL CLB address\n");
+ return;
+ }
+ ad->port_regs.cmd &= ~PORT_CMD_LIST_ON;
dma_memory_unmap(ad->hba->as, ad->lst, 1024,
DMA_DIRECTION_FROM_DEVICE, 1024);
ad->lst = NULL;
@@ -750,7 +772,7 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len)
ahci_trigger_irq(ad->hba, ad, PORT_IRQ_PIOS_FIS);
}
-static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
+static bool ahci_write_fis_d2h(AHCIDevice *ad)
{
AHCIPortRegs *pr = &ad->port_regs;
uint8_t *d2h_fis;
@@ -758,7 +780,7 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
IDEState *s = &ad->port.ifs[0];
if (!ad->res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) {
- return;
+ return false;
}
d2h_fis = &ad->res_fis[RES_FIS_RFIS];
@@ -791,6 +813,7 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
}
ahci_trigger_irq(ad->hba, ad, PORT_IRQ_D2H_REG_FIS);
+ return true;
}
static int prdt_tbl_entry_size(const AHCI_SG *tbl)
@@ -799,8 +822,21 @@ static int prdt_tbl_entry_size(const AHCI_SG *tbl)
return (le32_to_cpu(tbl->flags_size) & AHCI_PRDT_SIZE_MASK) + 1;
}
+/**
+ * Fetch entries in a guest-provided PRDT and convert it into a QEMU SGlist.
+ * @ad: The AHCIDevice for whom we are building the SGList.
+ * @sglist: The SGList target to add PRD entries to.
+ * @cmd: The AHCI Command Header that describes where the PRDT is.
+ * @limit: The remaining size of the S/ATA transaction, in bytes.
+ * @offset: The number of bytes already transferred, in bytes.
+ *
+ * The AHCI PRDT can describe up to 256GiB. S/ATA only support transactions of
+ * up to 32MiB as of ATA8-ACS3 rev 1b, assuming a 512 byte sector size. We stop
+ * building the sglist from the PRDT as soon as we hit @limit bytes,
+ * which is <= INT32_MAX/2GiB.
+ */
static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist,
- AHCICmdHdr *cmd, int64_t limit, int32_t offset)
+ AHCICmdHdr *cmd, int64_t limit, uint64_t offset)
{
uint16_t opts = le16_to_cpu(cmd->opts);
uint16_t prdtl = le16_to_cpu(cmd->prdtl);
@@ -818,14 +854,6 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist,
IDEBus *bus = &ad->port;
BusState *qbus = BUS(bus);
- /*
- * Note: AHCI PRDT can describe up to 256GiB. SATA/ATA only support
- * transactions of up to 32MiB as of ATA8-ACS3 rev 1b, assuming a
- * 512 byte sector size. We limit the PRDT in this implementation to
- * a reasonably large 2GiB, which can accommodate the maximum transfer
- * request for sector sizes up to 32K.
- */
-
if (!prdtl) {
DPRINTF(ad->port_no, "no sg list given by guest: 0x%08x\n", opts);
return -1;
@@ -875,13 +903,6 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist,
qemu_sglist_add(sglist, le64_to_cpu(tbl[i].addr),
MIN(prdt_tbl_entry_size(&tbl[i]),
limit - sglist->size));
- if (sglist->size > INT32_MAX) {
- error_report("AHCI Physical Region Descriptor Table describes "
- "more than 2 GiB.\n");
- qemu_sglist_destroy(sglist);
- r = -1;
- goto out;
- }
}
}
@@ -898,6 +919,7 @@ static void ncq_err(NCQTransferState *ncq_tfs)
ide_state->error = ABRT_ERR;
ide_state->status = READY_STAT | ERR_STAT;
ncq_tfs->drive->port_regs.scr_err |= (1 << ncq_tfs->tag);
+ ncq_tfs->used = 0;
}
static void ncq_finish(NCQTransferState *ncq_tfs)
@@ -1290,7 +1312,7 @@ out:
s->data_ptr = s->data_end;
/* Update number of transferred bytes, destroy sglist */
- ahci_commit_buf(dma, size);
+ dma_buf_commit(s, size);
s->end_transfer_func(s);
@@ -1332,9 +1354,8 @@ static void ahci_restart(IDEDMA *dma)
}
/**
- * Called in DMA R/W chains to read the PRDT, utilizing ahci_populate_sglist.
- * Not currently invoked by PIO R/W chains,
- * which invoke ahci_populate_sglist via ahci_start_transfer.
+ * Called in DMA and PIO R/W chains to read the PRDT.
+ * Not shared with NCQ pathways.
*/
static int32_t ahci_dma_prepare_buf(IDEDMA *dma, int32_t limit)
{
@@ -1353,21 +1374,16 @@ static int32_t ahci_dma_prepare_buf(IDEDMA *dma, int32_t limit)
}
/**
- * Destroys the scatter-gather list,
- * and updates the command header with a bytes-read value.
- * called explicitly via ahci_dma_rw_buf (ATAPI DMA),
- * and ahci_start_transfer (PIO R/W),
- * and called via callback from ide_dma_cb for DMA R/W paths.
+ * Updates the command header with a bytes-read value.
+ * Called via dma_buf_commit, for both DMA and PIO paths.
+ * sglist destruction is handled within dma_buf_commit.
*/
static void ahci_commit_buf(IDEDMA *dma, uint32_t tx_bytes)
{
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
- IDEState *s = &ad->port.ifs[0];
tx_bytes += le32_to_cpu(ad->cur_cmd->status);
ad->cur_cmd->status = cpu_to_le32(tx_bytes);
-
- qemu_sglist_destroy(&s->sg);
}
static int ahci_dma_rw_buf(IDEDMA *dma, int is_write)
@@ -1388,10 +1404,9 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write)
}
/* free sglist, update byte count */
- ahci_commit_buf(dma, l);
+ dma_buf_commit(s, l);
s->io_buffer_index += l;
- s->io_buffer_offset += l;
DPRINTF(ad->port_no, "len=%#x\n", l);
@@ -1405,7 +1420,7 @@ static void ahci_cmd_done(IDEDMA *dma)
DPRINTF(ad->port_no, "cmd done\n");
/* update d2h status */
- ahci_write_fis_d2h(ad, NULL);
+ ahci_write_fis_d2h(ad);
if (!ad->check_bh) {
/* maybe we still have something to process, check later */
@@ -1429,7 +1444,17 @@ static const IDEDMAOps ahci_dma_ops = {
.cmd_done = ahci_cmd_done,
};
-void ahci_init(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports)
+void ahci_init(AHCIState *s, DeviceState *qdev)
+{
+ s->container = qdev;
+ /* XXX BAR size should be 1k, but that breaks, so bump it to 4k for now */
+ memory_region_init_io(&s->mem, OBJECT(qdev), &ahci_mem_ops, s,
+ "ahci", AHCI_MEM_BAR_SIZE);
+ memory_region_init_io(&s->idp, OBJECT(qdev), &ahci_idp_ops, s,
+ "ahci-idp", 32);
+}
+
+void ahci_realize(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports)
{
qemu_irq *irqs;
int i;
@@ -1438,14 +1463,7 @@ void ahci_init(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports)
s->ports = ports;
s->dev = g_new0(AHCIDevice, ports);
ahci_reg_init(s);
- /* XXX BAR size should be 1k, but that breaks, so bump it to 4k for now */
- memory_region_init_io(&s->mem, OBJECT(qdev), &ahci_mem_ops, s,
- "ahci", AHCI_MEM_BAR_SIZE);
- memory_region_init_io(&s->idp, OBJECT(qdev), &ahci_idp_ops, s,
- "ahci-idp", 32);
-
irqs = qemu_allocate_irqs(ahci_irq_set, s, s->ports);
-
for (i = 0; i < s->ports; i++) {
AHCIDevice *ad = &s->dev[i];
@@ -1542,14 +1560,28 @@ static int ahci_state_post_load(void *opaque, int version_id)
int i, j;
struct AHCIDevice *ad;
NCQTransferState *ncq_tfs;
+ AHCIPortRegs *pr;
AHCIState *s = opaque;
for (i = 0; i < s->ports; i++) {
ad = &s->dev[i];
+ pr = &ad->port_regs;
+
+ if (!(pr->cmd & PORT_CMD_START) && (pr->cmd & PORT_CMD_LIST_ON)) {
+ error_report("AHCI: DMA engine should be off, but status bit "
+ "indicates it is still running.");
+ return -1;
+ }
+ if (!(pr->cmd & PORT_CMD_FIS_RX) && (pr->cmd & PORT_CMD_FIS_ON)) {
+ error_report("AHCI: FIS RX engine should be off, but status bit "
+ "indicates it is still running.");
+ return -1;
+ }
- /* Only remap the CLB address if appropriate, disallowing a state
- * transition from 'on' to 'off' it should be consistent here. */
- if (ahci_cond_start_engines(ad, false) != 0) {
+ /* After a migrate, the DMA/FIS engines are "off" and
+ * need to be conditionally restarted */
+ pr->cmd &= ~(PORT_CMD_LIST_ON | PORT_CMD_FIS_ON);
+ if (ahci_cond_start_engines(ad) != 0) {
return -1;
}
@@ -1625,18 +1657,6 @@ const VMStateDescription vmstate_ahci = {
},
};
-#define TYPE_SYSBUS_AHCI "sysbus-ahci"
-#define SYSBUS_AHCI(obj) OBJECT_CHECK(SysbusAHCIState, (obj), TYPE_SYSBUS_AHCI)
-
-typedef struct SysbusAHCIState {
- /*< private >*/
- SysBusDevice parent_obj;
- /*< public >*/
-
- AHCIState ahci;
- uint32_t num_ports;
-} SysbusAHCIState;
-
static const VMStateDescription vmstate_sysbus_ahci = {
.name = "sysbus-ahci",
.fields = (VMStateField[]) {
@@ -1652,17 +1672,24 @@ static void sysbus_ahci_reset(DeviceState *dev)
ahci_reset(&s->ahci);
}
-static void sysbus_ahci_realize(DeviceState *dev, Error **errp)
+static void sysbus_ahci_init(Object *obj)
{
- SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
- SysbusAHCIState *s = SYSBUS_AHCI(dev);
+ SysbusAHCIState *s = SYSBUS_AHCI(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
- ahci_init(&s->ahci, dev, &address_space_memory, s->num_ports);
+ ahci_init(&s->ahci, DEVICE(obj));
sysbus_init_mmio(sbd, &s->ahci.mem);
sysbus_init_irq(sbd, &s->ahci.irq);
}
+static void sysbus_ahci_realize(DeviceState *dev, Error **errp)
+{
+ SysbusAHCIState *s = SYSBUS_AHCI(dev);
+
+ ahci_realize(&s->ahci, dev, &address_space_memory, s->num_ports);
+}
+
static Property sysbus_ahci_properties[] = {
DEFINE_PROP_UINT32("num-ports", SysbusAHCIState, num_ports, 1),
DEFINE_PROP_END_OF_LIST(),
@@ -1683,12 +1710,108 @@ static const TypeInfo sysbus_ahci_info = {
.name = TYPE_SYSBUS_AHCI,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(SysbusAHCIState),
+ .instance_init = sysbus_ahci_init,
.class_init = sysbus_ahci_class_init,
};
+#define ALLWINNER_AHCI_BISTAFR ((0xa0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_BISTCR ((0xa4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_BISTFCTR ((0xa8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_BISTSR ((0xac - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_BISTDECR ((0xb0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_DIAGNR0 ((0xb4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_DIAGNR1 ((0xb8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_OOBR ((0xbc - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_PHYCS0R ((0xc0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_PHYCS1R ((0xc4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_PHYCS2R ((0xc8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_TIMER1MS ((0xe0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_GPARAM1R ((0xe8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_GPARAM2R ((0xec - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_PPARAMR ((0xf0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_TESTR ((0xf4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_VERSIONR ((0xf8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_IDR ((0xfc - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_RWCR ((0xfc - ALLWINNER_AHCI_MMIO_OFF) / 4)
+
+static uint64_t allwinner_ahci_mem_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ AllwinnerAHCIState *a = opaque;
+ uint64_t val = a->regs[addr/4];
+
+ switch (addr / 4) {
+ case ALLWINNER_AHCI_PHYCS0R:
+ val |= 0x2 << 28;
+ break;
+ case ALLWINNER_AHCI_PHYCS2R:
+ val &= ~(0x1 << 24);
+ break;
+ }
+ DPRINTF(-1, "addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 ", size=%d\n",
+ addr, val, size);
+ return val;
+}
+
+static void allwinner_ahci_mem_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ AllwinnerAHCIState *a = opaque;
+
+ DPRINTF(-1, "addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 ", size=%d\n",
+ addr, val, size);
+ a->regs[addr/4] = val;
+}
+
+static const MemoryRegionOps allwinner_ahci_mem_ops = {
+ .read = allwinner_ahci_mem_read,
+ .write = allwinner_ahci_mem_write,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void allwinner_ahci_init(Object *obj)
+{
+ SysbusAHCIState *s = SYSBUS_AHCI(obj);
+ AllwinnerAHCIState *a = ALLWINNER_AHCI(obj);
+
+ memory_region_init_io(&a->mmio, OBJECT(obj), &allwinner_ahci_mem_ops, a,
+ "allwinner-ahci", ALLWINNER_AHCI_MMIO_SIZE);
+ memory_region_add_subregion(&s->ahci.mem, ALLWINNER_AHCI_MMIO_OFF,
+ &a->mmio);
+}
+
+static const VMStateDescription vmstate_allwinner_ahci = {
+ .name = "allwinner-ahci",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, AllwinnerAHCIState,
+ ALLWINNER_AHCI_MMIO_SIZE/4),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void allwinner_ahci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->vmsd = &vmstate_allwinner_ahci;
+}
+
+static const TypeInfo allwinner_ahci_info = {
+ .name = TYPE_ALLWINNER_AHCI,
+ .parent = TYPE_SYSBUS_AHCI,
+ .instance_size = sizeof(AllwinnerAHCIState),
+ .instance_init = allwinner_ahci_init,
+ .class_init = allwinner_ahci_class_init,
+};
+
static void sysbus_ahci_register_types(void)
{
type_register_static(&sysbus_ahci_info);
+ type_register_static(&allwinner_ahci_info);
}
type_init(sysbus_ahci_register_types)
diff --git a/qemu/hw/ide/ahci.h b/qemu/hw/ide/ahci.h
index 79a463d93..bc777ed5c 100644
--- a/qemu/hw/ide/ahci.h
+++ b/qemu/hw/ide/ahci.h
@@ -24,6 +24,8 @@
#ifndef HW_IDE_AHCI_H
#define HW_IDE_AHCI_H
+#include <hw/sysbus.h>
+
#define AHCI_MEM_BAR_SIZE 0x1000
#define AHCI_MAX_PORTS 32
#define AHCI_MAX_SG 168 /* hardware max is 64K */
@@ -285,6 +287,8 @@ struct AHCIDevice {
};
typedef struct AHCIState {
+ DeviceState *container;
+
AHCIDevice *dev;
AHCIControlRegs control_regs;
MemoryRegion mem;
@@ -362,11 +366,40 @@ typedef struct SDBFIS {
uint32_t payload;
} QEMU_PACKED SDBFIS;
-void ahci_init(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports);
+void ahci_realize(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports);
+void ahci_init(AHCIState *s, DeviceState *qdev);
void ahci_uninit(AHCIState *s);
void ahci_reset(AHCIState *s);
void ahci_ide_create_devs(PCIDevice *dev, DriveInfo **hd);
+#define TYPE_SYSBUS_AHCI "sysbus-ahci"
+#define SYSBUS_AHCI(obj) OBJECT_CHECK(SysbusAHCIState, (obj), TYPE_SYSBUS_AHCI)
+
+typedef struct SysbusAHCIState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ /*< public >*/
+
+ AHCIState ahci;
+ uint32_t num_ports;
+} SysbusAHCIState;
+
+#define TYPE_ALLWINNER_AHCI "allwinner-ahci"
+#define ALLWINNER_AHCI(obj) OBJECT_CHECK(AllwinnerAHCIState, (obj), \
+ TYPE_ALLWINNER_AHCI)
+
+#define ALLWINNER_AHCI_MMIO_OFF 0x80
+#define ALLWINNER_AHCI_MMIO_SIZE 0x80
+
+struct AllwinnerAHCIState {
+ /*< private >*/
+ SysbusAHCIState parent_obj;
+ /*< public >*/
+
+ MemoryRegion mmio;
+ uint32_t regs[ALLWINNER_AHCI_MMIO_SIZE/4];
+};
+
#endif /* HW_IDE_AHCI_H */
diff --git a/qemu/hw/ide/atapi.c b/qemu/hw/ide/atapi.c
index 79dd16710..2bb606c1c 100644
--- a/qemu/hw/ide/atapi.c
+++ b/qemu/hw/ide/atapi.c
@@ -23,6 +23,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/ide/internal.h"
#include "hw/scsi/scsi.h"
#include "sysemu/block-backend.h"
@@ -105,33 +106,99 @@ static void cd_data_to_raw(uint8_t *buf, int lba)
memset(buf, 0, 288);
}
-static int cd_read_sector(IDEState *s, int lba, uint8_t *buf, int sector_size)
+static int
+cd_read_sector_sync(IDEState *s)
{
int ret;
+ block_acct_start(blk_get_stats(s->blk), &s->acct,
+ 4 * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
- switch(sector_size) {
+#ifdef DEBUG_IDE_ATAPI
+ printf("cd_read_sector_sync: lba=%d\n", s->lba);
+#endif
+
+ switch (s->cd_sector_size) {
case 2048:
- block_acct_start(blk_get_stats(s->blk), &s->acct,
- 4 * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
- ret = blk_read(s->blk, (int64_t)lba << 2, buf, 4);
- block_acct_done(blk_get_stats(s->blk), &s->acct);
+ ret = blk_read(s->blk, (int64_t)s->lba << 2,
+ s->io_buffer, 4);
break;
case 2352:
- block_acct_start(blk_get_stats(s->blk), &s->acct,
- 4 * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
- ret = blk_read(s->blk, (int64_t)lba << 2, buf + 16, 4);
- block_acct_done(blk_get_stats(s->blk), &s->acct);
- if (ret < 0)
- return ret;
- cd_data_to_raw(buf, lba);
+ ret = blk_read(s->blk, (int64_t)s->lba << 2,
+ s->io_buffer + 16, 4);
+ if (ret >= 0) {
+ cd_data_to_raw(s->io_buffer, s->lba);
+ }
break;
default:
- ret = -EIO;
- break;
+ block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_READ);
+ return -EIO;
+ }
+
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->blk), &s->acct);
+ } else {
+ block_acct_done(blk_get_stats(s->blk), &s->acct);
+ s->lba++;
+ s->io_buffer_index = 0;
}
+
return ret;
}
+static void cd_read_sector_cb(void *opaque, int ret)
+{
+ IDEState *s = opaque;
+
+#ifdef DEBUG_IDE_ATAPI
+ printf("cd_read_sector_cb: lba=%d ret=%d\n", s->lba, ret);
+#endif
+
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->blk), &s->acct);
+ ide_atapi_io_error(s, ret);
+ return;
+ }
+
+ block_acct_done(blk_get_stats(s->blk), &s->acct);
+
+ if (s->cd_sector_size == 2352) {
+ cd_data_to_raw(s->io_buffer, s->lba);
+ }
+
+ s->lba++;
+ s->io_buffer_index = 0;
+ s->status &= ~BUSY_STAT;
+
+ ide_atapi_cmd_reply_end(s);
+}
+
+static int cd_read_sector(IDEState *s)
+{
+ if (s->cd_sector_size != 2048 && s->cd_sector_size != 2352) {
+ block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_READ);
+ return -EINVAL;
+ }
+
+ s->iov.iov_base = (s->cd_sector_size == 2352) ?
+ s->io_buffer + 16 : s->io_buffer;
+
+ s->iov.iov_len = 4 * BDRV_SECTOR_SIZE;
+ qemu_iovec_init_external(&s->qiov, &s->iov, 1);
+
+#ifdef DEBUG_IDE_ATAPI
+ printf("cd_read_sector: lba=%d\n", s->lba);
+#endif
+
+ block_acct_start(blk_get_stats(s->blk), &s->acct,
+ 4 * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
+
+ ide_buffered_readv(s, (int64_t)s->lba << 2, &s->qiov, 4,
+ cd_read_sector_cb, s);
+
+ s->status |= BUSY_STAT;
+ return 0;
+}
+
void ide_atapi_cmd_ok(IDEState *s)
{
s->error = 0;
@@ -167,6 +234,17 @@ void ide_atapi_io_error(IDEState *s, int ret)
}
}
+static uint16_t atapi_byte_count_limit(IDEState *s)
+{
+ uint16_t bcl;
+
+ bcl = s->lcyl | (s->hcyl << 8);
+ if (bcl == 0xffff) {
+ return 0xfffe;
+ }
+ return bcl;
+}
+
/* The whole ATAPI transfer logic is handled in this function */
void ide_atapi_cmd_reply_end(IDEState *s)
{
@@ -182,18 +260,27 @@ void ide_atapi_cmd_reply_end(IDEState *s)
ide_atapi_cmd_ok(s);
ide_set_irq(s->bus);
#ifdef DEBUG_IDE_ATAPI
- printf("status=0x%x\n", s->status);
+ printf("end of transfer, status=0x%x\n", s->status);
#endif
} else {
/* see if a new sector must be read */
if (s->lba != -1 && s->io_buffer_index >= s->cd_sector_size) {
- ret = cd_read_sector(s, s->lba, s->io_buffer, s->cd_sector_size);
- if (ret < 0) {
- ide_atapi_io_error(s, ret);
+ if (!s->elementary_transfer_size) {
+ ret = cd_read_sector(s);
+ if (ret < 0) {
+ ide_atapi_io_error(s, ret);
+ }
return;
+ } else {
+ /* rebuffering within an elementary transfer is
+ * only possible with a sync request because we
+ * end up with a race condition otherwise */
+ ret = cd_read_sector_sync(s);
+ if (ret < 0) {
+ ide_atapi_io_error(s, ret);
+ return;
+ }
}
- s->lba++;
- s->io_buffer_index = 0;
}
if (s->elementary_transfer_size > 0) {
/* there are some data left to transmit in this elementary
@@ -209,12 +296,10 @@ void ide_atapi_cmd_reply_end(IDEState *s)
} else {
/* a new transfer is needed */
s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO;
- byte_count_limit = s->lcyl | (s->hcyl << 8);
+ byte_count_limit = atapi_byte_count_limit(s);
#ifdef DEBUG_IDE_ATAPI
printf("byte_count_limit=%d\n", byte_count_limit);
#endif
- if (byte_count_limit == 0xffff)
- byte_count_limit--;
size = s->packet_transfer_size;
if (size > byte_count_limit) {
/* byte count limit must be even if this case */
@@ -275,7 +360,6 @@ static void ide_atapi_cmd_read_pio(IDEState *s, int lba, int nb_sectors,
s->io_buffer_index = sector_size;
s->cd_sector_size = sector_size;
- s->status = READY_STAT | SEEK_STAT;
ide_atapi_cmd_reply_end(s);
}
@@ -291,15 +375,18 @@ static void ide_atapi_cmd_check_status(IDEState *s)
}
/* ATAPI DMA support */
-/* XXX: handle read errors */
static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret)
{
IDEState *s = opaque;
int data_offset, n;
if (ret < 0) {
- ide_atapi_io_error(s, ret);
- goto eot;
+ if (ide_handle_rw_error(s, -ret, ide_dma_cmd_to_retry(s->dma_cmd))) {
+ if (s->bus->error_status) {
+ return;
+ }
+ goto eot;
+ }
}
if (s->io_buffer_size > 0) {
@@ -351,13 +438,17 @@ static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret)
s->bus->dma->iov.iov_len = n * 4 * 512;
qemu_iovec_init_external(&s->bus->dma->qiov, &s->bus->dma->iov, 1);
- s->bus->dma->aiocb = blk_aio_readv(s->blk, (int64_t)s->lba << 2,
- &s->bus->dma->qiov, n * 4,
- ide_atapi_cmd_read_dma_cb, s);
+ s->bus->dma->aiocb = ide_buffered_readv(s, (int64_t)s->lba << 2,
+ &s->bus->dma->qiov, n * 4,
+ ide_atapi_cmd_read_dma_cb, s);
return;
eot:
- block_acct_done(blk_get_stats(s->blk), &s->acct);
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->blk), &s->acct);
+ } else {
+ block_acct_done(blk_get_stats(s->blk), &s->acct);
+ }
ide_set_inactive(s, false);
}
@@ -393,21 +484,16 @@ static void ide_atapi_cmd_read(IDEState *s, int lba, int nb_sectors,
}
}
-
-/* Called by *_restart_bh when the transfer function points
- * to ide_atapi_cmd
- */
void ide_atapi_dma_restart(IDEState *s)
{
/*
- * I'm not sure we have enough stored to restart the command
- * safely, so give the guest an error it should recover from.
- * I'm assuming most guests will try to recover from something
- * listed as a medium error on a CD; it seems to work on Linux.
- * This would be more of a problem if we did any other type of
- * DMA operation.
+ * At this point we can just re-evaluate the packet command and start over.
+ * The presence of ->dma_cb callback in the pre_save ensures that the packet
+ * command has been completely sent and we can safely restart command.
*/
- ide_atapi_cmd_error(s, MEDIUM_ERROR, ASC_NO_SEEK_COMPLETE);
+ s->unit = s->bus->retry_unit;
+ s->bus->dma->ops->restart_dma(s->bus->dma);
+ ide_atapi_cmd(s);
}
static inline uint8_t ide_atapi_set_profile(uint8_t *buf, uint8_t *index,
@@ -737,7 +823,6 @@ static void cmd_inquiry(IDEState *s, uint8_t *buf)
out:
buf[size_idx] = idx - preamble_len;
ide_atapi_cmd_reply(s, idx, max_len);
- return;
}
static void cmd_get_configuration(IDEState *s, uint8_t *buf)
@@ -1169,20 +1254,28 @@ enum {
* 4.1.8)
*/
CHECK_READY = 0x02,
+
+ /*
+ * Commands flagged with NONDATA do not in any circumstances return
+ * any data via ide_atapi_cmd_reply. These commands are exempt from
+ * the normal byte_count_limit constraints.
+ * See ATA8-ACS3 "7.21.5 Byte Count Limit"
+ */
+ NONDATA = 0x04,
};
-static const struct {
+static const struct AtapiCmd {
void (*handler)(IDEState *s, uint8_t *buf);
int flags;
} atapi_cmd_table[0x100] = {
- [ 0x00 ] = { cmd_test_unit_ready, CHECK_READY },
+ [ 0x00 ] = { cmd_test_unit_ready, CHECK_READY | NONDATA },
[ 0x03 ] = { cmd_request_sense, ALLOW_UA },
[ 0x12 ] = { cmd_inquiry, ALLOW_UA },
- [ 0x1b ] = { cmd_start_stop_unit, 0 }, /* [1] */
- [ 0x1e ] = { cmd_prevent_allow_medium_removal, 0 },
+ [ 0x1b ] = { cmd_start_stop_unit, NONDATA }, /* [1] */
+ [ 0x1e ] = { cmd_prevent_allow_medium_removal, NONDATA },
[ 0x25 ] = { cmd_read_cdvd_capacity, CHECK_READY },
[ 0x28 ] = { cmd_read, /* (10) */ CHECK_READY },
- [ 0x2b ] = { cmd_seek, CHECK_READY },
+ [ 0x2b ] = { cmd_seek, CHECK_READY | NONDATA },
[ 0x43 ] = { cmd_read_toc_pma_atip, CHECK_READY },
[ 0x46 ] = { cmd_get_configuration, ALLOW_UA },
[ 0x4a ] = { cmd_get_event_status_notification, ALLOW_UA },
@@ -1190,7 +1283,7 @@ static const struct {
[ 0x5a ] = { cmd_mode_sense, /* (10) */ 0 },
[ 0xa8 ] = { cmd_read, /* (12) */ CHECK_READY },
[ 0xad ] = { cmd_read_dvd_structure, CHECK_READY },
- [ 0xbb ] = { cmd_set_speed, 0 },
+ [ 0xbb ] = { cmd_set_speed, NONDATA },
[ 0xbd ] = { cmd_mechanism_status, 0 },
[ 0xbe ] = { cmd_read_cd, CHECK_READY },
/* [1] handler detects and reports not ready condition itself */
@@ -1198,9 +1291,9 @@ static const struct {
void ide_atapi_cmd(IDEState *s)
{
- uint8_t *buf;
+ uint8_t *buf = s->io_buffer;
+ const struct AtapiCmd *cmd = &atapi_cmd_table[s->io_buffer[0]];
- buf = s->io_buffer;
#ifdef DEBUG_IDE_ATAPI
{
int i;
@@ -1211,14 +1304,14 @@ void ide_atapi_cmd(IDEState *s)
printf("\n");
}
#endif
+
/*
* If there's a UNIT_ATTENTION condition pending, only command flagged with
* ALLOW_UA are allowed to complete. with other commands getting a CHECK
* condition response unless a higher priority status, defined by the drive
* here, is pending.
*/
- if (s->sense_key == UNIT_ATTENTION &&
- !(atapi_cmd_table[s->io_buffer[0]].flags & ALLOW_UA)) {
+ if (s->sense_key == UNIT_ATTENTION && !(cmd->flags & ALLOW_UA)) {
ide_atapi_cmd_check_status(s);
return;
}
@@ -1229,7 +1322,7 @@ void ide_atapi_cmd(IDEState *s)
* GET_EVENT_STATUS_NOTIFICATION to detect such tray open/close
* states rely on this behavior.
*/
- if (!(atapi_cmd_table[s->io_buffer[0]].flags & ALLOW_UA) &&
+ if (!(cmd->flags & ALLOW_UA) &&
!s->tray_open && blk_is_inserted(s->blk) && s->cdrom_changed) {
if (s->cdrom_changed == 1) {
@@ -1244,16 +1337,29 @@ void ide_atapi_cmd(IDEState *s)
}
/* Report a Not Ready condition if appropriate for the command */
- if ((atapi_cmd_table[s->io_buffer[0]].flags & CHECK_READY) &&
+ if ((cmd->flags & CHECK_READY) &&
(!media_present(s) || !blk_is_inserted(s->blk)))
{
ide_atapi_cmd_error(s, NOT_READY, ASC_MEDIUM_NOT_PRESENT);
return;
}
+ /* Nondata commands permit the byte_count_limit to be 0.
+ * If this is a data-transferring PIO command and BCL is 0,
+ * we abort at the /ATA/ level, not the ATAPI level.
+ * See ATA8 ACS3 section 7.17.6.49 and 7.21.5 */
+ if (cmd->handler && !(cmd->flags & NONDATA)) {
+ /* TODO: Check IDENTIFY data word 125 for default BCL (currently 0) */
+ if (!(atapi_byte_count_limit(s) || s->atapi_dma)) {
+ /* TODO: Move abort back into core.c and make static inline again */
+ ide_abort_command(s);
+ return;
+ }
+ }
+
/* Execute the command */
- if (atapi_cmd_table[s->io_buffer[0]].handler) {
- atapi_cmd_table[s->io_buffer[0]].handler(s, buf);
+ if (cmd->handler) {
+ cmd->handler(s, buf);
return;
}
diff --git a/qemu/hw/ide/cmd646.c b/qemu/hw/ide/cmd646.c
index 66fb9d96d..49294a531 100644
--- a/qemu/hw/ide/cmd646.c
+++ b/qemu/hw/ide/cmd646.c
@@ -22,6 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include <hw/hw.h>
#include <hw/i386/pc.h>
#include <hw/pci/pci.h>
@@ -417,6 +418,7 @@ static void cmd646_ide_class_init(ObjectClass *klass, void *data)
k->config_read = cmd646_pci_config_read;
k->config_write = cmd646_pci_config_write;
dc->props = cmd646_ide_properties;
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
}
static const TypeInfo cmd646_ide_info = {
diff --git a/qemu/hw/ide/core.c b/qemu/hw/ide/core.c
index 50449cae0..41e6a2dc4 100644
--- a/qemu/hw/ide/core.c
+++ b/qemu/hw/ide/core.c
@@ -22,6 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include <hw/hw.h>
#include <hw/i386/pc.h>
#include <hw/pci/pci.h>
@@ -32,6 +33,7 @@
#include "sysemu/dma.h"
#include "hw/block/block.h"
#include "sysemu/block-backend.h"
+#include "qemu/cutils.h"
#include <hw/ide/internal.h>
@@ -55,7 +57,6 @@ static const int smart_attributes[][12] = {
{ 190, 0x03, 0x00, 0x45, 0x45, 0x1f, 0x00, 0x1f, 0x1f, 0x00, 0x00, 0x32},
};
-static int ide_handle_rw_error(IDEState *s, int error, int op);
static void ide_dummy_transfer_stop(IDEState *s);
static void padstr(char *str, const char *src, int len)
@@ -457,7 +458,7 @@ BlockAIOCB *ide_issue_trim(BlockBackend *blk,
return &iocb->common;
}
-static inline void ide_abort_command(IDEState *s)
+void ide_abort_command(IDEState *s)
{
ide_transfer_stop(s);
s->status = READY_STAT | ERR_STAT;
@@ -486,13 +487,27 @@ static void ide_cmd_done(IDEState *s)
}
}
-void ide_transfer_stop(IDEState *s)
+static void ide_transfer_halt(IDEState *s,
+ void(*end_transfer_func)(IDEState *),
+ bool notify)
{
- s->end_transfer_func = ide_transfer_stop;
+ s->end_transfer_func = end_transfer_func;
s->data_ptr = s->io_buffer;
s->data_end = s->io_buffer;
s->status &= ~DRQ_STAT;
- ide_cmd_done(s);
+ if (notify) {
+ ide_cmd_done(s);
+ }
+}
+
+void ide_transfer_stop(IDEState *s)
+{
+ ide_transfer_halt(s, ide_transfer_stop, true);
+}
+
+static void ide_transfer_cancel(IDEState *s)
+{
+ ide_transfer_halt(s, ide_transfer_cancel, false);
}
int64_t ide_get_sector(IDEState *s)
@@ -561,6 +576,98 @@ static bool ide_sect_range_ok(IDEState *s,
return true;
}
+static void ide_buffered_readv_cb(void *opaque, int ret)
+{
+ IDEBufferedRequest *req = opaque;
+ if (!req->orphaned) {
+ if (!ret) {
+ qemu_iovec_from_buf(req->original_qiov, 0, req->iov.iov_base,
+ req->original_qiov->size);
+ }
+ req->original_cb(req->original_opaque, ret);
+ }
+ QLIST_REMOVE(req, list);
+ qemu_vfree(req->iov.iov_base);
+ g_free(req);
+}
+
+#define MAX_BUFFERED_REQS 16
+
+BlockAIOCB *ide_buffered_readv(IDEState *s, int64_t sector_num,
+ QEMUIOVector *iov, int nb_sectors,
+ BlockCompletionFunc *cb, void *opaque)
+{
+ BlockAIOCB *aioreq;
+ IDEBufferedRequest *req;
+ int c = 0;
+
+ QLIST_FOREACH(req, &s->buffered_requests, list) {
+ c++;
+ }
+ if (c > MAX_BUFFERED_REQS) {
+ return blk_abort_aio_request(s->blk, cb, opaque, -EIO);
+ }
+
+ req = g_new0(IDEBufferedRequest, 1);
+ req->original_qiov = iov;
+ req->original_cb = cb;
+ req->original_opaque = opaque;
+ req->iov.iov_base = qemu_blockalign(blk_bs(s->blk), iov->size);
+ req->iov.iov_len = iov->size;
+ qemu_iovec_init_external(&req->qiov, &req->iov, 1);
+
+ aioreq = blk_aio_readv(s->blk, sector_num, &req->qiov, nb_sectors,
+ ide_buffered_readv_cb, req);
+
+ QLIST_INSERT_HEAD(&s->buffered_requests, req, list);
+ return aioreq;
+}
+
+/**
+ * Cancel all pending DMA requests.
+ * Any buffered DMA requests are instantly canceled,
+ * but any pending unbuffered DMA requests must be waited on.
+ */
+void ide_cancel_dma_sync(IDEState *s)
+{
+ IDEBufferedRequest *req;
+
+ /* First invoke the callbacks of all buffered requests
+ * and flag those requests as orphaned. Ideally there
+ * are no unbuffered (Scatter Gather DMA Requests or
+ * write requests) pending and we can avoid to drain. */
+ QLIST_FOREACH(req, &s->buffered_requests, list) {
+ if (!req->orphaned) {
+#ifdef DEBUG_IDE
+ printf("%s: invoking cb %p of buffered request %p with"
+ " -ECANCELED\n", __func__, req->original_cb, req);
+#endif
+ req->original_cb(req->original_opaque, -ECANCELED);
+ }
+ req->orphaned = true;
+ }
+
+ /*
+ * We can't cancel Scatter Gather DMA in the middle of the
+ * operation or a partial (not full) DMA transfer would reach
+ * the storage so we wait for completion instead (we beahve
+ * like if the DMA was completed by the time the guest trying
+ * to cancel dma with bmdma_cmd_writeb with BM_CMD_START not
+ * set).
+ *
+ * In the future we'll be able to safely cancel the I/O if the
+ * whole DMA operation will be submitted to disk with a single
+ * aio operation with preadv/pwritev.
+ */
+ if (s->bus->dma->aiocb) {
+#ifdef DEBUG_IDE
+ printf("%s: draining all remaining requests", __func__);
+#endif
+ blk_drain(s->blk);
+ assert(s->bus->dma->aiocb == NULL);
+ }
+}
+
static void ide_sector_read(IDEState *s);
static void ide_sector_read_cb(void *opaque, int ret)
@@ -574,7 +681,6 @@ static void ide_sector_read_cb(void *opaque, int ret)
if (ret == -ECANCELED) {
return;
}
- block_acct_done(blk_get_stats(s->blk), &s->acct);
if (ret != 0) {
if (ide_handle_rw_error(s, -ret, IDE_RETRY_PIO |
IDE_RETRY_READ)) {
@@ -582,6 +688,8 @@ static void ide_sector_read_cb(void *opaque, int ret)
}
}
+ block_acct_done(blk_get_stats(s->blk), &s->acct);
+
n = s->nsector;
if (n > s->req_nb_sectors) {
n = s->req_nb_sectors;
@@ -591,7 +699,6 @@ static void ide_sector_read_cb(void *opaque, int ret)
s->nsector -= n;
/* Allow the guest to read the io_buffer */
ide_transfer_start(s, s->io_buffer, n * BDRV_SECTOR_SIZE, ide_sector_read);
- s->io_buffer_offset += 512 * n;
ide_set_irq(s->bus);
}
@@ -622,6 +729,7 @@ static void ide_sector_read(IDEState *s)
if (!ide_sect_range_ok(s, sector_num, n)) {
ide_rw_error(s);
+ block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_READ);
return;
}
@@ -631,15 +739,16 @@ static void ide_sector_read(IDEState *s)
block_acct_start(blk_get_stats(s->blk), &s->acct,
n * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
- s->pio_aiocb = blk_aio_readv(s->blk, sector_num, &s->qiov, n,
- ide_sector_read_cb, s);
+ s->pio_aiocb = ide_buffered_readv(s, sector_num, &s->qiov, n,
+ ide_sector_read_cb, s);
}
-static void dma_buf_commit(IDEState *s, uint32_t tx_bytes)
+void dma_buf_commit(IDEState *s, uint32_t tx_bytes)
{
if (s->bus->dma->ops->commit_buf) {
s->bus->dma->ops->commit_buf(s->bus->dma, tx_bytes);
}
+ s->io_buffer_offset += tx_bytes;
qemu_sglist_destroy(&s->sg);
}
@@ -663,7 +772,7 @@ void ide_dma_error(IDEState *s)
ide_set_irq(s->bus);
}
-static int ide_handle_rw_error(IDEState *s, int error, int op)
+int ide_handle_rw_error(IDEState *s, int error, int op)
{
bool is_read = (op & IDE_RETRY_READ) != 0;
BlockErrorAction action = blk_get_error_action(s->blk, is_read, error);
@@ -672,8 +781,11 @@ static int ide_handle_rw_error(IDEState *s, int error, int op)
assert(s->bus->retry_unit == s->unit);
s->bus->error_status = op;
} else if (action == BLOCK_ERROR_ACTION_REPORT) {
- if (op & IDE_RETRY_DMA) {
+ block_acct_failed(blk_get_stats(s->blk), &s->acct);
+ if (IS_IDE_RETRY_DMA(op)) {
ide_dma_error(s);
+ } else if (IS_IDE_RETRY_ATAPI(op)) {
+ ide_atapi_io_error(s, -error);
} else {
ide_rw_error(s);
}
@@ -693,14 +805,7 @@ static void ide_dma_cb(void *opaque, int ret)
return;
}
if (ret < 0) {
- int op = IDE_RETRY_DMA;
-
- if (s->dma_cmd == IDE_DMA_READ)
- op |= IDE_RETRY_READ;
- else if (s->dma_cmd == IDE_DMA_TRIM)
- op |= IDE_RETRY_TRIM;
-
- if (ide_handle_rw_error(s, -ret, op)) {
+ if (ide_handle_rw_error(s, -ret, ide_dma_cmd_to_retry(s->dma_cmd))) {
return;
}
}
@@ -750,6 +855,7 @@ static void ide_dma_cb(void *opaque, int ret)
if ((s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) &&
!ide_sect_range_ok(s, sector_num, n)) {
ide_dma_error(s);
+ block_acct_invalid(blk_get_stats(s->blk), s->acct.type);
return;
}
@@ -767,6 +873,8 @@ static void ide_dma_cb(void *opaque, int ret)
ide_issue_trim, ide_dma_cb, s,
DMA_DIRECTION_TO_DEVICE);
break;
+ default:
+ abort();
}
return;
@@ -826,7 +934,6 @@ static void ide_sector_write_cb(void *opaque, int ret)
if (ret == -ECANCELED) {
return;
}
- block_acct_done(blk_get_stats(s->blk), &s->acct);
s->pio_aiocb = NULL;
s->status &= ~BUSY_STAT;
@@ -837,12 +944,13 @@ static void ide_sector_write_cb(void *opaque, int ret)
}
}
+ block_acct_done(blk_get_stats(s->blk), &s->acct);
+
n = s->nsector;
if (n > s->req_nb_sectors) {
n = s->req_nb_sectors;
}
s->nsector -= n;
- s->io_buffer_offset += 512 * n;
ide_set_sector(s, ide_get_sector(s) + n);
if (s->nsector == 0) {
@@ -864,8 +972,8 @@ static void ide_sector_write_cb(void *opaque, int ret)
that at the expense of slower write performances. Use this
option _only_ to install Windows 2000. You must disable it
for normal use. */
- timer_mod(s->sector_write_timer,
- qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (get_ticks_per_sec() / 1000));
+ timer_mod(s->sector_write_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ (NANOSECONDS_PER_SECOND / 1000));
} else {
ide_set_irq(s->bus);
}
@@ -888,6 +996,7 @@ static void ide_sector_write(IDEState *s)
if (!ide_sect_range_ok(s, sector_num, n)) {
ide_rw_error(s);
+ block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_WRITE);
return;
}
@@ -896,7 +1005,7 @@ static void ide_sector_write(IDEState *s)
qemu_iovec_init_external(&s->qiov, &s->iov, 1);
block_acct_start(blk_get_stats(s->blk), &s->acct,
- n * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
+ n * BDRV_SECTOR_SIZE, BLOCK_ACCT_WRITE);
s->pio_aiocb = blk_aio_writev(s->blk, sector_num, &s->qiov, n,
ide_sector_write_cb, s);
}
@@ -1122,11 +1231,86 @@ void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val)
}
}
+static void ide_reset(IDEState *s)
+{
+#ifdef DEBUG_IDE
+ printf("ide: reset\n");
+#endif
+
+ if (s->pio_aiocb) {
+ blk_aio_cancel(s->pio_aiocb);
+ s->pio_aiocb = NULL;
+ }
+
+ if (s->drive_kind == IDE_CFATA)
+ s->mult_sectors = 0;
+ else
+ s->mult_sectors = MAX_MULT_SECTORS;
+ /* ide regs */
+ s->feature = 0;
+ s->error = 0;
+ s->nsector = 0;
+ s->sector = 0;
+ s->lcyl = 0;
+ s->hcyl = 0;
+
+ /* lba48 */
+ s->hob_feature = 0;
+ s->hob_sector = 0;
+ s->hob_nsector = 0;
+ s->hob_lcyl = 0;
+ s->hob_hcyl = 0;
+
+ s->select = 0xa0;
+ s->status = READY_STAT | SEEK_STAT;
+
+ s->lba48 = 0;
+
+ /* ATAPI specific */
+ s->sense_key = 0;
+ s->asc = 0;
+ s->cdrom_changed = 0;
+ s->packet_transfer_size = 0;
+ s->elementary_transfer_size = 0;
+ s->io_buffer_index = 0;
+ s->cd_sector_size = 0;
+ s->atapi_dma = 0;
+ s->tray_locked = 0;
+ s->tray_open = 0;
+ /* ATA DMA state */
+ s->io_buffer_size = 0;
+ s->req_nb_sectors = 0;
+
+ ide_set_signature(s);
+ /* init the transfer handler so that 0xffff is returned on data
+ accesses */
+ s->end_transfer_func = ide_dummy_transfer_stop;
+ ide_dummy_transfer_stop(s);
+ s->media_changed = 0;
+}
+
static bool cmd_nop(IDEState *s, uint8_t cmd)
{
return true;
}
+static bool cmd_device_reset(IDEState *s, uint8_t cmd)
+{
+ /* Halt PIO (in the DRQ phase), then DMA */
+ ide_transfer_cancel(s);
+ ide_cancel_dma_sync(s);
+
+ /* Reset any PIO commands, reset signature, etc */
+ ide_reset(s);
+
+ /* RESET: ATA8-ACS3 7.10.4 "Normal Outputs";
+ * ATA8-ACS3 Table 184 "Device Signatures for Normal Output" */
+ s->status = 0x00;
+
+ /* Do not overwrite status register */
+ return false;
+}
+
static bool cmd_data_set_management(IDEState *s, uint8_t cmd)
{
switch (s->feature) {
@@ -1443,15 +1627,6 @@ static bool cmd_exec_dev_diagnostic(IDEState *s, uint8_t cmd)
return false;
}
-static bool cmd_device_reset(IDEState *s, uint8_t cmd)
-{
- ide_set_signature(s);
- s->status = 0x00; /* NOTE: READY is _not_ set */
- s->error = 0x01;
-
- return false;
-}
-
static bool cmd_packet(IDEState *s, uint8_t cmd)
{
/* overlapping commands not supported */
@@ -1462,6 +1637,9 @@ static bool cmd_packet(IDEState *s, uint8_t cmd)
s->status = READY_STAT | SEEK_STAT;
s->atapi_dma = s->feature & 1;
+ if (s->atapi_dma) {
+ s->dma_cmd = IDE_DMA_ATAPI;
+ }
s->nsector = 1;
ide_transfer_start(s, s->io_buffer, ATAPI_PACKET_SIZE,
ide_atapi_cmd);
@@ -1747,11 +1925,11 @@ static const struct {
} ide_cmd_table[0x100] = {
/* NOP not implemented, mandatory for CD */
[CFA_REQ_EXT_ERROR_CODE] = { cmd_cfa_req_ext_error_code, CFA_OK },
- [WIN_DSM] = { cmd_data_set_management, ALL_OK },
+ [WIN_DSM] = { cmd_data_set_management, HD_CFA_OK },
[WIN_DEVICE_RESET] = { cmd_device_reset, CD_OK },
[WIN_RECAL] = { cmd_nop, HD_CFA_OK | SET_DSC},
[WIN_READ] = { cmd_read_pio, ALL_OK },
- [WIN_READ_ONCE] = { cmd_read_pio, ALL_OK },
+ [WIN_READ_ONCE] = { cmd_read_pio, HD_CFA_OK },
[WIN_READ_EXT] = { cmd_read_pio, HD_CFA_OK },
[WIN_READDMA_EXT] = { cmd_read_dma, HD_CFA_OK },
[WIN_READ_NATIVE_MAX_EXT] = { cmd_read_native_max, HD_CFA_OK | SET_DSC },
@@ -1770,12 +1948,12 @@ static const struct {
[CFA_TRANSLATE_SECTOR] = { cmd_cfa_translate_sector, CFA_OK },
[WIN_DIAGNOSE] = { cmd_exec_dev_diagnostic, ALL_OK },
[WIN_SPECIFY] = { cmd_nop, HD_CFA_OK | SET_DSC },
- [WIN_STANDBYNOW2] = { cmd_nop, ALL_OK },
- [WIN_IDLEIMMEDIATE2] = { cmd_nop, ALL_OK },
- [WIN_STANDBY2] = { cmd_nop, ALL_OK },
- [WIN_SETIDLE2] = { cmd_nop, ALL_OK },
- [WIN_CHECKPOWERMODE2] = { cmd_check_power_mode, ALL_OK | SET_DSC },
- [WIN_SLEEPNOW2] = { cmd_nop, ALL_OK },
+ [WIN_STANDBYNOW2] = { cmd_nop, HD_CFA_OK },
+ [WIN_IDLEIMMEDIATE2] = { cmd_nop, HD_CFA_OK },
+ [WIN_STANDBY2] = { cmd_nop, HD_CFA_OK },
+ [WIN_SETIDLE2] = { cmd_nop, HD_CFA_OK },
+ [WIN_CHECKPOWERMODE2] = { cmd_check_power_mode, HD_CFA_OK | SET_DSC },
+ [WIN_SLEEPNOW2] = { cmd_nop, HD_CFA_OK },
[WIN_PACKETCMD] = { cmd_packet, CD_OK },
[WIN_PIDENTIFY] = { cmd_identify_packet, CD_OK },
[WIN_SMART] = { cmd_smart, HD_CFA_OK | SET_DSC },
@@ -1789,19 +1967,19 @@ static const struct {
[WIN_WRITEDMA] = { cmd_write_dma, HD_CFA_OK },
[WIN_WRITEDMA_ONCE] = { cmd_write_dma, HD_CFA_OK },
[CFA_WRITE_MULTI_WO_ERASE] = { cmd_write_multiple, CFA_OK },
- [WIN_STANDBYNOW1] = { cmd_nop, ALL_OK },
- [WIN_IDLEIMMEDIATE] = { cmd_nop, ALL_OK },
- [WIN_STANDBY] = { cmd_nop, ALL_OK },
- [WIN_SETIDLE1] = { cmd_nop, ALL_OK },
- [WIN_CHECKPOWERMODE1] = { cmd_check_power_mode, ALL_OK | SET_DSC },
- [WIN_SLEEPNOW1] = { cmd_nop, ALL_OK },
+ [WIN_STANDBYNOW1] = { cmd_nop, HD_CFA_OK },
+ [WIN_IDLEIMMEDIATE] = { cmd_nop, HD_CFA_OK },
+ [WIN_STANDBY] = { cmd_nop, HD_CFA_OK },
+ [WIN_SETIDLE1] = { cmd_nop, HD_CFA_OK },
+ [WIN_CHECKPOWERMODE1] = { cmd_check_power_mode, HD_CFA_OK | SET_DSC },
+ [WIN_SLEEPNOW1] = { cmd_nop, HD_CFA_OK },
[WIN_FLUSH_CACHE] = { cmd_flush_cache, ALL_OK },
[WIN_FLUSH_CACHE_EXT] = { cmd_flush_cache, HD_CFA_OK },
[WIN_IDENTIFY] = { cmd_identify, ALL_OK },
[WIN_SETFEATURES] = { cmd_set_features, ALL_OK | SET_DSC },
[IBM_SENSE_CONDITION] = { cmd_ibm_sense_condition, CFA_OK | SET_DSC },
[CFA_WEAR_LEVEL] = { cmd_cfa_erase_sectors, HD_CFA_OK | SET_DSC },
- [WIN_READ_NATIVE_MAX] = { cmd_read_native_max, ALL_OK | SET_DSC },
+ [WIN_READ_NATIVE_MAX] = { cmd_read_native_max, HD_CFA_OK | SET_DSC },
};
static bool ide_cmd_permitted(IDEState *s, uint32_t cmd)
@@ -1824,9 +2002,13 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val)
return;
}
- /* Only DEVICE RESET is allowed while BSY or/and DRQ are set */
- if ((s->status & (BUSY_STAT|DRQ_STAT)) && val != WIN_DEVICE_RESET)
- return;
+ /* Only RESET is allowed while BSY and/or DRQ are set,
+ * and only to ATAPI devices. */
+ if (s->status & (BUSY_STAT|DRQ_STAT)) {
+ if (val != WIN_DEVICE_RESET || s->drive_kind != IDE_CD) {
+ return;
+ }
+ }
if (!ide_cmd_permitted(s, val)) {
ide_abort_command(s);
@@ -2126,64 +2308,6 @@ static void ide_dummy_transfer_stop(IDEState *s)
s->io_buffer[3] = 0xff;
}
-static void ide_reset(IDEState *s)
-{
-#ifdef DEBUG_IDE
- printf("ide: reset\n");
-#endif
-
- if (s->pio_aiocb) {
- blk_aio_cancel(s->pio_aiocb);
- s->pio_aiocb = NULL;
- }
-
- if (s->drive_kind == IDE_CFATA)
- s->mult_sectors = 0;
- else
- s->mult_sectors = MAX_MULT_SECTORS;
- /* ide regs */
- s->feature = 0;
- s->error = 0;
- s->nsector = 0;
- s->sector = 0;
- s->lcyl = 0;
- s->hcyl = 0;
-
- /* lba48 */
- s->hob_feature = 0;
- s->hob_sector = 0;
- s->hob_nsector = 0;
- s->hob_lcyl = 0;
- s->hob_hcyl = 0;
-
- s->select = 0xa0;
- s->status = READY_STAT | SEEK_STAT;
-
- s->lba48 = 0;
-
- /* ATAPI specific */
- s->sense_key = 0;
- s->asc = 0;
- s->cdrom_changed = 0;
- s->packet_transfer_size = 0;
- s->elementary_transfer_size = 0;
- s->io_buffer_index = 0;
- s->cd_sector_size = 0;
- s->atapi_dma = 0;
- s->tray_locked = 0;
- s->tray_open = 0;
- /* ATA DMA state */
- s->io_buffer_size = 0;
- s->req_nb_sectors = 0;
-
- ide_set_signature(s);
- /* init the transfer handler so that 0xffff is returned on data
- accesses */
- s->end_transfer_func = ide_dummy_transfer_stop;
- ide_dummy_transfer_stop(s);
- s->media_changed = 0;
-}
-
void ide_bus_reset(IDEBus *bus)
{
bus->unit = 0;
@@ -2313,7 +2437,7 @@ int ide_init_drive(IDEState *s, BlockBackend *blk, IDEDriveKind kind,
if (version) {
pstrcpy(s->version, sizeof(s->version), version);
} else {
- pstrcpy(s->version, sizeof(s->version), qemu_get_version());
+ pstrcpy(s->version, sizeof(s->version), qemu_hw_version());
}
ide_reset(s);
@@ -2400,15 +2524,13 @@ static void ide_restart_bh(void *opaque)
if (s->bus->dma->ops->restart) {
s->bus->dma->ops->restart(s->bus->dma);
}
- }
-
- if (error_status & IDE_RETRY_DMA) {
+ } else if (IS_IDE_RETRY_DMA(error_status)) {
if (error_status & IDE_RETRY_TRIM) {
ide_restart_dma(s, IDE_DMA_TRIM);
} else {
ide_restart_dma(s, is_read ? IDE_DMA_READ : IDE_DMA_WRITE);
}
- } else if (error_status & IDE_RETRY_PIO) {
+ } else if (IS_IDE_RETRY_PIO(error_status)) {
if (is_read) {
ide_sector_read(s);
} else {
@@ -2416,15 +2538,11 @@ static void ide_restart_bh(void *opaque)
}
} else if (error_status & IDE_RETRY_FLUSH) {
ide_flush_cache(s);
+ } else if (IS_IDE_RETRY_ATAPI(error_status)) {
+ assert(s->end_transfer_func == ide_atapi_cmd);
+ ide_atapi_dma_restart(s);
} else {
- /*
- * We've not got any bits to tell us about ATAPI - but
- * we do have the end_transfer_func that tells us what
- * we're trying to do.
- */
- if (s->end_transfer_func == ide_atapi_cmd) {
- ide_atapi_dma_restart(s);
- }
+ abort();
}
}
diff --git a/qemu/hw/ide/ich.c b/qemu/hw/ide/ich.c
index 350c7f1c7..0a13334ba 100644
--- a/qemu/hw/ide/ich.c
+++ b/qemu/hw/ide/ich.c
@@ -60,6 +60,7 @@
*
*/
+#include "qemu/osdep.h"
#include <hw/hw.h>
#include <hw/pci/msi.h>
#include <hw/i386/pc.h>
@@ -97,6 +98,13 @@ static void pci_ich9_reset(DeviceState *dev)
ahci_reset(&d->ahci);
}
+static void pci_ich9_ahci_init(Object *obj)
+{
+ struct AHCIPCIState *d = ICH_AHCI(obj);
+
+ ahci_init(&d->ahci, DEVICE(obj));
+}
+
static void pci_ich9_ahci_realize(PCIDevice *dev, Error **errp)
{
struct AHCIPCIState *d;
@@ -104,7 +112,7 @@ static void pci_ich9_ahci_realize(PCIDevice *dev, Error **errp)
uint8_t *sata_cap;
d = ICH_AHCI(dev);
- ahci_init(&d->ahci, DEVICE(dev), pci_get_address_space(dev), 6);
+ ahci_realize(&d->ahci, DEVICE(dev), pci_get_address_space(dev), 6);
pci_config_set_prog_interface(dev->config, AHCI_PROGMODE_MAJOR_REV_1);
@@ -171,6 +179,7 @@ static const TypeInfo ich_ahci_info = {
.name = TYPE_ICH9_AHCI,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(AHCIPCIState),
+ .instance_init = pci_ich9_ahci_init,
.class_init = ich_ahci_class_init,
};
diff --git a/qemu/hw/ide/internal.h b/qemu/hw/ide/internal.h
index 30fdcbc5f..d2c458f57 100644
--- a/qemu/hw/ide/internal.h
+++ b/qemu/hw/ide/internal.h
@@ -338,11 +338,22 @@ enum ide_dma_cmd {
IDE_DMA_READ,
IDE_DMA_WRITE,
IDE_DMA_TRIM,
+ IDE_DMA_ATAPI,
};
#define ide_cmd_is_read(s) \
((s)->dma_cmd == IDE_DMA_READ)
+typedef struct IDEBufferedRequest {
+ QLIST_ENTRY(IDEBufferedRequest) list;
+ struct iovec iov;
+ QEMUIOVector qiov;
+ QEMUIOVector *original_qiov;
+ BlockCompletionFunc *original_cb;
+ void *original_opaque;
+ bool orphaned;
+} IDEBufferedRequest;
+
/* NOTE: IDEState represents in fact one drive */
struct IDEState {
IDEBus *bus;
@@ -396,8 +407,9 @@ struct IDEState {
BlockAIOCB *pio_aiocb;
struct iovec iov;
QEMUIOVector qiov;
+ QLIST_HEAD(, IDEBufferedRequest) buffered_requests;
/* ATA DMA state */
- int32_t io_buffer_offset;
+ uint64_t io_buffer_offset;
int32_t io_buffer_size;
QEMUSGList sg;
/* PIO transfer handling */
@@ -495,13 +507,45 @@ struct IDEDevice {
};
/* These are used for the error_status field of IDEBus */
+#define IDE_RETRY_MASK 0xf8
#define IDE_RETRY_DMA 0x08
#define IDE_RETRY_PIO 0x10
+#define IDE_RETRY_ATAPI 0x20 /* reused IDE_RETRY_READ bit */
#define IDE_RETRY_READ 0x20
#define IDE_RETRY_FLUSH 0x40
#define IDE_RETRY_TRIM 0x80
#define IDE_RETRY_HBA 0x100
+#define IS_IDE_RETRY_DMA(_status) \
+ ((_status) & IDE_RETRY_DMA)
+
+#define IS_IDE_RETRY_PIO(_status) \
+ ((_status) & IDE_RETRY_PIO)
+
+/*
+ * The method of the IDE_RETRY_ATAPI determination is to use a previously
+ * impossible bit combination as a new status value.
+ */
+#define IS_IDE_RETRY_ATAPI(_status) \
+ (((_status) & IDE_RETRY_MASK) == IDE_RETRY_ATAPI)
+
+static inline uint8_t ide_dma_cmd_to_retry(uint8_t dma_cmd)
+{
+ switch (dma_cmd) {
+ case IDE_DMA_READ:
+ return IDE_RETRY_DMA | IDE_RETRY_READ;
+ case IDE_DMA_WRITE:
+ return IDE_RETRY_DMA;
+ case IDE_DMA_TRIM:
+ return IDE_RETRY_DMA | IDE_RETRY_TRIM;
+ case IDE_DMA_ATAPI:
+ return IDE_RETRY_ATAPI;
+ default:
+ break;
+ }
+ return 0;
+}
+
static inline IDEState *idebus_active_if(IDEBus *bus)
{
return bus->ifs + bus->unit;
@@ -536,7 +580,9 @@ int64_t ide_get_sector(IDEState *s);
void ide_set_sector(IDEState *s, int64_t sector_num);
void ide_start_dma(IDEState *s, BlockCompletionFunc *cb);
+void dma_buf_commit(IDEState *s, uint32_t tx_bytes);
void ide_dma_error(IDEState *s);
+void ide_abort_command(IDEState *s);
void ide_atapi_cmd_ok(IDEState *s);
void ide_atapi_cmd_error(IDEState *s, int sense_key, int asc);
@@ -570,6 +616,10 @@ void ide_set_inactive(IDEState *s, bool more);
BlockAIOCB *ide_issue_trim(BlockBackend *blk,
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
BlockCompletionFunc *cb, void *opaque);
+BlockAIOCB *ide_buffered_readv(IDEState *s, int64_t sector_num,
+ QEMUIOVector *iov, int nb_sectors,
+ BlockCompletionFunc *cb, void *opaque);
+void ide_cancel_dma_sync(IDEState *s);
/* hw/ide/atapi.c */
void ide_atapi_cmd(IDEState *s);
@@ -580,4 +630,6 @@ void ide_bus_new(IDEBus *idebus, size_t idebus_size, DeviceState *dev,
int bus_id, int max_units);
IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive);
+int ide_handle_rw_error(IDEState *s, int error, int op);
+
#endif /* HW_IDE_INTERNAL_H */
diff --git a/qemu/hw/ide/isa.c b/qemu/hw/ide/isa.c
index 9f80503fa..eba567c87 100644
--- a/qemu/hw/ide/isa.c
+++ b/qemu/hw/ide/isa.c
@@ -22,6 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include <hw/hw.h>
#include <hw/i386/pc.h>
#include <hw/isa/isa.h>
diff --git a/qemu/hw/ide/macio.c b/qemu/hw/ide/macio.c
index 66ac2baa9..76256eb8a 100644
--- a/qemu/hw/ide/macio.c
+++ b/qemu/hw/ide/macio.c
@@ -22,6 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/ppc/mac.h"
#include "hw/ppc/mac_dbdma.h"
@@ -119,8 +120,8 @@ static void pmac_dma_read(BlockBackend *blk,
MACIO_DPRINTF("--- Block read transfer - sector_num: %" PRIx64 " "
"nsector: %x\n", (offset >> 9), (bytes >> 9));
- m->aiocb = blk_aio_readv(blk, (offset >> 9), &io->iov, (bytes >> 9),
- cb, io);
+ s->bus->dma->aiocb = blk_aio_readv(blk, (offset >> 9), &io->iov,
+ (bytes >> 9), cb, io);
}
static void pmac_dma_write(BlockBackend *blk,
@@ -204,8 +205,8 @@ static void pmac_dma_write(BlockBackend *blk,
MACIO_DPRINTF("--- Block write transfer - sector_num: %" PRIx64 " "
"nsector: %x\n", (offset >> 9), (bytes >> 9));
- m->aiocb = blk_aio_writev(blk, (offset >> 9), &io->iov, (bytes >> 9),
- cb, io);
+ s->bus->dma->aiocb = blk_aio_writev(blk, (offset >> 9), &io->iov,
+ (bytes >> 9), cb, io);
}
static void pmac_dma_trim(BlockBackend *blk,
@@ -231,8 +232,8 @@ static void pmac_dma_trim(BlockBackend *blk,
s->io_buffer_index += io->len;
io->len = 0;
- m->aiocb = ide_issue_trim(blk, (offset >> 9), &io->iov, (bytes >> 9),
- cb, io);
+ s->bus->dma->aiocb = ide_issue_trim(blk, (offset >> 9), &io->iov,
+ (bytes >> 9), cb, io);
}
static void pmac_ide_atapi_transfer_cb(void *opaque, int ret)
@@ -280,16 +281,20 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret)
}
/* Calculate current offset */
- offset = (int64_t)(s->lba << 11) + s->io_buffer_index;
+ offset = ((int64_t)s->lba << 11) + s->io_buffer_index;
pmac_dma_read(s->blk, offset, io->len, pmac_ide_atapi_transfer_cb, io);
return;
done:
- block_acct_done(blk_get_stats(s->blk), &s->acct);
- io->dma_end(opaque);
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->blk), &s->acct);
+ } else {
+ block_acct_done(blk_get_stats(s->blk), &s->acct);
+ }
- return;
+ ide_set_inactive(s, false);
+ io->dma_end(opaque);
}
static void pmac_ide_transfer_cb(void *opaque, int ret)
@@ -303,7 +308,6 @@ static void pmac_ide_transfer_cb(void *opaque, int ret)
if (ret < 0) {
MACIO_DPRINTF("DMA error: %d\n", ret);
- m->aiocb = NULL;
ide_dma_error(s);
goto done;
}
@@ -342,14 +346,22 @@ static void pmac_ide_transfer_cb(void *opaque, int ret)
case IDE_DMA_TRIM:
pmac_dma_trim(s->blk, offset, io->len, pmac_ide_transfer_cb, io);
break;
+ default:
+ abort();
}
return;
done:
if (s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) {
- block_acct_done(blk_get_stats(s->blk), &s->acct);
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->blk), &s->acct);
+ } else {
+ block_acct_done(blk_get_stats(s->blk), &s->acct);
+ }
}
+
+ ide_set_inactive(s, false);
io->dma_end(opaque);
}
@@ -387,8 +399,9 @@ static void pmac_ide_transfer(DBDMA_io *io)
static void pmac_ide_flush(DBDMA_io *io)
{
MACIOIDEState *m = io->opaque;
+ IDEState *s = idebus_active_if(&m->bus);
- if (m->aiocb) {
+ if (s->bus->dma->aiocb) {
blk_drain_all();
}
}
@@ -506,11 +519,12 @@ static const MemoryRegionOps pmac_ide_ops = {
static const VMStateDescription vmstate_pmac = {
.name = "ide",
- .version_id = 3,
+ .version_id = 4,
.minimum_version_id = 0,
.fields = (VMStateField[]) {
VMSTATE_IDE_BUS(bus, MACIOIDEState),
VMSTATE_IDE_DRIVES(bus.ifs, MACIOIDEState),
+ VMSTATE_BOOL(dma_active, MACIOIDEState),
VMSTATE_END_OF_LIST()
}
};
@@ -590,6 +604,7 @@ static void macio_ide_class_init(ObjectClass *oc, void *data)
dc->realize = macio_ide_realizefn;
dc->reset = macio_ide_reset;
dc->vmsd = &vmstate_pmac;
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
}
static const TypeInfo macio_ide_type_info = {
diff --git a/qemu/hw/ide/microdrive.c b/qemu/hw/ide/microdrive.c
index 6639dd488..5c9db8047 100644
--- a/qemu/hw/ide/microdrive.c
+++ b/qemu/hw/ide/microdrive.c
@@ -22,6 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include <hw/hw.h>
#include <hw/i386/pc.h>
#include <hw/pcmcia.h>
diff --git a/qemu/hw/ide/mmio.c b/qemu/hw/ide/mmio.c
index b6ce62ac5..493f65a1d 100644
--- a/qemu/hw/ide/mmio.c
+++ b/qemu/hw/ide/mmio.c
@@ -22,6 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "sysemu/block-backend.h"
diff --git a/qemu/hw/ide/pci.c b/qemu/hw/ide/pci.c
index d31ff885b..8d56a00b1 100644
--- a/qemu/hw/ide/pci.c
+++ b/qemu/hw/ide/pci.c
@@ -22,6 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include <hw/hw.h>
#include <hw/i386/pc.h>
#include <hw/pci/pci.h>
@@ -103,13 +104,6 @@ static int32_t bmdma_prepare_buf(IDEDMA *dma, int32_t limit)
qemu_sglist_add(&s->sg, bm->cur_prd_addr, sg_len);
}
- /* Note: We limit the max transfer to be 2GiB.
- * This should accommodate the largest ATA transaction
- * for LBA48 (65,536 sectors) and 32K sector sizes. */
- if (s->sg.size > INT32_MAX) {
- error_report("IDE: sglist describes more than 2GiB.");
- break;
- }
bm->cur_prd_addr += l;
bm->cur_prd_len -= l;
s->io_buffer_size += l;
@@ -240,22 +234,7 @@ void bmdma_cmd_writeb(BMDMAState *bm, uint32_t val)
/* Ignore writes to SSBM if it keeps the old value */
if ((val & BM_CMD_START) != (bm->cmd & BM_CMD_START)) {
if (!(val & BM_CMD_START)) {
- /*
- * We can't cancel Scatter Gather DMA in the middle of the
- * operation or a partial (not full) DMA transfer would reach
- * the storage so we wait for completion instead (we beahve
- * like if the DMA was completed by the time the guest trying
- * to cancel dma with bmdma_cmd_writeb with BM_CMD_START not
- * set).
- *
- * In the future we'll be able to safely cancel the I/O if the
- * whole DMA operation will be submitted to disk with a single
- * aio operation with preadv/pwritev.
- */
- if (bm->bus->dma->aiocb) {
- blk_drain_all();
- assert(bm->bus->dma->aiocb == NULL);
- }
+ ide_cancel_dma_sync(idebus_active_if(bm->bus));
bm->status &= ~BM_STATUS_DMAING;
} else {
bm->cur_addr = bm->addr;
@@ -329,6 +308,10 @@ static void ide_bmdma_pre_save(void *opaque)
BMDMAState *bm = opaque;
uint8_t abused_bits = BM_MIGRATION_COMPAT_STATUS_BITS;
+ if (!(bm->status & BM_STATUS_DMAING) && bm->dma_cb) {
+ bm->bus->error_status =
+ ide_dma_cmd_to_retry(bmdma_active_if(bm)->dma_cmd);
+ }
bm->migration_retry_unit = bm->bus->retry_unit;
bm->migration_retry_sector_num = bm->bus->retry_sector_num;
bm->migration_retry_nsector = bm->bus->retry_nsector;
diff --git a/qemu/hw/ide/piix.c b/qemu/hw/ide/piix.c
index 5a26c86a8..6d76ce980 100644
--- a/qemu/hw/ide/piix.c
+++ b/qemu/hw/ide/piix.c
@@ -23,6 +23,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include <hw/hw.h>
#include <hw/i386/pc.h>
#include <hw/pci/pci.h>
@@ -188,6 +189,7 @@ int pci_piix3_xen_ide_unplug(DeviceState *dev)
idedev = pci_ide->bus[di->bus].slave;
}
idedev->conf.blk = NULL;
+ monitor_remove_blk(blk);
blk_unref(blk);
}
}
@@ -257,22 +259,10 @@ static const TypeInfo piix3_ide_info = {
.class_init = piix3_ide_class_init,
};
-static void piix3_ide_xen_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
- k->realize = pci_piix_ide_realize;
- k->vendor_id = PCI_VENDOR_ID_INTEL;
- k->device_id = PCI_DEVICE_ID_INTEL_82371SB_1;
- k->class_id = PCI_CLASS_STORAGE_IDE;
- set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
-}
-
static const TypeInfo piix3_ide_xen_info = {
.name = "piix3-ide-xen",
.parent = TYPE_PCI_IDE,
- .class_init = piix3_ide_xen_class_init,
+ .class_init = piix3_ide_class_init,
};
static void piix4_ide_class_init(ObjectClass *klass, void *data)
diff --git a/qemu/hw/ide/qdev.c b/qemu/hw/ide/qdev.c
index 788b36133..4bc74a32d 100644
--- a/qemu/hw/ide/qdev.c
+++ b/qemu/hw/ide/qdev.c
@@ -16,8 +16,10 @@
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include <hw/hw.h>
#include "sysemu/dma.h"
+#include "qapi/error.h"
#include "qemu/error-report.h"
#include <hw/ide/internal.h>
#include "sysemu/block-backend.h"
@@ -118,7 +120,8 @@ IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive)
dev = qdev_create(&bus->qbus, drive->media_cd ? "ide-cd" : "ide-hd");
qdev_prop_set_uint32(dev, "unit", unit);
- qdev_prop_set_drive_nofail(dev, "drive", blk_by_legacy_dinfo(drive));
+ qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(drive),
+ &error_fatal);
qdev_init_nofail(dev);
return DO_UPCAST(IDEDevice, qdev, dev);
}
@@ -171,7 +174,7 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind)
blkconf_serial(&dev->conf, &dev->serial);
if (kind != IDE_CD) {
- blkconf_geometry(&dev->conf, &dev->chs_trans, 65536, 16, 255, &err);
+ blkconf_geometry(&dev->conf, &dev->chs_trans, 65535, 16, 255, &err);
if (err) {
error_report_err(err);
return -1;
@@ -198,22 +201,22 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind)
return 0;
}
-static void ide_dev_get_bootindex(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void ide_dev_get_bootindex(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
IDEDevice *d = IDE_DEVICE(obj);
- visit_type_int32(v, &d->conf.bootindex, name, errp);
+ visit_type_int32(v, name, &d->conf.bootindex, errp);
}
-static void ide_dev_set_bootindex(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void ide_dev_set_bootindex(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
IDEDevice *d = IDE_DEVICE(obj);
int32_t boot_index;
Error *local_err = NULL;
- visit_type_int32(v, &boot_index, name, &local_err);
+ visit_type_int32(v, name, &boot_index, &local_err);
if (local_err) {
goto out;
}
diff --git a/qemu/hw/ide/via.c b/qemu/hw/ide/via.c
index e2da9ef71..d3f72267a 100644
--- a/qemu/hw/ide/via.c
+++ b/qemu/hw/ide/via.c
@@ -23,6 +23,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include <hw/hw.h>
#include <hw/i386/pc.h>
#include <hw/pci/pci.h>
diff --git a/qemu/hw/input/Makefile.objs b/qemu/hw/input/Makefile.objs
index 624ba7ea4..7715d7230 100644
--- a/qemu/hw/input/Makefile.objs
+++ b/qemu/hw/input/Makefile.objs
@@ -8,9 +8,9 @@ common-obj-$(CONFIG_STELLARIS_INPUT) += stellaris_input.o
common-obj-$(CONFIG_TSC2005) += tsc2005.o
common-obj-$(CONFIG_VMMOUSE) += vmmouse.o
-ifeq ($(CONFIG_LINUX),y)
common-obj-$(CONFIG_VIRTIO) += virtio-input.o
common-obj-$(CONFIG_VIRTIO) += virtio-input-hid.o
+ifeq ($(CONFIG_LINUX),y)
common-obj-$(CONFIG_VIRTIO) += virtio-input-host.o
endif
diff --git a/qemu/hw/input/adb.c b/qemu/hw/input/adb.c
index a18eea265..f0ad0d447 100644
--- a/qemu/hw/input/adb.c
+++ b/qemu/hw/input/adb.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/input/adb.h"
#include "ui/console.h"
@@ -88,7 +89,7 @@ int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len)
}
/* XXX: move that to cuda ? */
-int adb_poll(ADBBusState *s, uint8_t *obuf)
+int adb_poll(ADBBusState *s, uint8_t *obuf, uint16_t poll_mask)
{
ADBDevice *d;
int olen, i;
@@ -99,13 +100,15 @@ int adb_poll(ADBBusState *s, uint8_t *obuf)
if (s->poll_index >= s->nb_devices)
s->poll_index = 0;
d = s->devices[s->poll_index];
- buf[0] = ADB_READREG | (d->devaddr << 4);
- olen = adb_request(s, obuf + 1, buf, 1);
- /* if there is data, we poll again the same device */
- if (olen > 0) {
- obuf[0] = buf[0];
- olen++;
- break;
+ if ((1 << d->devaddr) & poll_mask) {
+ buf[0] = ADB_READREG | (d->devaddr << 4);
+ olen = adb_request(s, obuf + 1, buf, 1);
+ /* if there is data, we poll again the same device */
+ if (olen > 0) {
+ obuf[0] = buf[0];
+ olen++;
+ break;
+ }
}
s->poll_index++;
}
@@ -362,6 +365,7 @@ static void adb_kbd_class_init(ObjectClass *oc, void *data)
akc->parent_realize = dc->realize;
dc->realize = adb_kbd_realizefn;
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
adc->devreq = adb_kbd_request;
dc->reset = adb_kbd_reset;
@@ -566,6 +570,7 @@ static void adb_mouse_class_init(ObjectClass *oc, void *data)
amc->parent_realize = dc->realize;
dc->realize = adb_mouse_realizefn;
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
adc->devreq = adb_mouse_request;
dc->reset = adb_mouse_reset;
diff --git a/qemu/hw/input/hid.c b/qemu/hw/input/hid.c
index 21ebd9e71..d92c7463b 100644
--- a/qemu/hw/input/hid.c
+++ b/qemu/hw/input/hid.c
@@ -22,6 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "ui/console.h"
#include "qemu/timer.h"
@@ -44,7 +45,7 @@ static const uint8_t hid_usage_keys[0x100] = {
0xe2, 0x2c, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f,
0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59,
- 0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x00, 0x44,
+ 0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x64, 0x44,
0x45, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
0xe8, 0xe9, 0x71, 0x72, 0x73, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00,
@@ -95,7 +96,7 @@ void hid_set_next_idle(HIDState *hs)
{
if (hs->idle) {
uint64_t expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
- get_ticks_per_sec() * hs->idle * 4 / 1000;
+ NANOSECONDS_PER_SECOND * hs->idle * 4 / 1000;
if (!hs->idle_timer) {
hs->idle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, hid_idle_timer, hs);
}
@@ -108,44 +109,49 @@ void hid_set_next_idle(HIDState *hs)
static void hid_pointer_event(DeviceState *dev, QemuConsole *src,
InputEvent *evt)
{
- static const int bmap[INPUT_BUTTON_MAX] = {
+ static const int bmap[INPUT_BUTTON__MAX] = {
[INPUT_BUTTON_LEFT] = 0x01,
[INPUT_BUTTON_RIGHT] = 0x02,
[INPUT_BUTTON_MIDDLE] = 0x04,
};
HIDState *hs = (HIDState *)dev;
HIDPointerEvent *e;
+ InputMoveEvent *move;
+ InputBtnEvent *btn;
assert(hs->n < QUEUE_LENGTH);
e = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];
- switch (evt->kind) {
+ switch (evt->type) {
case INPUT_EVENT_KIND_REL:
- if (evt->rel->axis == INPUT_AXIS_X) {
- e->xdx += evt->rel->value;
- } else if (evt->rel->axis == INPUT_AXIS_Y) {
- e->ydy += evt->rel->value;
+ move = evt->u.rel.data;
+ if (move->axis == INPUT_AXIS_X) {
+ e->xdx += move->value;
+ } else if (move->axis == INPUT_AXIS_Y) {
+ e->ydy += move->value;
}
break;
case INPUT_EVENT_KIND_ABS:
- if (evt->rel->axis == INPUT_AXIS_X) {
- e->xdx = evt->rel->value;
- } else if (evt->rel->axis == INPUT_AXIS_Y) {
- e->ydy = evt->rel->value;
+ move = evt->u.abs.data;
+ if (move->axis == INPUT_AXIS_X) {
+ e->xdx = move->value;
+ } else if (move->axis == INPUT_AXIS_Y) {
+ e->ydy = move->value;
}
break;
case INPUT_EVENT_KIND_BTN:
- if (evt->btn->down) {
- e->buttons_state |= bmap[evt->btn->button];
- if (evt->btn->button == INPUT_BUTTON_WHEEL_UP) {
+ btn = evt->u.btn.data;
+ if (btn->down) {
+ e->buttons_state |= bmap[btn->button];
+ if (btn->button == INPUT_BUTTON_WHEEL_UP) {
e->dz--;
- } else if (evt->btn->button == INPUT_BUTTON_WHEEL_DOWN) {
+ } else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
e->dz++;
}
} else {
- e->buttons_state &= ~bmap[evt->btn->button];
+ e->buttons_state &= ~bmap[btn->button];
}
break;
@@ -222,9 +228,10 @@ static void hid_keyboard_event(DeviceState *dev, QemuConsole *src,
HIDState *hs = (HIDState *)dev;
int scancodes[3], i, count;
int slot;
+ InputKeyEvent *key = evt->u.key.data;
- count = qemu_input_key_value_to_scancode(evt->key->key,
- evt->key->down,
+ count = qemu_input_key_value_to_scancode(key->key,
+ key->down,
scancodes);
if (hs->n + count > QUEUE_LENGTH) {
fprintf(stderr, "usb-kbd: warning: key event queue full\n");
diff --git a/qemu/hw/input/lm832x.c b/qemu/hw/input/lm832x.c
index 530a6e01f..539682cac 100644
--- a/qemu/hw/input/lm832x.c
+++ b/qemu/hw/input/lm832x.c
@@ -18,6 +18,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i2c/i2c.h"
#include "qemu/timer.h"
diff --git a/qemu/hw/input/milkymist-softusb.c b/qemu/hw/input/milkymist-softusb.c
index 7b0f4db88..40dfca157 100644
--- a/qemu/hw/input/milkymist-softusb.c
+++ b/qemu/hw/input/milkymist-softusb.c
@@ -21,6 +21,8 @@
* not available yet
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "trace.h"
@@ -255,12 +257,12 @@ static int milkymist_softusb_init(SysBusDevice *dev)
/* register pmem and dmem */
memory_region_init_ram(&s->pmem, OBJECT(s), "milkymist-softusb.pmem",
- s->pmem_size, &error_abort);
+ s->pmem_size, &error_fatal);
vmstate_register_ram_global(&s->pmem);
s->pmem_ptr = memory_region_get_ram_ptr(&s->pmem);
sysbus_init_mmio(dev, &s->pmem);
memory_region_init_ram(&s->dmem, OBJECT(s), "milkymist-softusb.dmem",
- s->dmem_size, &error_abort);
+ s->dmem_size, &error_fatal);
vmstate_register_ram_global(&s->dmem);
s->dmem_ptr = memory_region_get_ram_ptr(&s->dmem);
sysbus_init_mmio(dev, &s->dmem);
diff --git a/qemu/hw/input/pckbd.c b/qemu/hw/input/pckbd.c
index ddac69df6..1d932ec19 100644
--- a/qemu/hw/input/pckbd.c
+++ b/qemu/hw/input/pckbd.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/isa/isa.h"
#include "hw/i386/pc.h"
diff --git a/qemu/hw/input/pl050.c b/qemu/hw/input/pl050.c
index c1b08d5a4..3092b0fe3 100644
--- a/qemu/hw/input/pl050.c
+++ b/qemu/hw/input/pl050.c
@@ -7,6 +7,7 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/input/ps2.h"
diff --git a/qemu/hw/input/ps2.c b/qemu/hw/input/ps2.c
index fdbe565e6..a8aa36f5c 100644
--- a/qemu/hw/input/ps2.c
+++ b/qemu/hw/input/ps2.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/input/ps2.h"
#include "ui/console.h"
@@ -181,10 +182,11 @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src,
{
PS2KbdState *s = (PS2KbdState *)dev;
int scancodes[3], i, count;
+ InputKeyEvent *key = evt->u.key.data;
qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
- count = qemu_input_key_value_to_scancode(evt->key->key,
- evt->key->down,
+ count = qemu_input_key_value_to_scancode(key->key,
+ key->down,
scancodes);
for (i = 0; i < count; i++) {
ps2_put_keycode(s, scancodes[i]);
@@ -382,36 +384,40 @@ static void ps2_mouse_send_packet(PS2MouseState *s)
static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
InputEvent *evt)
{
- static const int bmap[INPUT_BUTTON_MAX] = {
+ static const int bmap[INPUT_BUTTON__MAX] = {
[INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON,
[INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON,
[INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON,
};
PS2MouseState *s = (PS2MouseState *)dev;
+ InputMoveEvent *move;
+ InputBtnEvent *btn;
/* check if deltas are recorded when disabled */
if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
return;
- switch (evt->kind) {
+ switch (evt->type) {
case INPUT_EVENT_KIND_REL:
- if (evt->rel->axis == INPUT_AXIS_X) {
- s->mouse_dx += evt->rel->value;
- } else if (evt->rel->axis == INPUT_AXIS_Y) {
- s->mouse_dy -= evt->rel->value;
+ move = evt->u.rel.data;
+ if (move->axis == INPUT_AXIS_X) {
+ s->mouse_dx += move->value;
+ } else if (move->axis == INPUT_AXIS_Y) {
+ s->mouse_dy -= move->value;
}
break;
case INPUT_EVENT_KIND_BTN:
- if (evt->btn->down) {
- s->mouse_buttons |= bmap[evt->btn->button];
- if (evt->btn->button == INPUT_BUTTON_WHEEL_UP) {
+ btn = evt->u.btn.data;
+ if (btn->down) {
+ s->mouse_buttons |= bmap[btn->button];
+ if (btn->button == INPUT_BUTTON_WHEEL_UP) {
s->mouse_dz--;
- } else if (evt->btn->button == INPUT_BUTTON_WHEEL_DOWN) {
+ } else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
s->mouse_dz++;
}
} else {
- s->mouse_buttons &= ~bmap[evt->btn->button];
+ s->mouse_buttons &= ~bmap[btn->button];
}
break;
@@ -622,7 +628,7 @@ static void ps2_kbd_reset(void *opaque)
ps2_common_reset(&s->common);
s->scan_enabled = 0;
s->translate = 0;
- s->scancode_set = 0;
+ s->scancode_set = 2;
}
static void ps2_mouse_reset(void *opaque)
diff --git a/qemu/hw/input/pxa2xx_keypad.c b/qemu/hw/input/pxa2xx_keypad.c
index 85011145e..2b70bbb95 100644
--- a/qemu/hw/input/pxa2xx_keypad.c
+++ b/qemu/hw/input/pxa2xx_keypad.c
@@ -11,6 +11,7 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/arm/pxa.h"
#include "ui/console.h"
diff --git a/qemu/hw/input/stellaris_input.c b/qemu/hw/input/stellaris_input.c
index 0609e8086..99168bfee 100644
--- a/qemu/hw/input/stellaris_input.c
+++ b/qemu/hw/input/stellaris_input.c
@@ -6,6 +6,7 @@
*
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/devices.h"
#include "ui/console.h"
@@ -69,14 +70,14 @@ static const VMStateDescription vmstate_stellaris_gamepad = {
}
};
-/* Returns an array 5 ouput slots. */
+/* Returns an array of 5 output slots. */
void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode)
{
gamepad_state *s;
int i;
- s = (gamepad_state *)g_malloc0(sizeof (gamepad_state));
- s->buttons = (gamepad_button *)g_malloc0(n * sizeof (gamepad_button));
+ s = g_new0(gamepad_state, 1);
+ s->buttons = g_new0(gamepad_button, n);
for (i = 0; i < n; i++) {
s->buttons[i].irq = irq[i];
s->buttons[i].keycode = keycode[i];
diff --git a/qemu/hw/input/tsc2005.c b/qemu/hw/input/tsc2005.c
index 21d4f4dbb..9b359aaec 100644
--- a/qemu/hw/input/tsc2005.c
+++ b/qemu/hw/input/tsc2005.c
@@ -18,6 +18,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "qemu/timer.h"
#include "ui/console.h"
@@ -290,7 +291,8 @@ static void tsc2005_pin_update(TSC2005State *s)
s->precision = s->nextprecision;
s->function = s->nextfunction;
s->pdst = !s->pnd0; /* Synchronised on internal clock */
- expires = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (get_ticks_per_sec() >> 7);
+ expires = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ (NANOSECONDS_PER_SECOND >> 7);
timer_mod(s->timer, expires);
}
diff --git a/qemu/hw/input/tsc210x.c b/qemu/hw/input/tsc210x.c
index fae338563..93ca374fc 100644
--- a/qemu/hw/input/tsc210x.c
+++ b/qemu/hw/input/tsc210x.c
@@ -19,6 +19,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "audio/audio.h"
#include "qemu/timer.h"
@@ -834,7 +835,8 @@ static void tsc210x_pin_update(TSC210xState *s)
s->busy = 1;
s->precision = s->nextprecision;
s->function = s->nextfunction;
- expires = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (get_ticks_per_sec() >> 10);
+ expires = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ (NANOSECONDS_PER_SECOND >> 10);
timer_mod(s->timer, expires);
}
@@ -1086,9 +1088,7 @@ uWireSlave *tsc2102_init(qemu_irq pint)
{
TSC210xState *s;
- s = (TSC210xState *)
- g_malloc0(sizeof(TSC210xState));
- memset(s, 0, sizeof(TSC210xState));
+ s = g_new0(TSC210xState, 1);
s->x = 160;
s->y = 160;
s->pressure = 0;
@@ -1135,9 +1135,7 @@ uWireSlave *tsc2301_init(qemu_irq penirq, qemu_irq kbirq, qemu_irq dav)
{
TSC210xState *s;
- s = (TSC210xState *)
- g_malloc0(sizeof(TSC210xState));
- memset(s, 0, sizeof(TSC210xState));
+ s = g_new0(TSC210xState, 1);
s->x = 400;
s->y = 240;
s->pressure = 0;
diff --git a/qemu/hw/input/virtio-input-hid.c b/qemu/hw/input/virtio-input-hid.c
index 4d85dad4d..3ee0c1814 100644
--- a/qemu/hw/input/virtio-input-hid.c
+++ b/qemu/hw/input/virtio-input-hid.c
@@ -4,6 +4,7 @@
* top-level directory.
*/
+#include "qemu/osdep.h"
#include "qemu/iov.h"
#include "hw/qdev.h"
@@ -21,7 +22,7 @@
/* ----------------------------------------------------------------- */
-static const unsigned int keymap_qcode[Q_KEY_CODE_MAX] = {
+static const unsigned int keymap_qcode[Q_KEY_CODE__MAX] = {
[Q_KEY_CODE_ESC] = KEY_ESC,
[Q_KEY_CODE_1] = KEY_1,
[Q_KEY_CODE_2] = KEY_2,
@@ -120,6 +121,8 @@ static const unsigned int keymap_qcode[Q_KEY_CODE_MAX] = {
[Q_KEY_CODE_CTRL_R] = KEY_RIGHTCTRL,
[Q_KEY_CODE_SYSRQ] = KEY_SYSRQ,
+ [Q_KEY_CODE_PRINT] = KEY_SYSRQ,
+ [Q_KEY_CODE_PAUSE] = KEY_PAUSE,
[Q_KEY_CODE_ALT_R] = KEY_RIGHTALT,
[Q_KEY_CODE_HOME] = KEY_HOME,
@@ -138,7 +141,7 @@ static const unsigned int keymap_qcode[Q_KEY_CODE_MAX] = {
[Q_KEY_CODE_MENU] = KEY_MENU,
};
-static const unsigned int keymap_button[INPUT_BUTTON_MAX] = {
+static const unsigned int keymap_button[INPUT_BUTTON__MAX] = {
[INPUT_BUTTON_LEFT] = BTN_LEFT,
[INPUT_BUTTON_RIGHT] = BTN_RIGHT,
[INPUT_BUTTON_MIDDLE] = BTN_MIDDLE,
@@ -146,12 +149,12 @@ static const unsigned int keymap_button[INPUT_BUTTON_MAX] = {
[INPUT_BUTTON_WHEEL_DOWN] = BTN_GEAR_DOWN,
};
-static const unsigned int axismap_rel[INPUT_AXIS_MAX] = {
+static const unsigned int axismap_rel[INPUT_AXIS__MAX] = {
[INPUT_AXIS_X] = REL_X,
[INPUT_AXIS_Y] = REL_Y,
};
-static const unsigned int axismap_abs[INPUT_AXIS_MAX] = {
+static const unsigned int axismap_abs[INPUT_AXIS__MAX] = {
[INPUT_AXIS_X] = ABS_X,
[INPUT_AXIS_Y] = ABS_Y,
};
@@ -190,45 +193,53 @@ static void virtio_input_handle_event(DeviceState *dev, QemuConsole *src,
VirtIOInput *vinput = VIRTIO_INPUT(dev);
virtio_input_event event;
int qcode;
+ InputKeyEvent *key;
+ InputMoveEvent *move;
+ InputBtnEvent *btn;
- switch (evt->kind) {
+ switch (evt->type) {
case INPUT_EVENT_KIND_KEY:
- qcode = qemu_input_key_value_to_qcode(evt->key->key);
+ key = evt->u.key.data;
+ qcode = qemu_input_key_value_to_qcode(key->key);
if (qcode && keymap_qcode[qcode]) {
event.type = cpu_to_le16(EV_KEY);
event.code = cpu_to_le16(keymap_qcode[qcode]);
- event.value = cpu_to_le32(evt->key->down ? 1 : 0);
+ event.value = cpu_to_le32(key->down ? 1 : 0);
virtio_input_send(vinput, &event);
} else {
- if (evt->key->down) {
+ if (key->down) {
fprintf(stderr, "%s: unmapped key: %d [%s]\n", __func__,
qcode, QKeyCode_lookup[qcode]);
}
}
break;
case INPUT_EVENT_KIND_BTN:
- if (keymap_button[evt->btn->button]) {
+ btn = evt->u.btn.data;
+ if (keymap_button[btn->button]) {
event.type = cpu_to_le16(EV_KEY);
- event.code = cpu_to_le16(keymap_button[evt->btn->button]);
- event.value = cpu_to_le32(evt->btn->down ? 1 : 0);
+ event.code = cpu_to_le16(keymap_button[btn->button]);
+ event.value = cpu_to_le32(btn->down ? 1 : 0);
virtio_input_send(vinput, &event);
} else {
- if (evt->btn->down) {
+ if (btn->down) {
fprintf(stderr, "%s: unmapped button: %d [%s]\n", __func__,
- evt->btn->button, InputButton_lookup[evt->btn->button]);
+ btn->button,
+ InputButton_lookup[btn->button]);
}
}
break;
case INPUT_EVENT_KIND_REL:
+ move = evt->u.rel.data;
event.type = cpu_to_le16(EV_REL);
- event.code = cpu_to_le16(axismap_rel[evt->rel->axis]);
- event.value = cpu_to_le32(evt->rel->value);
+ event.code = cpu_to_le16(axismap_rel[move->axis]);
+ event.value = cpu_to_le32(move->value);
virtio_input_send(vinput, &event);
break;
case INPUT_EVENT_KIND_ABS:
+ move = evt->u.abs.data;
event.type = cpu_to_le16(EV_ABS);
- event.code = cpu_to_le16(axismap_abs[evt->abs->axis]);
- event.value = cpu_to_le32(evt->abs->value);
+ event.code = cpu_to_le16(axismap_abs[move->axis]);
+ event.value = cpu_to_le32(move->value);
virtio_input_send(vinput, &event);
break;
default:
@@ -473,12 +484,12 @@ static struct virtio_input_config virtio_tablet_config[] = {
.select = VIRTIO_INPUT_CFG_ABS_INFO,
.subsel = ABS_X,
.size = sizeof(virtio_input_absinfo),
- .u.abs.max = const_le32(INPUT_EVENT_ABS_SIZE),
+ .u.abs.max = const_le32(INPUT_EVENT_ABS_SIZE - 1),
},{
.select = VIRTIO_INPUT_CFG_ABS_INFO,
.subsel = ABS_Y,
.size = sizeof(virtio_input_absinfo),
- .u.abs.max = const_le32(INPUT_EVENT_ABS_SIZE),
+ .u.abs.max = const_le32(INPUT_EVENT_ABS_SIZE - 1),
},
{ /* end of list */ },
};
diff --git a/qemu/hw/input/virtio-input-host.c b/qemu/hw/input/virtio-input-host.c
index 8978f16ba..cb79e8002 100644
--- a/qemu/hw/input/virtio-input-host.c
+++ b/qemu/hw/input/virtio-input-host.c
@@ -4,6 +4,8 @@
* top-level directory.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu-common.h"
#include "qemu/sockets.h"
@@ -68,13 +70,39 @@ static void virtio_input_bits_config(VirtIOInputHost *vih,
virtio_input_add_config(VIRTIO_INPUT(vih), &bits);
}
+static void virtio_input_abs_config(VirtIOInputHost *vih, int axis)
+{
+ virtio_input_config config;
+ struct input_absinfo absinfo;
+ int rc;
+
+ rc = ioctl(vih->fd, EVIOCGABS(axis), &absinfo);
+ if (rc < 0) {
+ return;
+ }
+
+ memset(&config, 0, sizeof(config));
+ config.select = VIRTIO_INPUT_CFG_ABS_INFO;
+ config.subsel = axis;
+ config.size = sizeof(virtio_input_absinfo);
+
+ config.u.abs.min = cpu_to_le32(absinfo.minimum);
+ config.u.abs.max = cpu_to_le32(absinfo.maximum);
+ config.u.abs.fuzz = cpu_to_le32(absinfo.fuzz);
+ config.u.abs.flat = cpu_to_le32(absinfo.flat);
+ config.u.abs.res = cpu_to_le32(absinfo.resolution);
+
+ virtio_input_add_config(VIRTIO_INPUT(vih), &config);
+}
+
static void virtio_input_host_realize(DeviceState *dev, Error **errp)
{
VirtIOInputHost *vih = VIRTIO_INPUT_HOST(dev);
VirtIOInput *vinput = VIRTIO_INPUT(dev);
- virtio_input_config id;
+ virtio_input_config id, *abs;
struct input_id ids;
- int rc, ver;
+ int rc, ver, i, axis;
+ uint8_t byte;
if (!vih->evdev) {
error_setg(errp, "evdev property is required");
@@ -123,6 +151,23 @@ static void virtio_input_host_realize(DeviceState *dev, Error **errp)
virtio_input_bits_config(vih, EV_ABS, ABS_CNT);
virtio_input_bits_config(vih, EV_MSC, MSC_CNT);
virtio_input_bits_config(vih, EV_SW, SW_CNT);
+ virtio_input_bits_config(vih, EV_LED, LED_CNT);
+
+ abs = virtio_input_find_config(VIRTIO_INPUT(vih),
+ VIRTIO_INPUT_CFG_EV_BITS, EV_ABS);
+ if (abs) {
+ for (i = 0; i < abs->size; i++) {
+ byte = abs->u.bitmap[i];
+ axis = 8 * i;
+ while (byte) {
+ if (byte & 1) {
+ virtio_input_abs_config(vih, axis);
+ }
+ axis++;
+ byte >>= 1;
+ }
+ }
+ }
qemu_set_fd_handler(vih->fd, virtio_input_host_event, NULL, vih);
return;
@@ -143,6 +188,28 @@ static void virtio_input_host_unrealize(DeviceState *dev, Error **errp)
}
}
+static void virtio_input_host_handle_status(VirtIOInput *vinput,
+ virtio_input_event *event)
+{
+ VirtIOInputHost *vih = VIRTIO_INPUT_HOST(vinput);
+ struct input_event evdev;
+ int rc;
+
+ if (gettimeofday(&evdev.time, NULL)) {
+ perror("virtio_input_host_handle_status: gettimeofday");
+ return;
+ }
+
+ evdev.type = le16_to_cpu(event->type);
+ evdev.code = le16_to_cpu(event->code);
+ evdev.value = le32_to_cpu(event->value);
+
+ rc = write(vih->fd, &evdev, sizeof(evdev));
+ if (rc == -1) {
+ perror("virtio_input_host_handle_status: write");
+ }
+}
+
static const VMStateDescription vmstate_virtio_input_host = {
.name = "virtio-input-host",
.unmigratable = 1,
@@ -162,6 +229,7 @@ static void virtio_input_host_class_init(ObjectClass *klass, void *data)
dc->props = virtio_input_host_properties;
vic->realize = virtio_input_host_realize;
vic->unrealize = virtio_input_host_unrealize;
+ vic->handle_status = virtio_input_host_handle_status;
}
static void virtio_input_host_init(Object *obj)
diff --git a/qemu/hw/input/virtio-input.c b/qemu/hw/input/virtio-input.c
index 7b25d2769..f59749a94 100644
--- a/qemu/hw/input/virtio-input.c
+++ b/qemu/hw/input/virtio-input.c
@@ -4,6 +4,8 @@
* top-level directory.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu/iov.h"
#include "hw/qdev.h"
@@ -12,14 +14,20 @@
#include "standard-headers/linux/input.h"
+#define VIRTIO_INPUT_VM_VERSION 1
+
/* ----------------------------------------------------------------- */
void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event)
{
- VirtQueueElement elem;
+ VirtQueueElement *elem;
unsigned have, need;
int i, len;
+ if (!vinput->active) {
+ return;
+ }
+
/* queue up events ... */
if (vinput->qindex == vinput->qsize) {
vinput->qsize++;
@@ -45,14 +53,16 @@ void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event)
/* ... and finally pass them to the guest */
for (i = 0; i < vinput->qindex; i++) {
- if (!virtqueue_pop(vinput->evt, &elem)) {
+ elem = virtqueue_pop(vinput->evt, sizeof(VirtQueueElement));
+ if (!elem) {
/* should not happen, we've checked for space beforehand */
fprintf(stderr, "%s: Huh? No vq elem available ...\n", __func__);
return;
}
- len = iov_from_buf(elem.in_sg, elem.in_num,
+ len = iov_from_buf(elem->in_sg, elem->in_num,
0, vinput->queue+i, sizeof(virtio_input_event));
- virtqueue_push(vinput->evt, &elem, len);
+ virtqueue_push(vinput->evt, elem, len);
+ g_free(elem);
}
virtio_notify(VIRTIO_DEVICE(vinput), vinput->evt);
vinput->qindex = 0;
@@ -68,24 +78,30 @@ static void virtio_input_handle_sts(VirtIODevice *vdev, VirtQueue *vq)
VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vdev);
VirtIOInput *vinput = VIRTIO_INPUT(vdev);
virtio_input_event event;
- VirtQueueElement elem;
+ VirtQueueElement *elem;
int len;
- while (virtqueue_pop(vinput->sts, &elem)) {
+ for (;;) {
+ elem = virtqueue_pop(vinput->sts, sizeof(VirtQueueElement));
+ if (!elem) {
+ break;
+ }
+
memset(&event, 0, sizeof(event));
- len = iov_to_buf(elem.out_sg, elem.out_num,
+ len = iov_to_buf(elem->out_sg, elem->out_num,
0, &event, sizeof(event));
if (vic->handle_status) {
vic->handle_status(vinput, &event);
}
- virtqueue_push(vinput->sts, &elem, len);
+ virtqueue_push(vinput->sts, elem, len);
+ g_free(elem);
}
virtio_notify(vdev, vinput->sts);
}
-static virtio_input_config *virtio_input_find_config(VirtIOInput *vinput,
- uint8_t select,
- uint8_t subsel)
+virtio_input_config *virtio_input_find_config(VirtIOInput *vinput,
+ uint8_t select,
+ uint8_t subsel)
{
VirtIOInputConfig *cfg;
@@ -200,6 +216,38 @@ static void virtio_input_reset(VirtIODevice *vdev)
}
}
+static void virtio_input_save(QEMUFile *f, void *opaque)
+{
+ VirtIOInput *vinput = opaque;
+ VirtIODevice *vdev = VIRTIO_DEVICE(vinput);
+
+ virtio_save(vdev, f);
+}
+
+static int virtio_input_load(QEMUFile *f, void *opaque, int version_id)
+{
+ VirtIOInput *vinput = opaque;
+ VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vinput);
+ VirtIODevice *vdev = VIRTIO_DEVICE(vinput);
+ int ret;
+
+ if (version_id != VIRTIO_INPUT_VM_VERSION) {
+ return -EINVAL;
+ }
+
+ ret = virtio_load(vdev, f, version_id);
+ if (ret) {
+ return ret;
+ }
+
+ /* post_load() */
+ vinput->active = vdev->status & VIRTIO_CONFIG_S_DRIVER_OK;
+ if (vic->change_active) {
+ vic->change_active(vinput);
+ }
+ return 0;
+}
+
static void virtio_input_device_realize(DeviceState *dev, Error **errp)
{
VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev);
@@ -231,14 +279,20 @@ static void virtio_input_device_realize(DeviceState *dev, Error **errp)
vinput->cfg_size);
vinput->evt = virtio_add_queue(vdev, 64, virtio_input_handle_evt);
vinput->sts = virtio_add_queue(vdev, 64, virtio_input_handle_sts);
+
+ register_savevm(dev, "virtio-input", -1, VIRTIO_INPUT_VM_VERSION,
+ virtio_input_save, virtio_input_load, vinput);
}
static void virtio_input_device_unrealize(DeviceState *dev, Error **errp)
{
VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev);
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VirtIOInput *vinput = VIRTIO_INPUT(dev);
Error *local_err = NULL;
+ unregister_savevm(dev, "virtio-input", vinput);
+
if (vic->unrealize) {
vic->unrealize(dev, &local_err);
if (local_err) {
diff --git a/qemu/hw/input/vmmouse.c b/qemu/hw/input/vmmouse.c
index d7b1c76f5..6d15a887c 100644
--- a/qemu/hw/input/vmmouse.c
+++ b/qemu/hw/input/vmmouse.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "ui/console.h"
#include "hw/input/ps2.h"
diff --git a/qemu/hw/intc/Makefile.objs b/qemu/hw/intc/Makefile.objs
index 092d8a80a..0e47f0f9e 100644
--- a/qemu/hw/intc/Makefile.objs
+++ b/qemu/hw/intc/Makefile.objs
@@ -12,19 +12,23 @@ common-obj-$(CONFIG_IOAPIC) += ioapic_common.o
common-obj-$(CONFIG_ARM_GIC) += arm_gic_common.o
common-obj-$(CONFIG_ARM_GIC) += arm_gic.o
common-obj-$(CONFIG_ARM_GIC) += arm_gicv2m.o
+common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_common.o
common-obj-$(CONFIG_OPENPIC) += openpic.o
obj-$(CONFIG_APIC) += apic.o apic_common.o
obj-$(CONFIG_ARM_GIC_KVM) += arm_gic_kvm.o
+obj-$(call land,$(CONFIG_ARM_GIC_KVM),$(TARGET_AARCH64)) += arm_gicv3_kvm.o
obj-$(CONFIG_STELLARIS) += armv7m_nvic.o
obj-$(CONFIG_EXYNOS4) += exynos4210_gic.o exynos4210_combiner.o
obj-$(CONFIG_GRLIB) += grlib_irqmp.o
obj-$(CONFIG_IOAPIC) += ioapic.o
obj-$(CONFIG_OMAP) += omap_intc.o
obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o
+obj-$(CONFIG_RASPI) += bcm2835_ic.o bcm2836_control.o
obj-$(CONFIG_SH4) += sh_intc.o
obj-$(CONFIG_XICS) += xics.o
obj-$(CONFIG_XICS_KVM) += xics_kvm.o
obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
obj-$(CONFIG_S390_FLIC) += s390_flic.o
obj-$(CONFIG_S390_FLIC_KVM) += s390_flic_kvm.o
+obj-$(CONFIG_ASPEED_SOC) += aspeed_vic.o
diff --git a/qemu/hw/intc/allwinner-a10-pic.c b/qemu/hw/intc/allwinner-a10-pic.c
index eed7621f1..dc971a160 100644
--- a/qemu/hw/intc/allwinner-a10-pic.c
+++ b/qemu/hw/intc/allwinner-a10-pic.c
@@ -15,6 +15,7 @@
* for more details.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/devices.h"
#include "sysemu/sysemu.h"
diff --git a/qemu/hw/intc/apic.c b/qemu/hw/intc/apic.c
index 77b639cce..28c2ea540 100644
--- a/qemu/hw/intc/apic.c
+++ b/qemu/hw/intc/apic.c
@@ -16,6 +16,7 @@
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>
*/
+#include "qemu/osdep.h"
#include "qemu/thread.h"
#include "hw/i386/apic_internal.h"
#include "hw/i386/apic.h"
@@ -51,14 +52,6 @@ static int apic_ffs_bit(uint32_t value)
return ctz32(value);
}
-static inline void apic_set_bit(uint32_t *tab, int index)
-{
- int i, mask;
- i = index >> 5;
- mask = 1 << (index & 0x1f);
- tab[i] |= mask;
-}
-
static inline void apic_reset_bit(uint32_t *tab, int index)
{
int i, mask;
@@ -67,14 +60,6 @@ static inline void apic_reset_bit(uint32_t *tab, int index)
tab[i] &= ~mask;
}
-static inline int apic_get_bit(uint32_t *tab, int index)
-{
- int i, mask;
- i = index >> 5;
- mask = 1 << (index & 0x1f);
- return !!(tab[i] & mask);
-}
-
/* return -1 if no bit is set */
static int get_highest_priority_int(uint32_t *tab)
{
@@ -318,7 +303,7 @@ static uint8_t apic_get_tpr(APICCommonState *s)
return s->tpr >> 4;
}
-static int apic_get_ppr(APICCommonState *s)
+int apic_get_ppr(APICCommonState *s)
{
int tpr, isrv, ppr;
@@ -739,7 +724,7 @@ static uint32_t apic_mem_readl(void *opaque, hwaddr addr)
val = s->divide_conf;
break;
default:
- s->esr |= ESR_ILLEGAL_ADDRESS;
+ s->esr |= APIC_ESR_ILLEGAL_ADDRESS;
val = 0;
break;
}
@@ -852,7 +837,7 @@ static void apic_mem_writel(void *opaque, hwaddr addr, uint32_t val)
}
break;
default:
- s->esr |= ESR_ILLEGAL_ADDRESS;
+ s->esr |= APIC_ESR_ILLEGAL_ADDRESS;
break;
}
}
@@ -889,7 +874,7 @@ static void apic_realize(DeviceState *dev, Error **errp)
s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, apic_timer, s);
local_apics[s->idx] = s;
- msi_supported = true;
+ msi_nonbroken = true;
}
static void apic_class_init(ObjectClass *klass, void *data)
diff --git a/qemu/hw/intc/apic_common.c b/qemu/hw/intc/apic_common.c
index 0032b97c5..4abe145c6 100644
--- a/qemu/hw/intc/apic_common.c
+++ b/qemu/hw/intc/apic_common.c
@@ -17,6 +17,8 @@
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/i386/apic.h"
#include "hw/i386/apic_internal.h"
#include "trace.h"
@@ -296,7 +298,6 @@ static void apic_common_realize(DeviceState *dev, Error **errp)
APICCommonClass *info;
static DeviceState *vapic;
static int apic_no;
- static bool mmio_registered;
if (apic_no >= MAX_APICS) {
error_setg(errp, "%s initialization failed.",
@@ -307,11 +308,6 @@ static void apic_common_realize(DeviceState *dev, Error **errp)
info = APIC_COMMON_GET_CLASS(s);
info->realize(dev, errp);
- if (!mmio_registered) {
- ICCBus *b = ICC_BUS(qdev_get_parent_bus(dev));
- memory_region_add_subregion(b->apic_address_space, 0, &s->io_memory);
- mmio_registered = true;
- }
/* Note: We need at least 1M to map the VAPIC option ROM */
if (!vapic && s->vapic_control & VAPIC_ENABLE_MASK &&
@@ -425,13 +421,12 @@ static Property apic_properties_common[] = {
static void apic_common_class_init(ObjectClass *klass, void *data)
{
- ICCDeviceClass *idc = ICC_DEVICE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
dc->vmsd = &vmstate_apic_common;
dc->reset = apic_reset_common;
dc->props = apic_properties_common;
- idc->realize = apic_common_realize;
+ dc->realize = apic_common_realize;
/*
* Reason: APIC and CPU need to be wired up by
* x86_cpu_apic_create()
@@ -441,7 +436,7 @@ static void apic_common_class_init(ObjectClass *klass, void *data)
static const TypeInfo apic_common_type = {
.name = TYPE_APIC_COMMON,
- .parent = TYPE_ICC_DEVICE,
+ .parent = TYPE_DEVICE,
.instance_size = sizeof(APICCommonState),
.class_size = sizeof(APICCommonClass),
.class_init = apic_common_class_init,
diff --git a/qemu/hw/intc/arm_gic.c b/qemu/hw/intc/arm_gic.c
index 454bfd7df..f55124174 100644
--- a/qemu/hw/intc/arm_gic.c
+++ b/qemu/hw/intc/arm_gic.c
@@ -18,8 +18,10 @@
* armv7m_nvic device.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "gic_internal.h"
+#include "qapi/error.h"
#include "qom/cpu.h"
//#define DEBUG_GIC
@@ -31,11 +33,17 @@ do { fprintf(stderr, "arm_gic: " fmt , ## __VA_ARGS__); } while (0)
#define DPRINTF(fmt, ...) do {} while(0)
#endif
-static const uint8_t gic_id[] = {
- 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1
+static const uint8_t gic_id_11mpcore[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1
};
-#define NUM_CPU(s) ((s)->num_cpu)
+static const uint8_t gic_id_gicv1[] = {
+ 0x04, 0x00, 0x00, 0x00, 0x90, 0xb3, 0x1b, 0x00, 0x0d, 0xf0, 0x05, 0xb1
+};
+
+static const uint8_t gic_id_gicv2[] = {
+ 0x04, 0x00, 0x00, 0x00, 0x90, 0xb4, 0x2b, 0x00, 0x0d, 0xf0, 0x05, 0xb1
+};
static inline int gic_get_current_cpu(GICState *s)
{
@@ -64,7 +72,7 @@ void gic_update(GICState *s)
int cpu;
int cm;
- for (cpu = 0; cpu < NUM_CPU(s); cpu++) {
+ for (cpu = 0; cpu < s->num_cpu; cpu++) {
cm = 1 << cpu;
s->current_pending[cpu] = 1023;
if (!(s->ctlr & (GICD_CTLR_EN_GRP0 | GICD_CTLR_EN_GRP1))
@@ -219,15 +227,99 @@ static uint16_t gic_get_current_pending_irq(GICState *s, int cpu,
return pending_irq;
}
-static void gic_set_running_irq(GICState *s, int cpu, int irq)
+static int gic_get_group_priority(GICState *s, int cpu, int irq)
{
- s->running_irq[cpu] = irq;
- if (irq == 1023) {
- s->running_priority[cpu] = 0x100;
+ /* Return the group priority of the specified interrupt
+ * (which is the top bits of its priority, with the number
+ * of bits masked determined by the applicable binary point register).
+ */
+ int bpr;
+ uint32_t mask;
+
+ if (gic_has_groups(s) &&
+ !(s->cpu_ctlr[cpu] & GICC_CTLR_CBPR) &&
+ GIC_TEST_GROUP(irq, (1 << cpu))) {
+ bpr = s->abpr[cpu];
} else {
- s->running_priority[cpu] = GIC_GET_PRIORITY(irq, cpu);
+ bpr = s->bpr[cpu];
}
- gic_update(s);
+
+ /* a BPR of 0 means the group priority bits are [7:1];
+ * a BPR of 1 means they are [7:2], and so on down to
+ * a BPR of 7 meaning no group priority bits at all.
+ */
+ mask = ~0U << ((bpr & 7) + 1);
+
+ return GIC_GET_PRIORITY(irq, cpu) & mask;
+}
+
+static void gic_activate_irq(GICState *s, int cpu, int irq)
+{
+ /* Set the appropriate Active Priority Register bit for this IRQ,
+ * and update the running priority.
+ */
+ int prio = gic_get_group_priority(s, cpu, irq);
+ int preemption_level = prio >> (GIC_MIN_BPR + 1);
+ int regno = preemption_level / 32;
+ int bitno = preemption_level % 32;
+
+ if (gic_has_groups(s) && GIC_TEST_GROUP(irq, (1 << cpu))) {
+ s->nsapr[regno][cpu] |= (1 << bitno);
+ } else {
+ s->apr[regno][cpu] |= (1 << bitno);
+ }
+
+ s->running_priority[cpu] = prio;
+ GIC_SET_ACTIVE(irq, 1 << cpu);
+}
+
+static int gic_get_prio_from_apr_bits(GICState *s, int cpu)
+{
+ /* Recalculate the current running priority for this CPU based
+ * on the set bits in the Active Priority Registers.
+ */
+ int i;
+ for (i = 0; i < GIC_NR_APRS; i++) {
+ uint32_t apr = s->apr[i][cpu] | s->nsapr[i][cpu];
+ if (!apr) {
+ continue;
+ }
+ return (i * 32 + ctz32(apr)) << (GIC_MIN_BPR + 1);
+ }
+ return 0x100;
+}
+
+static void gic_drop_prio(GICState *s, int cpu, int group)
+{
+ /* Drop the priority of the currently active interrupt in the
+ * specified group.
+ *
+ * Note that we can guarantee (because of the requirement to nest
+ * GICC_IAR reads [which activate an interrupt and raise priority]
+ * with GICC_EOIR writes [which drop the priority for the interrupt])
+ * that the interrupt we're being called for is the highest priority
+ * active interrupt, meaning that it has the lowest set bit in the
+ * APR registers.
+ *
+ * If the guest does not honour the ordering constraints then the
+ * behaviour of the GIC is UNPREDICTABLE, which for us means that
+ * the values of the APR registers might become incorrect and the
+ * running priority will be wrong, so interrupts that should preempt
+ * might not do so, and interrupts that should not preempt might do so.
+ */
+ int i;
+
+ for (i = 0; i < GIC_NR_APRS; i++) {
+ uint32_t *papr = group ? &s->nsapr[i][cpu] : &s->apr[i][cpu];
+ if (!*papr) {
+ continue;
+ }
+ /* Clear lowest set bit */
+ *papr &= *papr - 1;
+ break;
+ }
+
+ s->running_priority[cpu] = gic_get_prio_from_apr_bits(s, cpu);
}
uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs)
@@ -239,7 +331,7 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs)
* for the case where this GIC supports grouping and the pending interrupt
* is in the wrong group.
*/
- irq = gic_get_current_pending_irq(s, cpu, attrs);;
+ irq = gic_get_current_pending_irq(s, cpu, attrs);
if (irq >= GIC_MAXIRQ) {
DPRINTF("ACK, no pending interrupt or it is hidden: %d\n", irq);
@@ -250,7 +342,6 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs)
DPRINTF("ACK, pending interrupt (%d) has insufficient priority\n", irq);
return 1023;
}
- s->last_active[irq][cpu] = s->running_irq[cpu];
if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
/* Clear pending flags for both level and edge triggered interrupts.
@@ -281,7 +372,8 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs)
}
}
- gic_set_running_irq(s, cpu, irq);
+ gic_activate_irq(s, cpu, irq);
+ gic_update(s);
DPRINTF("ACK %d\n", irq);
return ret;
}
@@ -409,10 +501,46 @@ static uint8_t gic_get_running_priority(GICState *s, int cpu, MemTxAttrs attrs)
}
}
+/* Return true if we should split priority drop and interrupt deactivation,
+ * ie whether the relevant EOIMode bit is set.
+ */
+static bool gic_eoi_split(GICState *s, int cpu, MemTxAttrs attrs)
+{
+ if (s->revision != 2) {
+ /* Before GICv2 prio-drop and deactivate are not separable */
+ return false;
+ }
+ if (s->security_extn && !attrs.secure) {
+ return s->cpu_ctlr[cpu] & GICC_CTLR_EOIMODE_NS;
+ }
+ return s->cpu_ctlr[cpu] & GICC_CTLR_EOIMODE;
+}
+
+static void gic_deactivate_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
+{
+ int cm = 1 << cpu;
+ int group = gic_has_groups(s) && GIC_TEST_GROUP(irq, cm);
+
+ if (!gic_eoi_split(s, cpu, attrs)) {
+ /* This is UNPREDICTABLE; we choose to ignore it */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "gic_deactivate_irq: GICC_DIR write when EOIMode clear");
+ return;
+ }
+
+ if (s->security_extn && !attrs.secure && !group) {
+ DPRINTF("Non-secure DI for Group0 interrupt %d ignored\n", irq);
+ return;
+ }
+
+ GIC_CLEAR_ACTIVE(irq, cm);
+}
+
void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
{
- int update = 0;
int cm = 1 << cpu;
+ int group;
+
DPRINTF("EOI %d\n", irq);
if (irq >= s->num_irq) {
/* This handles two cases:
@@ -425,8 +553,9 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
*/
return;
}
- if (s->running_irq[cpu] == 1023)
+ if (s->running_priority[cpu] == 0x100) {
return; /* No active IRQ. */
+ }
if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
/* Mark level triggered interrupts as pending if they are still
@@ -435,11 +564,12 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
&& GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) {
DPRINTF("Set %d pending mask %x\n", irq, cm);
GIC_SET_PENDING(irq, cm);
- update = 1;
}
}
- if (s->security_extn && !attrs.secure && !GIC_TEST_GROUP(irq, cm)) {
+ group = gic_has_groups(s) && GIC_TEST_GROUP(irq, cm);
+
+ if (s->security_extn && !attrs.secure && !group) {
DPRINTF("Non-secure EOI for Group0 interrupt %d ignored\n", irq);
return;
}
@@ -449,23 +579,13 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
* i.e. go ahead and complete the irq anyway.
*/
- if (irq != s->running_irq[cpu]) {
- /* Complete an IRQ that is not currently running. */
- int tmp = s->running_irq[cpu];
- while (s->last_active[tmp][cpu] != 1023) {
- if (s->last_active[tmp][cpu] == irq) {
- s->last_active[tmp][cpu] = s->last_active[irq][cpu];
- break;
- }
- tmp = s->last_active[tmp][cpu];
- }
- if (update) {
- gic_update(s);
- }
- } else {
- /* Complete the current running IRQ. */
- gic_set_running_irq(s, cpu, s->last_active[s->running_irq[cpu]][cpu]);
+ gic_drop_prio(s, cpu, group);
+
+ /* In GICv2 the guest can choose to split priority-drop and deactivate */
+ if (!gic_eoi_split(s, cpu, attrs)) {
+ GIC_CLEAR_ACTIVE(irq, cm);
}
+ gic_update(s);
}
static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs)
@@ -494,7 +614,7 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs)
if (offset == 4)
/* Interrupt Controller Type Register */
return ((s->num_irq / 32) - 1)
- | ((NUM_CPU(s) - 1) << 5)
+ | ((s->num_cpu - 1) << 5)
| (s->security_extn << 10);
if (offset < 0x08)
return 0;
@@ -612,14 +732,31 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs)
}
res = s->sgi_pending[irq][cpu];
- } else if (offset < 0xfe0) {
+ } else if (offset < 0xfd0) {
goto bad_reg;
- } else /* offset >= 0xfe0 */ {
+ } else if (offset < 0x1000) {
if (offset & 3) {
res = 0;
} else {
- res = gic_id[(offset - 0xfe0) >> 2];
+ switch (s->revision) {
+ case REV_11MPCORE:
+ res = gic_id_11mpcore[(offset - 0xfd0) >> 2];
+ break;
+ case 1:
+ res = gic_id_gicv1[(offset - 0xfd0) >> 2];
+ break;
+ case 2:
+ res = gic_id_gicv2[(offset - 0xfd0) >> 2];
+ break;
+ case REV_NVIC:
+ /* Shouldn't be able to get here */
+ abort();
+ default:
+ res = 0;
+ }
}
+ } else {
+ g_assert_not_reached();
}
return res;
bad_reg:
@@ -922,11 +1059,67 @@ static MemTxResult gic_dist_write(void *opaque, hwaddr offset, uint64_t data,
}
}
-static const MemoryRegionOps gic_dist_ops = {
- .read_with_attrs = gic_dist_read,
- .write_with_attrs = gic_dist_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
+static inline uint32_t gic_apr_ns_view(GICState *s, int cpu, int regno)
+{
+ /* Return the Nonsecure view of GICC_APR<regno>. This is the
+ * second half of GICC_NSAPR.
+ */
+ switch (GIC_MIN_BPR) {
+ case 0:
+ if (regno < 2) {
+ return s->nsapr[regno + 2][cpu];
+ }
+ break;
+ case 1:
+ if (regno == 0) {
+ return s->nsapr[regno + 1][cpu];
+ }
+ break;
+ case 2:
+ if (regno == 0) {
+ return extract32(s->nsapr[0][cpu], 16, 16);
+ }
+ break;
+ case 3:
+ if (regno == 0) {
+ return extract32(s->nsapr[0][cpu], 8, 8);
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ return 0;
+}
+
+static inline void gic_apr_write_ns_view(GICState *s, int cpu, int regno,
+ uint32_t value)
+{
+ /* Write the Nonsecure view of GICC_APR<regno>. */
+ switch (GIC_MIN_BPR) {
+ case 0:
+ if (regno < 2) {
+ s->nsapr[regno + 2][cpu] = value;
+ }
+ break;
+ case 1:
+ if (regno == 0) {
+ s->nsapr[regno + 1][cpu] = value;
+ }
+ break;
+ case 2:
+ if (regno == 0) {
+ s->nsapr[0][cpu] = deposit32(s->nsapr[0][cpu], 16, 16, value);
+ }
+ break;
+ case 3:
+ if (regno == 0) {
+ s->nsapr[0][cpu] = deposit32(s->nsapr[0][cpu], 8, 8, value);
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset,
uint64_t *data, MemTxAttrs attrs)
@@ -968,8 +1161,31 @@ static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset,
}
break;
case 0xd0: case 0xd4: case 0xd8: case 0xdc:
- *data = s->apr[(offset - 0xd0) / 4][cpu];
+ {
+ int regno = (offset - 0xd0) / 4;
+
+ if (regno >= GIC_NR_APRS || s->revision != 2) {
+ *data = 0;
+ } else if (s->security_extn && !attrs.secure) {
+ /* NS view of GICC_APR<n> is the top half of GIC_NSAPR<n> */
+ *data = gic_apr_ns_view(s, regno, cpu);
+ } else {
+ *data = s->apr[regno][cpu];
+ }
+ break;
+ }
+ case 0xe0: case 0xe4: case 0xe8: case 0xec:
+ {
+ int regno = (offset - 0xe0) / 4;
+
+ if (regno >= GIC_NR_APRS || s->revision != 2 || !gic_has_groups(s) ||
+ (s->security_extn && !attrs.secure)) {
+ *data = 0;
+ } else {
+ *data = s->nsapr[regno][cpu];
+ }
break;
+ }
default:
qemu_log_mask(LOG_GUEST_ERROR,
"gic_cpu_read: Bad offset %x\n", (int)offset);
@@ -1007,7 +1223,36 @@ static MemTxResult gic_cpu_write(GICState *s, int cpu, int offset,
}
break;
case 0xd0: case 0xd4: case 0xd8: case 0xdc:
- qemu_log_mask(LOG_UNIMP, "Writing APR not implemented\n");
+ {
+ int regno = (offset - 0xd0) / 4;
+
+ if (regno >= GIC_NR_APRS || s->revision != 2) {
+ return MEMTX_OK;
+ }
+ if (s->security_extn && !attrs.secure) {
+ /* NS view of GICC_APR<n> is the top half of GIC_NSAPR<n> */
+ gic_apr_write_ns_view(s, regno, cpu, value);
+ } else {
+ s->apr[regno][cpu] = value;
+ }
+ break;
+ }
+ case 0xe0: case 0xe4: case 0xe8: case 0xec:
+ {
+ int regno = (offset - 0xe0) / 4;
+
+ if (regno >= GIC_NR_APRS || s->revision != 2) {
+ return MEMTX_OK;
+ }
+ if (!gic_has_groups(s) || (s->security_extn && !attrs.secure)) {
+ return MEMTX_OK;
+ }
+ s->nsapr[regno][cpu] = value;
+ break;
+ }
+ case 0x1000:
+ /* GICC_DIR */
+ gic_deactivate_irq(s, cpu, value & 0x3ff, attrs);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
@@ -1056,10 +1301,17 @@ static MemTxResult gic_do_cpu_write(void *opaque, hwaddr addr,
return gic_cpu_write(s, id, addr, value, attrs);
}
-static const MemoryRegionOps gic_thiscpu_ops = {
- .read_with_attrs = gic_thiscpu_read,
- .write_with_attrs = gic_thiscpu_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
+static const MemoryRegionOps gic_ops[2] = {
+ {
+ .read_with_attrs = gic_dist_read,
+ .write_with_attrs = gic_dist_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ },
+ {
+ .read_with_attrs = gic_thiscpu_read,
+ .write_with_attrs = gic_thiscpu_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ }
};
static const MemoryRegionOps gic_cpu_ops = {
@@ -1068,31 +1320,10 @@ static const MemoryRegionOps gic_cpu_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
+/* This function is used by nvic model */
void gic_init_irqs_and_distributor(GICState *s)
{
- SysBusDevice *sbd = SYS_BUS_DEVICE(s);
- int i;
-
- i = s->num_irq - GIC_INTERNAL;
- /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU.
- * GPIO array layout is thus:
- * [0..N-1] SPIs
- * [N..N+31] PPIs for CPU 0
- * [N+32..N+63] PPIs for CPU 1
- * ...
- */
- if (s->revision != REV_NVIC) {
- i += (GIC_INTERNAL * s->num_cpu);
- }
- qdev_init_gpio_in(DEVICE(s), gic_set_irq, i);
- for (i = 0; i < NUM_CPU(s); i++) {
- sysbus_init_irq(sbd, &s->parent_irq[i]);
- }
- for (i = 0; i < NUM_CPU(s); i++) {
- sysbus_init_irq(sbd, &s->parent_fiq[i]);
- }
- memory_region_init_io(&s->iomem, OBJECT(s), &gic_dist_ops, s,
- "gic_dist", 0x1000);
+ gic_init_irqs_and_mmio(s, gic_set_irq, gic_ops);
}
static void arm_gic_realize(DeviceState *dev, Error **errp)
@@ -1110,28 +1341,22 @@ static void arm_gic_realize(DeviceState *dev, Error **errp)
return;
}
- gic_init_irqs_and_distributor(s);
+ /* This creates distributor and main CPU interface (s->cpuiomem[0]) */
+ gic_init_irqs_and_mmio(s, gic_set_irq, gic_ops);
- /* Memory regions for the CPU interfaces (NVIC doesn't have these):
- * a region for "CPU interface for this core", then a region for
- * "CPU interface for core 0", "for core 1", ...
+ /* Extra core-specific regions for the CPU interfaces. This is
+ * necessary for "franken-GIC" implementations, for example on
+ * Exynos 4.
* NB that the memory region size of 0x100 applies for the 11MPCore
* and also cores following the GIC v1 spec (ie A9).
* GIC v2 defines a larger memory region (0x1000) so this will need
* to be extended when we implement A15.
*/
- memory_region_init_io(&s->cpuiomem[0], OBJECT(s), &gic_thiscpu_ops, s,
- "gic_cpu", 0x100);
- for (i = 0; i < NUM_CPU(s); i++) {
+ for (i = 0; i < s->num_cpu; i++) {
s->backref[i] = s;
memory_region_init_io(&s->cpuiomem[i+1], OBJECT(s), &gic_cpu_ops,
&s->backref[i], "gic_cpu", 0x100);
- }
- /* Distributor */
- sysbus_init_mmio(sbd, &s->iomem);
- /* cpu interfaces (one for "current cpu" plus one per cpu) */
- for (i = 0; i <= NUM_CPU(s); i++) {
- sysbus_init_mmio(sbd, &s->cpuiomem[i]);
+ sysbus_init_mmio(sbd, &s->cpuiomem[i+1]);
}
}
diff --git a/qemu/hw/intc/arm_gic_common.c b/qemu/hw/intc/arm_gic_common.c
index a64d0714e..0a1f56af1 100644
--- a/qemu/hw/intc/arm_gic_common.c
+++ b/qemu/hw/intc/arm_gic_common.c
@@ -18,7 +18,10 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "gic_internal.h"
+#include "hw/arm/linux-boot-if.h"
static void gic_pre_save(void *opaque)
{
@@ -59,8 +62,8 @@ static const VMStateDescription vmstate_gic_irq_state = {
static const VMStateDescription vmstate_gic = {
.name = "arm_gic",
- .version_id = 10,
- .minimum_version_id = 10,
+ .version_id = 12,
+ .minimum_version_id = 12,
.pre_save = gic_pre_save,
.post_load = gic_post_load,
.fields = (VMStateField[]) {
@@ -71,19 +74,59 @@ static const VMStateDescription vmstate_gic = {
VMSTATE_UINT8_ARRAY(irq_target, GICState, GIC_MAXIRQ),
VMSTATE_UINT8_2DARRAY(priority1, GICState, GIC_INTERNAL, GIC_NCPU),
VMSTATE_UINT8_ARRAY(priority2, GICState, GIC_MAXIRQ - GIC_INTERNAL),
- VMSTATE_UINT16_2DARRAY(last_active, GICState, GIC_MAXIRQ, GIC_NCPU),
VMSTATE_UINT8_2DARRAY(sgi_pending, GICState, GIC_NR_SGIS, GIC_NCPU),
VMSTATE_UINT16_ARRAY(priority_mask, GICState, GIC_NCPU),
- VMSTATE_UINT16_ARRAY(running_irq, GICState, GIC_NCPU),
VMSTATE_UINT16_ARRAY(running_priority, GICState, GIC_NCPU),
VMSTATE_UINT16_ARRAY(current_pending, GICState, GIC_NCPU),
VMSTATE_UINT8_ARRAY(bpr, GICState, GIC_NCPU),
VMSTATE_UINT8_ARRAY(abpr, GICState, GIC_NCPU),
VMSTATE_UINT32_2DARRAY(apr, GICState, GIC_NR_APRS, GIC_NCPU),
+ VMSTATE_UINT32_2DARRAY(nsapr, GICState, GIC_NR_APRS, GIC_NCPU),
VMSTATE_END_OF_LIST()
}
};
+void gic_init_irqs_and_mmio(GICState *s, qemu_irq_handler handler,
+ const MemoryRegionOps *ops)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(s);
+ int i = s->num_irq - GIC_INTERNAL;
+
+ /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU.
+ * GPIO array layout is thus:
+ * [0..N-1] SPIs
+ * [N..N+31] PPIs for CPU 0
+ * [N+32..N+63] PPIs for CPU 1
+ * ...
+ */
+ if (s->revision != REV_NVIC) {
+ i += (GIC_INTERNAL * s->num_cpu);
+ }
+ qdev_init_gpio_in(DEVICE(s), handler, i);
+
+ for (i = 0; i < s->num_cpu; i++) {
+ sysbus_init_irq(sbd, &s->parent_irq[i]);
+ }
+ for (i = 0; i < s->num_cpu; i++) {
+ sysbus_init_irq(sbd, &s->parent_fiq[i]);
+ }
+
+ /* Distributor */
+ memory_region_init_io(&s->iomem, OBJECT(s), ops, s, "gic_dist", 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+
+ if (s->revision != REV_NVIC) {
+ /* This is the main CPU interface "for this core". It is always
+ * present because it is required by both software emulation and KVM.
+ * NVIC is not handled here because its CPU interface is different,
+ * neither it can use KVM.
+ */
+ memory_region_init_io(&s->cpuiomem[0], OBJECT(s), ops ? &ops[1] : NULL,
+ s, "gic_cpu", s->revision == 2 ? 0x2000 : 0x100);
+ sysbus_init_mmio(sbd, &s->cpuiomem[0]);
+ }
+}
+
static void arm_gic_common_realize(DeviceState *dev, Error **errp)
{
GICState *s = ARM_GIC_COMMON(dev);
@@ -124,21 +167,35 @@ static void arm_gic_common_reset(DeviceState *dev)
{
GICState *s = ARM_GIC_COMMON(dev);
int i, j;
+ int resetprio;
+
+ /* If we're resetting a TZ-aware GIC as if secure firmware
+ * had set it up ready to start a kernel in non-secure,
+ * we need to set interrupt priorities to a "zero for the
+ * NS view" value. This is particularly critical for the
+ * priority_mask[] values, because if they are zero then NS
+ * code cannot ever rewrite the priority to anything else.
+ */
+ if (s->security_extn && s->irq_reset_nonsecure) {
+ resetprio = 0x80;
+ } else {
+ resetprio = 0;
+ }
+
memset(s->irq_state, 0, GIC_MAXIRQ * sizeof(gic_irq_state));
for (i = 0 ; i < s->num_cpu; i++) {
if (s->revision == REV_11MPCORE) {
s->priority_mask[i] = 0xf0;
} else {
- s->priority_mask[i] = 0;
+ s->priority_mask[i] = resetprio;
}
s->current_pending[i] = 1023;
- s->running_irq[i] = 1023;
s->running_priority[i] = 0x100;
s->cpu_ctlr[i] = 0;
s->bpr[i] = GIC_MIN_BPR;
s->abpr[i] = GIC_MIN_ABPR;
for (j = 0; j < GIC_INTERNAL; j++) {
- s->priority1[j][i] = 0;
+ s->priority1[j][i] = resetprio;
}
for (j = 0; j < GIC_NR_SGIS; j++) {
s->sgi_pending[j][i] = 0;
@@ -150,7 +207,7 @@ static void arm_gic_common_reset(DeviceState *dev)
}
for (i = 0; i < ARRAY_SIZE(s->priority2); i++) {
- s->priority2[i] = 0;
+ s->priority2[i] = resetprio;
}
for (i = 0; i < GIC_MAXIRQ; i++) {
@@ -161,9 +218,32 @@ static void arm_gic_common_reset(DeviceState *dev)
s->irq_target[i] = 0;
}
}
+ if (s->security_extn && s->irq_reset_nonsecure) {
+ for (i = 0; i < GIC_MAXIRQ; i++) {
+ GIC_SET_GROUP(i, ALL_CPU_MASK);
+ }
+ }
+
s->ctlr = 0;
}
+static void arm_gic_common_linux_init(ARMLinuxBootIf *obj,
+ bool secure_boot)
+{
+ GICState *s = ARM_GIC_COMMON(obj);
+
+ if (s->security_extn && !secure_boot) {
+ /* We're directly booting a kernel into NonSecure. If this GIC
+ * implements the security extensions then we must configure it
+ * to have all the interrupts be NonSecure (this is a job that
+ * is done by the Secure boot firmware in real hardware, and in
+ * this mode QEMU is acting as a minimalist firmware-and-bootloader
+ * equivalent).
+ */
+ s->irq_reset_nonsecure = true;
+ }
+}
+
static Property arm_gic_common_properties[] = {
DEFINE_PROP_UINT32("num-cpu", GICState, num_cpu, 1),
DEFINE_PROP_UINT32("num-irq", GICState, num_irq, 32),
@@ -180,11 +260,13 @@ static Property arm_gic_common_properties[] = {
static void arm_gic_common_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
+ ARMLinuxBootIfClass *albifc = ARM_LINUX_BOOT_IF_CLASS(klass);
dc->reset = arm_gic_common_reset;
dc->realize = arm_gic_common_realize;
dc->props = arm_gic_common_properties;
dc->vmsd = &vmstate_gic;
+ albifc->arm_linux_init = arm_gic_common_linux_init;
}
static const TypeInfo arm_gic_common_type = {
@@ -194,6 +276,10 @@ static const TypeInfo arm_gic_common_type = {
.class_size = sizeof(ARMGICCommonClass),
.class_init = arm_gic_common_class_init,
.abstract = true,
+ .interfaces = (InterfaceInfo []) {
+ { TYPE_ARM_LINUX_BOOT_IF },
+ { },
+ },
};
static void register_types(void)
diff --git a/qemu/hw/intc/arm_gic_kvm.c b/qemu/hw/intc/arm_gic_kvm.c
index f56bff1af..bc85ab769 100644
--- a/qemu/hw/intc/arm_gic_kvm.c
+++ b/qemu/hw/intc/arm_gic_kvm.c
@@ -19,10 +19,14 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/sysbus.h"
+#include "migration/migration.h"
#include "sysemu/kvm.h"
#include "kvm_arm.h"
#include "gic_internal.h"
+#include "vgic_common.h"
//#define DEBUG_GIC_KVM
@@ -52,7 +56,7 @@ typedef struct KVMARMGICClass {
void (*parent_reset)(DeviceState *dev);
} KVMARMGICClass;
-static void kvm_arm_gic_set_irq(void *opaque, int irq, int level)
+void kvm_arm_gic_set_irq(uint32_t num_irq, int irq, int level)
{
/* Meaning of the 'irq' parameter:
* [0..N-1] : external interrupts
@@ -63,10 +67,9 @@ static void kvm_arm_gic_set_irq(void *opaque, int irq, int level)
* has separate fields in the irq number for type,
* CPU number and interrupt number.
*/
- GICState *s = (GICState *)opaque;
int kvm_irq, irqtype, cpu;
- if (irq < (s->num_irq - GIC_INTERNAL)) {
+ if (irq < (num_irq - GIC_INTERNAL)) {
/* External interrupt. The kernel numbers these like the GIC
* hardware, with external interrupt IDs starting after the
* internal ones.
@@ -77,7 +80,7 @@ static void kvm_arm_gic_set_irq(void *opaque, int irq, int level)
} else {
/* Internal interrupt: decode into (cpu, interrupt id) */
irqtype = KVM_ARM_IRQ_TYPE_PPI;
- irq -= (s->num_irq - GIC_INTERNAL);
+ irq -= (num_irq - GIC_INTERNAL);
cpu = irq / GIC_INTERNAL;
irq %= GIC_INTERNAL;
}
@@ -87,69 +90,36 @@ static void kvm_arm_gic_set_irq(void *opaque, int irq, int level)
kvm_set_irq(kvm_state, kvm_irq, !!level);
}
-static bool kvm_arm_gic_can_save_restore(GICState *s)
-{
- return s->dev_fd >= 0;
-}
-
-static bool kvm_gic_supports_attr(GICState *s, int group, int attrnum)
+static void kvm_arm_gicv2_set_irq(void *opaque, int irq, int level)
{
- struct kvm_device_attr attr = {
- .group = group,
- .attr = attrnum,
- .flags = 0,
- };
-
- if (s->dev_fd == -1) {
- return false;
- }
+ GICState *s = (GICState *)opaque;
- return kvm_device_ioctl(s->dev_fd, KVM_HAS_DEVICE_ATTR, &attr) == 0;
+ kvm_arm_gic_set_irq(s->num_irq, irq, level);
}
-static void kvm_gic_access(GICState *s, int group, int offset,
- int cpu, uint32_t *val, bool write)
+static bool kvm_arm_gic_can_save_restore(GICState *s)
{
- struct kvm_device_attr attr;
- int type;
- int err;
-
- cpu = cpu & 0xff;
-
- attr.flags = 0;
- attr.group = group;
- attr.attr = (((uint64_t)cpu << KVM_DEV_ARM_VGIC_CPUID_SHIFT) &
- KVM_DEV_ARM_VGIC_CPUID_MASK) |
- (((uint64_t)offset << KVM_DEV_ARM_VGIC_OFFSET_SHIFT) &
- KVM_DEV_ARM_VGIC_OFFSET_MASK);
- attr.addr = (uintptr_t)val;
-
- if (write) {
- type = KVM_SET_DEVICE_ATTR;
- } else {
- type = KVM_GET_DEVICE_ATTR;
- }
-
- err = kvm_device_ioctl(s->dev_fd, type, &attr);
- if (err < 0) {
- fprintf(stderr, "KVM_{SET/GET}_DEVICE_ATTR failed: %s\n",
- strerror(-err));
- abort();
- }
+ return s->dev_fd >= 0;
}
+#define KVM_VGIC_ATTR(offset, cpu) \
+ ((((uint64_t)(cpu) << KVM_DEV_ARM_VGIC_CPUID_SHIFT) & \
+ KVM_DEV_ARM_VGIC_CPUID_MASK) | \
+ (((uint64_t)(offset) << KVM_DEV_ARM_VGIC_OFFSET_SHIFT) & \
+ KVM_DEV_ARM_VGIC_OFFSET_MASK))
+
static void kvm_gicd_access(GICState *s, int offset, int cpu,
uint32_t *val, bool write)
{
- kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
- offset, cpu, val, write);
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
+ KVM_VGIC_ATTR(offset, cpu), val, write);
}
static void kvm_gicc_access(GICState *s, int offset, int cpu,
uint32_t *val, bool write)
{
- kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_CPU_REGS,
- offset, cpu, val, write);
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CPU_REGS,
+ KVM_VGIC_ATTR(offset, cpu), val, write);
}
#define for_each_irq_reg(_ctr, _max_irq, _field_width) \
@@ -340,11 +310,6 @@ static void kvm_arm_gic_put(GICState *s)
int num_cpu;
int num_irq;
- if (!kvm_arm_gic_can_save_restore(s)) {
- DPRINTF("Cannot put kernel gic state, no kernel interface");
- return;
- }
-
/* Note: We do the restore in a slightly different order than the save
* (where the order doesn't matter and is simply ordered according to the
* register offset values */
@@ -444,11 +409,6 @@ static void kvm_arm_gic_get(GICState *s)
int i;
int cpu;
- if (!kvm_arm_gic_can_save_restore(s)) {
- DPRINTF("Cannot get kernel gic state, no kernel interface");
- return;
- }
-
/*****************************************************************
* Distributor State
*/
@@ -536,14 +496,16 @@ static void kvm_arm_gic_reset(DeviceState *dev)
KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s);
kgc->parent_reset(dev);
- kvm_arm_gic_put(s);
+
+ if (kvm_arm_gic_can_save_restore(s)) {
+ kvm_arm_gic_put(s);
+ }
}
static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
{
int i;
GICState *s = KVM_ARM_GIC(dev);
- SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s);
Error *local_err = NULL;
int ret;
@@ -560,58 +522,37 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
return;
}
- i = s->num_irq - GIC_INTERNAL;
- /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU.
- * GPIO array layout is thus:
- * [0..N-1] SPIs
- * [N..N+31] PPIs for CPU 0
- * [N+32..N+63] PPIs for CPU 1
- * ...
- */
- i += (GIC_INTERNAL * s->num_cpu);
- qdev_init_gpio_in(dev, kvm_arm_gic_set_irq, i);
+ gic_init_irqs_and_mmio(s, kvm_arm_gicv2_set_irq, NULL);
for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) {
qemu_irq irq = qdev_get_gpio_in(dev, i);
kvm_irqchip_set_qemuirq_gsi(kvm_state, irq, i);
}
- /* We never use our outbound IRQ/FIQ lines but provide them so that
- * we maintain the same interface as the non-KVM GIC.
- */
- for (i = 0; i < s->num_cpu; i++) {
- sysbus_init_irq(sbd, &s->parent_irq[i]);
- }
- for (i = 0; i < s->num_cpu; i++) {
- sysbus_init_irq(sbd, &s->parent_fiq[i]);
- }
-
/* Try to create the device via the device control API */
s->dev_fd = -1;
ret = kvm_create_device(kvm_state, KVM_DEV_TYPE_ARM_VGIC_V2, false);
if (ret >= 0) {
s->dev_fd = ret;
+
+ /* Newstyle API is used, we may have attributes */
+ if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0)) {
+ uint32_t numirqs = s->num_irq;
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0,
+ &numirqs, true);
+ }
+ /* Tell the kernel to complete VGIC initialization now */
+ if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
+ KVM_DEV_ARM_VGIC_CTRL_INIT)) {
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
+ KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true);
+ }
} else if (ret != -ENODEV && ret != -ENOTSUP) {
error_setg_errno(errp, -ret, "error creating in-kernel VGIC");
return;
}
- if (kvm_gic_supports_attr(s, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0)) {
- uint32_t numirqs = s->num_irq;
- kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0, 0, &numirqs, 1);
- }
-
- /* Tell the kernel to complete VGIC initialization now */
- if (kvm_gic_supports_attr(s, KVM_DEV_ARM_VGIC_GRP_CTRL,
- KVM_DEV_ARM_VGIC_CTRL_INIT)) {
- kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_CTRL,
- KVM_DEV_ARM_VGIC_CTRL_INIT, 0, 0, 1);
- }
-
/* Distributor */
- memory_region_init_reservation(&s->iomem, OBJECT(s),
- "kvm-gic_dist", 0x1000);
- sysbus_init_mmio(sbd, &s->iomem);
kvm_arm_register_device(&s->iomem,
(KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT)
| KVM_VGIC_V2_ADDR_TYPE_DIST,
@@ -622,15 +563,18 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
* provide the "interface for core #N" memory regions, because
* cores with a VGIC don't have those.
*/
- memory_region_init_reservation(&s->cpuiomem[0], OBJECT(s),
- "kvm-gic_cpu", 0x1000);
- sysbus_init_mmio(sbd, &s->cpuiomem[0]);
kvm_arm_register_device(&s->cpuiomem[0],
(KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT)
| KVM_VGIC_V2_ADDR_TYPE_CPU,
KVM_DEV_ARM_VGIC_GRP_ADDR,
KVM_VGIC_V2_ADDR_TYPE_CPU,
s->dev_fd);
+
+ if (!kvm_arm_gic_can_save_restore(s)) {
+ error_setg(&s->migration_blocker, "This operating system kernel does "
+ "not support vGICv2 migration");
+ migrate_add_blocker(s->migration_blocker);
+ }
}
static void kvm_arm_gic_class_init(ObjectClass *klass, void *data)
diff --git a/qemu/hw/intc/arm_gicv2m.c b/qemu/hw/intc/arm_gicv2m.c
index 43d1976c4..e8b5177dc 100644
--- a/qemu/hw/intc/arm_gicv2m.c
+++ b/qemu/hw/intc/arm_gicv2m.c
@@ -25,6 +25,8 @@
* identification registers and with a single non-secure MSI register frame.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/sysbus.h"
#include "hw/pci/msi.h"
@@ -147,7 +149,7 @@ static void gicv2m_realize(DeviceState *dev, Error **errp)
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->spi[i]);
}
- msi_supported = true;
+ msi_nonbroken = true;
kvm_gsi_direct_mapping = true;
kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled();
}
diff --git a/qemu/hw/intc/arm_gicv3_common.c b/qemu/hw/intc/arm_gicv3_common.c
new file mode 100644
index 000000000..b9d3824f2
--- /dev/null
+++ b/qemu/hw/intc/arm_gicv3_common.c
@@ -0,0 +1,142 @@
+/*
+ * ARM GICv3 support - common bits of emulated and KVM kernel model
+ *
+ * Copyright (c) 2012 Linaro Limited
+ * Copyright (c) 2015 Huawei.
+ * Written by Peter Maydell
+ * Extended to 64 cores by Shlomo Pongratz
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/intc/arm_gicv3_common.h"
+
+static void gicv3_pre_save(void *opaque)
+{
+ GICv3State *s = (GICv3State *)opaque;
+ ARMGICv3CommonClass *c = ARM_GICV3_COMMON_GET_CLASS(s);
+
+ if (c->pre_save) {
+ c->pre_save(s);
+ }
+}
+
+static int gicv3_post_load(void *opaque, int version_id)
+{
+ GICv3State *s = (GICv3State *)opaque;
+ ARMGICv3CommonClass *c = ARM_GICV3_COMMON_GET_CLASS(s);
+
+ if (c->post_load) {
+ c->post_load(s);
+ }
+ return 0;
+}
+
+static const VMStateDescription vmstate_gicv3 = {
+ .name = "arm_gicv3",
+ .unmigratable = 1,
+ .pre_save = gicv3_pre_save,
+ .post_load = gicv3_post_load,
+};
+
+void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler,
+ const MemoryRegionOps *ops)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(s);
+ int i;
+
+ /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU.
+ * GPIO array layout is thus:
+ * [0..N-1] spi
+ * [N..N+31] PPIs for CPU 0
+ * [N+32..N+63] PPIs for CPU 1
+ * ...
+ */
+ i = s->num_irq - GIC_INTERNAL + GIC_INTERNAL * s->num_cpu;
+ qdev_init_gpio_in(DEVICE(s), handler, i);
+
+ s->parent_irq = g_malloc(s->num_cpu * sizeof(qemu_irq));
+ s->parent_fiq = g_malloc(s->num_cpu * sizeof(qemu_irq));
+
+ for (i = 0; i < s->num_cpu; i++) {
+ sysbus_init_irq(sbd, &s->parent_irq[i]);
+ }
+ for (i = 0; i < s->num_cpu; i++) {
+ sysbus_init_irq(sbd, &s->parent_fiq[i]);
+ }
+
+ memory_region_init_io(&s->iomem_dist, OBJECT(s), ops, s,
+ "gicv3_dist", 0x10000);
+ memory_region_init_io(&s->iomem_redist, OBJECT(s), ops ? &ops[1] : NULL, s,
+ "gicv3_redist", 0x20000 * s->num_cpu);
+
+ sysbus_init_mmio(sbd, &s->iomem_dist);
+ sysbus_init_mmio(sbd, &s->iomem_redist);
+}
+
+static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
+{
+ GICv3State *s = ARM_GICV3_COMMON(dev);
+
+ /* revision property is actually reserved and currently used only in order
+ * to keep the interface compatible with GICv2 code, avoiding extra
+ * conditions. However, in future it could be used, for example, if we
+ * implement GICv4.
+ */
+ if (s->revision != 3) {
+ error_setg(errp, "unsupported GIC revision %d", s->revision);
+ return;
+ }
+}
+
+static void arm_gicv3_common_reset(DeviceState *dev)
+{
+ /* TODO */
+}
+
+static Property arm_gicv3_common_properties[] = {
+ DEFINE_PROP_UINT32("num-cpu", GICv3State, num_cpu, 1),
+ DEFINE_PROP_UINT32("num-irq", GICv3State, num_irq, 32),
+ DEFINE_PROP_UINT32("revision", GICv3State, revision, 3),
+ DEFINE_PROP_BOOL("has-security-extensions", GICv3State, security_extn, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void arm_gicv3_common_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = arm_gicv3_common_reset;
+ dc->realize = arm_gicv3_common_realize;
+ dc->props = arm_gicv3_common_properties;
+ dc->vmsd = &vmstate_gicv3;
+}
+
+static const TypeInfo arm_gicv3_common_type = {
+ .name = TYPE_ARM_GICV3_COMMON,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(GICv3State),
+ .class_size = sizeof(ARMGICv3CommonClass),
+ .class_init = arm_gicv3_common_class_init,
+ .abstract = true,
+};
+
+static void register_types(void)
+{
+ type_register_static(&arm_gicv3_common_type);
+}
+
+type_init(register_types)
diff --git a/qemu/hw/intc/arm_gicv3_kvm.c b/qemu/hw/intc/arm_gicv3_kvm.c
new file mode 100644
index 000000000..acc173004
--- /dev/null
+++ b/qemu/hw/intc/arm_gicv3_kvm.c
@@ -0,0 +1,151 @@
+/*
+ * ARM Generic Interrupt Controller using KVM in-kernel support
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ * Written by Pavel Fedin
+ * Based on vGICv2 code by Peter Maydell
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/intc/arm_gicv3_common.h"
+#include "hw/sysbus.h"
+#include "sysemu/kvm.h"
+#include "kvm_arm.h"
+#include "vgic_common.h"
+
+#ifdef DEBUG_GICV3_KVM
+#define DPRINTF(fmt, ...) \
+ do { fprintf(stderr, "kvm_gicv3: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+ do { } while (0)
+#endif
+
+#define TYPE_KVM_ARM_GICV3 "kvm-arm-gicv3"
+#define KVM_ARM_GICV3(obj) \
+ OBJECT_CHECK(GICv3State, (obj), TYPE_KVM_ARM_GICV3)
+#define KVM_ARM_GICV3_CLASS(klass) \
+ OBJECT_CLASS_CHECK(KVMARMGICv3Class, (klass), TYPE_KVM_ARM_GICV3)
+#define KVM_ARM_GICV3_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(KVMARMGICv3Class, (obj), TYPE_KVM_ARM_GICV3)
+
+typedef struct KVMARMGICv3Class {
+ ARMGICv3CommonClass parent_class;
+ DeviceRealize parent_realize;
+ void (*parent_reset)(DeviceState *dev);
+} KVMARMGICv3Class;
+
+static void kvm_arm_gicv3_set_irq(void *opaque, int irq, int level)
+{
+ GICv3State *s = (GICv3State *)opaque;
+
+ kvm_arm_gic_set_irq(s->num_irq, irq, level);
+}
+
+static void kvm_arm_gicv3_put(GICv3State *s)
+{
+ /* TODO */
+ DPRINTF("Cannot put kernel gic state, no kernel interface\n");
+}
+
+static void kvm_arm_gicv3_get(GICv3State *s)
+{
+ /* TODO */
+ DPRINTF("Cannot get kernel gic state, no kernel interface\n");
+}
+
+static void kvm_arm_gicv3_reset(DeviceState *dev)
+{
+ GICv3State *s = ARM_GICV3_COMMON(dev);
+ KVMARMGICv3Class *kgc = KVM_ARM_GICV3_GET_CLASS(s);
+
+ DPRINTF("Reset\n");
+
+ kgc->parent_reset(dev);
+ kvm_arm_gicv3_put(s);
+}
+
+static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp)
+{
+ GICv3State *s = KVM_ARM_GICV3(dev);
+ KVMARMGICv3Class *kgc = KVM_ARM_GICV3_GET_CLASS(s);
+ Error *local_err = NULL;
+
+ DPRINTF("kvm_arm_gicv3_realize\n");
+
+ kgc->parent_realize(dev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (s->security_extn) {
+ error_setg(errp, "the in-kernel VGICv3 does not implement the "
+ "security extensions");
+ return;
+ }
+
+ gicv3_init_irqs_and_mmio(s, kvm_arm_gicv3_set_irq, NULL);
+
+ /* Try to create the device via the device control API */
+ s->dev_fd = kvm_create_device(kvm_state, KVM_DEV_TYPE_ARM_VGIC_V3, false);
+ if (s->dev_fd < 0) {
+ error_setg_errno(errp, -s->dev_fd, "error creating in-kernel VGIC");
+ return;
+ }
+
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
+ 0, &s->num_irq, true);
+
+ /* Tell the kernel to complete VGIC initialization now */
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
+ KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true);
+
+ kvm_arm_register_device(&s->iomem_dist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR,
+ KVM_VGIC_V3_ADDR_TYPE_DIST, s->dev_fd);
+ kvm_arm_register_device(&s->iomem_redist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR,
+ KVM_VGIC_V3_ADDR_TYPE_REDIST, s->dev_fd);
+}
+
+static void kvm_arm_gicv3_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ARMGICv3CommonClass *agcc = ARM_GICV3_COMMON_CLASS(klass);
+ KVMARMGICv3Class *kgc = KVM_ARM_GICV3_CLASS(klass);
+
+ agcc->pre_save = kvm_arm_gicv3_get;
+ agcc->post_load = kvm_arm_gicv3_put;
+ kgc->parent_realize = dc->realize;
+ kgc->parent_reset = dc->reset;
+ dc->realize = kvm_arm_gicv3_realize;
+ dc->reset = kvm_arm_gicv3_reset;
+}
+
+static const TypeInfo kvm_arm_gicv3_info = {
+ .name = TYPE_KVM_ARM_GICV3,
+ .parent = TYPE_ARM_GICV3_COMMON,
+ .instance_size = sizeof(GICv3State),
+ .class_init = kvm_arm_gicv3_class_init,
+ .class_size = sizeof(KVMARMGICv3Class),
+};
+
+static void kvm_arm_gicv3_register_types(void)
+{
+ type_register_static(&kvm_arm_gicv3_info);
+}
+
+type_init(kvm_arm_gicv3_register_types)
diff --git a/qemu/hw/intc/armv7m_nvic.c b/qemu/hw/intc/armv7m_nvic.c
index e13b729e1..669e82adf 100644
--- a/qemu/hw/intc/armv7m_nvic.c
+++ b/qemu/hw/intc/armv7m_nvic.c
@@ -10,6 +10,9 @@
* NVIC. Much of that is also implemented here.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
#include "hw/sysbus.h"
#include "qemu/timer.h"
#include "hw/arm/arm.h"
@@ -28,6 +31,7 @@ typedef struct {
MemoryRegion gic_iomem_alias;
MemoryRegion container;
uint32_t num_irq;
+ qemu_irq sysresetreq;
} nvic_state;
#define TYPE_NVIC "armv7m_nvic"
@@ -185,26 +189,25 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset)
return cpu->midr;
case 0xd04: /* Interrupt Control State. */
/* VECTACTIVE */
- val = s->gic.running_irq[0];
+ cpu = ARM_CPU(current_cpu);
+ val = cpu->env.v7m.exception;
if (val == 1023) {
val = 0;
} else if (val >= 32) {
val -= 16;
}
- /* RETTOBASE */
- if (s->gic.running_irq[0] == 1023
- || s->gic.last_active[s->gic.running_irq[0]][0] == 1023) {
- val |= (1 << 11);
- }
/* VECTPENDING */
if (s->gic.current_pending[0] != 1023)
val |= (s->gic.current_pending[0] << 12);
- /* ISRPENDING */
+ /* ISRPENDING and RETTOBASE */
for (irq = 32; irq < s->num_irq; irq++) {
if (s->gic.irq_state[irq].pending) {
val |= (1 << 22);
break;
}
+ if (irq != cpu->env.v7m.exception && s->gic.irq_state[irq].active) {
+ val |= (1 << 11);
+ }
}
/* PENDSTSET */
if (s->gic.irq_state[ARMV7M_EXCP_SYSTICK].pending)
@@ -349,10 +352,13 @@ static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value)
break;
case 0xd0c: /* Application Interrupt/Reset Control. */
if ((value >> 16) == 0x05fa) {
+ if (value & 4) {
+ qemu_irq_pulse(s->sysresetreq);
+ }
if (value & 2) {
qemu_log_mask(LOG_UNIMP, "VECTCLRACTIVE unimplemented\n");
}
- if (value & 5) {
+ if (value & 1) {
qemu_log_mask(LOG_UNIMP, "AIRCR system reset unimplemented\n");
}
if (value & 0x700) {
@@ -536,11 +542,14 @@ static void armv7m_nvic_instance_init(Object *obj)
* value in the GICState struct.
*/
GICState *s = ARM_GIC_COMMON(obj);
+ DeviceState *dev = DEVICE(obj);
+ nvic_state *nvic = NVIC(obj);
/* The ARM v7m may have anything from 0 to 496 external interrupt
* IRQ lines. We default to 64. Other boards may differ and should
* set the num-irq property appropriately.
*/
s->num_irq = 64;
+ qdev_init_gpio_out_named(dev, &nvic->sysresetreq, "SYSRESETREQ", 1);
}
static void armv7m_nvic_class_init(ObjectClass *klass, void *data)
diff --git a/qemu/hw/intc/aspeed_vic.c b/qemu/hw/intc/aspeed_vic.c
new file mode 100644
index 000000000..19a0ff748
--- /dev/null
+++ b/qemu/hw/intc/aspeed_vic.c
@@ -0,0 +1,339 @@
+/*
+ * ASPEED Interrupt Controller (New)
+ *
+ * Andrew Jeffery <andrew@aj.id.au>
+ *
+ * Copyright 2015, 2016 IBM Corp.
+ *
+ * This code is licensed under the GPL version 2 or later. See
+ * the COPYING file in the top-level directory.
+ */
+
+/* The hardware exposes two register sets, a legacy set and a 'new' set. The
+ * model implements the 'new' register set, and logs warnings on accesses to
+ * the legacy IO space.
+ *
+ * The hardware uses 32bit registers to manage 51 IRQs, with low and high
+ * registers for each conceptual register. The device model's implementation
+ * uses 64bit data types to store both low and high register values (in the one
+ * member), but must cope with access offset values in multiples of 4 passed to
+ * the callbacks. As such the read() and write() implementations process the
+ * provided offset to understand whether the access is requesting the lower or
+ * upper 32 bits of the 64bit member.
+ *
+ * Additionally, the "Interrupt Enable", "Edge Status" and "Software Interrupt"
+ * fields have separate "enable"/"status" and "clear" registers, where set bits
+ * are written to one or the other to change state (avoiding a
+ * read-modify-write sequence).
+ */
+
+#include "qemu/osdep.h"
+#include <inttypes.h>
+#include "hw/intc/aspeed_vic.h"
+#include "qemu/bitops.h"
+#include "trace.h"
+
+#define AVIC_NEW_BASE_OFFSET 0x80
+
+#define AVIC_L_MASK 0xFFFFFFFFU
+#define AVIC_H_MASK 0x0007FFFFU
+#define AVIC_EVENT_W_MASK (0x78000ULL << 32)
+
+static void aspeed_vic_update(AspeedVICState *s)
+{
+ uint64_t new = (s->raw & s->enable);
+ uint64_t flags;
+
+ flags = new & s->select;
+ trace_aspeed_vic_update_fiq(!!flags);
+ qemu_set_irq(s->fiq, !!flags);
+
+ flags = new & ~s->select;
+ trace_aspeed_vic_update_irq(!!flags);
+ qemu_set_irq(s->irq, !!flags);
+}
+
+static void aspeed_vic_set_irq(void *opaque, int irq, int level)
+{
+ uint64_t irq_mask;
+ bool raise;
+ AspeedVICState *s = (AspeedVICState *)opaque;
+
+ if (irq > ASPEED_VIC_NR_IRQS) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n",
+ __func__, irq);
+ return;
+ }
+
+ trace_aspeed_vic_set_irq(irq, level);
+
+ irq_mask = BIT(irq);
+ if (s->sense & irq_mask) {
+ /* level-triggered */
+ if (s->event & irq_mask) {
+ /* high-sensitive */
+ raise = level;
+ } else {
+ /* low-sensitive */
+ raise = !level;
+ }
+ s->raw = deposit64(s->raw, irq, 1, raise);
+ } else {
+ uint64_t old_level = s->level & irq_mask;
+
+ /* edge-triggered */
+ if (s->dual_edge & irq_mask) {
+ raise = (!!old_level) != (!!level);
+ } else {
+ if (s->event & irq_mask) {
+ /* rising-sensitive */
+ raise = !old_level && level;
+ } else {
+ /* falling-sensitive */
+ raise = old_level && !level;
+ }
+ }
+ if (raise) {
+ s->raw = deposit64(s->raw, irq, 1, raise);
+ }
+ }
+ s->level = deposit64(s->level, irq, 1, level);
+ aspeed_vic_update(s);
+}
+
+static uint64_t aspeed_vic_read(void *opaque, hwaddr offset, unsigned size)
+{
+ uint64_t val;
+ const bool high = !!(offset & 0x4);
+ hwaddr n_offset = (offset & ~0x4);
+ AspeedVICState *s = (AspeedVICState *)opaque;
+
+ if (offset < AVIC_NEW_BASE_OFFSET) {
+ qemu_log_mask(LOG_UNIMP, "%s: Ignoring read from legacy registers "
+ "at 0x%" HWADDR_PRIx "[%u]\n", __func__, offset, size);
+ return 0;
+ }
+
+ n_offset -= AVIC_NEW_BASE_OFFSET;
+
+ switch (n_offset) {
+ case 0x0: /* IRQ Status */
+ val = s->raw & ~s->select & s->enable;
+ break;
+ case 0x08: /* FIQ Status */
+ val = s->raw & s->select & s->enable;
+ break;
+ case 0x10: /* Raw Interrupt Status */
+ val = s->raw;
+ break;
+ case 0x18: /* Interrupt Selection */
+ val = s->select;
+ break;
+ case 0x20: /* Interrupt Enable */
+ val = s->enable;
+ break;
+ case 0x30: /* Software Interrupt */
+ val = s->trigger;
+ break;
+ case 0x40: /* Interrupt Sensitivity */
+ val = s->sense;
+ break;
+ case 0x48: /* Interrupt Both Edge Trigger Control */
+ val = s->dual_edge;
+ break;
+ case 0x50: /* Interrupt Event */
+ val = s->event;
+ break;
+ case 0x60: /* Edge Triggered Interrupt Status */
+ val = s->raw & ~s->sense;
+ break;
+ /* Illegal */
+ case 0x28: /* Interrupt Enable Clear */
+ case 0x38: /* Software Interrupt Clear */
+ case 0x58: /* Edge Triggered Interrupt Clear */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Read of write-only register with offset 0x%"
+ HWADDR_PRIx "\n", __func__, offset);
+ val = 0;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad register at offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ val = 0;
+ break;
+ }
+ if (high) {
+ val = extract64(val, 32, 19);
+ }
+ trace_aspeed_vic_read(offset, size, val);
+ return val;
+}
+
+static void aspeed_vic_write(void *opaque, hwaddr offset, uint64_t data,
+ unsigned size)
+{
+ const bool high = !!(offset & 0x4);
+ hwaddr n_offset = (offset & ~0x4);
+ AspeedVICState *s = (AspeedVICState *)opaque;
+
+ if (offset < AVIC_NEW_BASE_OFFSET) {
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Ignoring write to legacy registers at 0x%"
+ HWADDR_PRIx "[%u] <- 0x%" PRIx64 "\n", __func__, offset,
+ size, data);
+ return;
+ }
+
+ n_offset -= AVIC_NEW_BASE_OFFSET;
+ trace_aspeed_vic_write(offset, size, data);
+
+ /* Given we have members using separate enable/clear registers, deposit64()
+ * isn't quite the tool for the job. Instead, relocate the incoming bits to
+ * the required bit offset based on the provided access address
+ */
+ if (high) {
+ data &= AVIC_H_MASK;
+ data <<= 32;
+ } else {
+ data &= AVIC_L_MASK;
+ }
+
+ switch (n_offset) {
+ case 0x18: /* Interrupt Selection */
+ /* Register has deposit64() semantics - overwrite requested 32 bits */
+ if (high) {
+ s->select &= AVIC_L_MASK;
+ } else {
+ s->select &= ((uint64_t) AVIC_H_MASK) << 32;
+ }
+ s->select |= data;
+ break;
+ case 0x20: /* Interrupt Enable */
+ s->enable |= data;
+ break;
+ case 0x28: /* Interrupt Enable Clear */
+ s->enable &= ~data;
+ break;
+ case 0x30: /* Software Interrupt */
+ qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. "
+ "IRQs requested: 0x%016" PRIx64 "\n", __func__, data);
+ break;
+ case 0x38: /* Software Interrupt Clear */
+ qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. "
+ "IRQs to be cleared: 0x%016" PRIx64 "\n", __func__, data);
+ break;
+ case 0x50: /* Interrupt Event */
+ /* Register has deposit64() semantics - overwrite the top four valid
+ * IRQ bits, as only the top four IRQs (GPIOs) can change their event
+ * type */
+ if (high) {
+ s->event &= ~AVIC_EVENT_W_MASK;
+ s->event |= (data & AVIC_EVENT_W_MASK);
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "Ignoring invalid write to interrupt event register");
+ }
+ break;
+ case 0x58: /* Edge Triggered Interrupt Clear */
+ s->raw &= ~(data & ~s->sense);
+ break;
+ case 0x00: /* IRQ Status */
+ case 0x08: /* FIQ Status */
+ case 0x10: /* Raw Interrupt Status */
+ case 0x40: /* Interrupt Sensitivity */
+ case 0x48: /* Interrupt Both Edge Trigger Control */
+ case 0x60: /* Edge Triggered Interrupt Status */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Write of read-only register with offset 0x%"
+ HWADDR_PRIx "\n", __func__, offset);
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad register at offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ break;
+ }
+ aspeed_vic_update(s);
+}
+
+static const MemoryRegionOps aspeed_vic_ops = {
+ .read = aspeed_vic_read,
+ .write = aspeed_vic_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .valid.unaligned = false,
+};
+
+static void aspeed_vic_reset(DeviceState *dev)
+{
+ AspeedVICState *s = ASPEED_VIC(dev);
+
+ s->level = 0;
+ s->raw = 0;
+ s->select = 0;
+ s->enable = 0;
+ s->trigger = 0;
+ s->sense = 0x1F07FFF8FFFFULL;
+ s->dual_edge = 0xF800070000ULL;
+ s->event = 0x5F07FFF8FFFFULL;
+}
+
+#define AVIC_IO_REGION_SIZE 0x20000
+
+static void aspeed_vic_realize(DeviceState *dev, Error **errp)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ AspeedVICState *s = ASPEED_VIC(dev);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_vic_ops, s,
+ TYPE_ASPEED_VIC, AVIC_IO_REGION_SIZE);
+
+ sysbus_init_mmio(sbd, &s->iomem);
+
+ qdev_init_gpio_in(dev, aspeed_vic_set_irq, ASPEED_VIC_NR_IRQS);
+ sysbus_init_irq(sbd, &s->irq);
+ sysbus_init_irq(sbd, &s->fiq);
+}
+
+static const VMStateDescription vmstate_aspeed_vic = {
+ .name = "aspeed.new-vic",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(level, AspeedVICState),
+ VMSTATE_UINT64(raw, AspeedVICState),
+ VMSTATE_UINT64(select, AspeedVICState),
+ VMSTATE_UINT64(enable, AspeedVICState),
+ VMSTATE_UINT64(trigger, AspeedVICState),
+ VMSTATE_UINT64(sense, AspeedVICState),
+ VMSTATE_UINT64(dual_edge, AspeedVICState),
+ VMSTATE_UINT64(event, AspeedVICState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void aspeed_vic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ dc->realize = aspeed_vic_realize;
+ dc->reset = aspeed_vic_reset;
+ dc->desc = "ASPEED Interrupt Controller (New)";
+ dc->vmsd = &vmstate_aspeed_vic;
+}
+
+static const TypeInfo aspeed_vic_info = {
+ .name = TYPE_ASPEED_VIC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AspeedVICState),
+ .class_init = aspeed_vic_class_init,
+};
+
+static void aspeed_vic_register_types(void)
+{
+ type_register_static(&aspeed_vic_info);
+}
+
+type_init(aspeed_vic_register_types);
diff --git a/qemu/hw/intc/bcm2835_ic.c b/qemu/hw/intc/bcm2835_ic.c
new file mode 100644
index 000000000..80513b28f
--- /dev/null
+++ b/qemu/hw/intc/bcm2835_ic.c
@@ -0,0 +1,237 @@
+/*
+ * Raspberry Pi emulation (c) 2012 Gregory Estrade
+ * Refactoring for Pi2 Copyright (c) 2015, Microsoft. Written by Andrew Baumann.
+ * This code is licensed under the GNU GPLv2 and later.
+ * Heavily based on pl190.c, copyright terms below:
+ *
+ * Arm PrimeCell PL190 Vector Interrupt Controller
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/intc/bcm2835_ic.h"
+
+#define GPU_IRQS 64
+#define ARM_IRQS 8
+
+#define IRQ_PENDING_BASIC 0x00 /* IRQ basic pending */
+#define IRQ_PENDING_1 0x04 /* IRQ pending 1 */
+#define IRQ_PENDING_2 0x08 /* IRQ pending 2 */
+#define FIQ_CONTROL 0x0C /* FIQ register */
+#define IRQ_ENABLE_1 0x10 /* Interrupt enable register 1 */
+#define IRQ_ENABLE_2 0x14 /* Interrupt enable register 2 */
+#define IRQ_ENABLE_BASIC 0x18 /* Base interrupt enable register */
+#define IRQ_DISABLE_1 0x1C /* Interrupt disable register 1 */
+#define IRQ_DISABLE_2 0x20 /* Interrupt disable register 2 */
+#define IRQ_DISABLE_BASIC 0x24 /* Base interrupt disable register */
+
+/* Update interrupts. */
+static void bcm2835_ic_update(BCM2835ICState *s)
+{
+ bool set = false;
+
+ if (s->fiq_enable) {
+ if (s->fiq_select >= GPU_IRQS) {
+ /* ARM IRQ */
+ set = extract32(s->arm_irq_level, s->fiq_select - GPU_IRQS, 1);
+ } else {
+ set = extract64(s->gpu_irq_level, s->fiq_select, 1);
+ }
+ }
+ qemu_set_irq(s->fiq, set);
+
+ set = (s->gpu_irq_level & s->gpu_irq_enable)
+ || (s->arm_irq_level & s->arm_irq_enable);
+ qemu_set_irq(s->irq, set);
+
+}
+
+static void bcm2835_ic_set_gpu_irq(void *opaque, int irq, int level)
+{
+ BCM2835ICState *s = opaque;
+
+ assert(irq >= 0 && irq < 64);
+ s->gpu_irq_level = deposit64(s->gpu_irq_level, irq, 1, level != 0);
+ bcm2835_ic_update(s);
+}
+
+static void bcm2835_ic_set_arm_irq(void *opaque, int irq, int level)
+{
+ BCM2835ICState *s = opaque;
+
+ assert(irq >= 0 && irq < 8);
+ s->arm_irq_level = deposit32(s->arm_irq_level, irq, 1, level != 0);
+ bcm2835_ic_update(s);
+}
+
+static const int irq_dups[] = { 7, 9, 10, 18, 19, 53, 54, 55, 56, 57, 62 };
+
+static uint64_t bcm2835_ic_read(void *opaque, hwaddr offset, unsigned size)
+{
+ BCM2835ICState *s = opaque;
+ uint32_t res = 0;
+ uint64_t gpu_pending = s->gpu_irq_level & s->gpu_irq_enable;
+ int i;
+
+ switch (offset) {
+ case IRQ_PENDING_BASIC:
+ /* bits 0-7: ARM irqs */
+ res = s->arm_irq_level & s->arm_irq_enable;
+
+ /* bits 8 & 9: pending registers 1 & 2 */
+ res |= (((uint32_t)gpu_pending) != 0) << 8;
+ res |= ((gpu_pending >> 32) != 0) << 9;
+
+ /* bits 10-20: selected GPU IRQs */
+ for (i = 0; i < ARRAY_SIZE(irq_dups); i++) {
+ res |= extract64(gpu_pending, irq_dups[i], 1) << (i + 10);
+ }
+ break;
+ case IRQ_PENDING_1:
+ res = gpu_pending;
+ break;
+ case IRQ_PENDING_2:
+ res = gpu_pending >> 32;
+ break;
+ case FIQ_CONTROL:
+ res = (s->fiq_enable << 7) | s->fiq_select;
+ break;
+ case IRQ_ENABLE_1:
+ res = s->gpu_irq_enable;
+ break;
+ case IRQ_ENABLE_2:
+ res = s->gpu_irq_enable >> 32;
+ break;
+ case IRQ_ENABLE_BASIC:
+ res = s->arm_irq_enable;
+ break;
+ case IRQ_DISABLE_1:
+ res = ~s->gpu_irq_enable;
+ break;
+ case IRQ_DISABLE_2:
+ res = ~s->gpu_irq_enable >> 32;
+ break;
+ case IRQ_DISABLE_BASIC:
+ res = ~s->arm_irq_enable;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return 0;
+ }
+
+ return res;
+}
+
+static void bcm2835_ic_write(void *opaque, hwaddr offset, uint64_t val,
+ unsigned size)
+{
+ BCM2835ICState *s = opaque;
+
+ switch (offset) {
+ case FIQ_CONTROL:
+ s->fiq_select = extract32(val, 0, 7);
+ s->fiq_enable = extract32(val, 7, 1);
+ break;
+ case IRQ_ENABLE_1:
+ s->gpu_irq_enable |= val;
+ break;
+ case IRQ_ENABLE_2:
+ s->gpu_irq_enable |= val << 32;
+ break;
+ case IRQ_ENABLE_BASIC:
+ s->arm_irq_enable |= val & 0xff;
+ break;
+ case IRQ_DISABLE_1:
+ s->gpu_irq_enable &= ~val;
+ break;
+ case IRQ_DISABLE_2:
+ s->gpu_irq_enable &= ~(val << 32);
+ break;
+ case IRQ_DISABLE_BASIC:
+ s->arm_irq_enable &= ~val & 0xff;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return;
+ }
+ bcm2835_ic_update(s);
+}
+
+static const MemoryRegionOps bcm2835_ic_ops = {
+ .read = bcm2835_ic_read,
+ .write = bcm2835_ic_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+static void bcm2835_ic_reset(DeviceState *d)
+{
+ BCM2835ICState *s = BCM2835_IC(d);
+
+ s->gpu_irq_enable = 0;
+ s->arm_irq_enable = 0;
+ s->fiq_enable = false;
+ s->fiq_select = 0;
+}
+
+static void bcm2835_ic_init(Object *obj)
+{
+ BCM2835ICState *s = BCM2835_IC(obj);
+
+ memory_region_init_io(&s->iomem, obj, &bcm2835_ic_ops, s, TYPE_BCM2835_IC,
+ 0x200);
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
+
+ qdev_init_gpio_in_named(DEVICE(s), bcm2835_ic_set_gpu_irq,
+ BCM2835_IC_GPU_IRQ, GPU_IRQS);
+ qdev_init_gpio_in_named(DEVICE(s), bcm2835_ic_set_arm_irq,
+ BCM2835_IC_ARM_IRQ, ARM_IRQS);
+
+ sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq);
+ sysbus_init_irq(SYS_BUS_DEVICE(s), &s->fiq);
+}
+
+static const VMStateDescription vmstate_bcm2835_ic = {
+ .name = TYPE_BCM2835_IC,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(gpu_irq_level, BCM2835ICState),
+ VMSTATE_UINT64(gpu_irq_enable, BCM2835ICState),
+ VMSTATE_UINT8(arm_irq_level, BCM2835ICState),
+ VMSTATE_UINT8(arm_irq_enable, BCM2835ICState),
+ VMSTATE_BOOL(fiq_enable, BCM2835ICState),
+ VMSTATE_UINT8(fiq_select, BCM2835ICState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void bcm2835_ic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = bcm2835_ic_reset;
+ dc->vmsd = &vmstate_bcm2835_ic;
+}
+
+static TypeInfo bcm2835_ic_info = {
+ .name = TYPE_BCM2835_IC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BCM2835ICState),
+ .class_init = bcm2835_ic_class_init,
+ .instance_init = bcm2835_ic_init,
+};
+
+static void bcm2835_ic_register_types(void)
+{
+ type_register_static(&bcm2835_ic_info);
+}
+
+type_init(bcm2835_ic_register_types)
diff --git a/qemu/hw/intc/bcm2836_control.c b/qemu/hw/intc/bcm2836_control.c
new file mode 100644
index 000000000..d0271810c
--- /dev/null
+++ b/qemu/hw/intc/bcm2836_control.c
@@ -0,0 +1,304 @@
+/*
+ * Rasperry Pi 2 emulation ARM control logic module.
+ * Copyright (c) 2015, Microsoft
+ * Written by Andrew Baumann
+ *
+ * Based on bcm2835_ic.c (Raspberry Pi emulation) (c) 2012 Gregory Estrade
+ * This code is licensed under the GNU GPLv2 and later.
+ *
+ * At present, only implements interrupt routing, and mailboxes (i.e.,
+ * not local timer, PMU interrupt, or AXI counters).
+ *
+ * Ref:
+ * https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf
+ */
+
+#include "qemu/osdep.h"
+#include "hw/intc/bcm2836_control.h"
+
+#define REG_GPU_ROUTE 0x0c
+#define REG_TIMERCONTROL 0x40
+#define REG_MBOXCONTROL 0x50
+#define REG_IRQSRC 0x60
+#define REG_FIQSRC 0x70
+#define REG_MBOX0_WR 0x80
+#define REG_MBOX0_RDCLR 0xc0
+#define REG_LIMIT 0x100
+
+#define IRQ_BIT(cntrl, num) (((cntrl) & (1 << (num))) != 0)
+#define FIQ_BIT(cntrl, num) (((cntrl) & (1 << ((num) + 4))) != 0)
+
+#define IRQ_CNTPSIRQ 0
+#define IRQ_CNTPNSIRQ 1
+#define IRQ_CNTHPIRQ 2
+#define IRQ_CNTVIRQ 3
+#define IRQ_MAILBOX0 4
+#define IRQ_MAILBOX1 5
+#define IRQ_MAILBOX2 6
+#define IRQ_MAILBOX3 7
+#define IRQ_GPU 8
+#define IRQ_PMU 9
+#define IRQ_AXI 10
+#define IRQ_TIMER 11
+#define IRQ_MAX IRQ_TIMER
+
+static void deliver_local(BCM2836ControlState *s, uint8_t core, uint8_t irq,
+ uint32_t controlreg, uint8_t controlidx)
+{
+ if (FIQ_BIT(controlreg, controlidx)) {
+ /* deliver a FIQ */
+ s->fiqsrc[core] |= (uint32_t)1 << irq;
+ } else if (IRQ_BIT(controlreg, controlidx)) {
+ /* deliver an IRQ */
+ s->irqsrc[core] |= (uint32_t)1 << irq;
+ } else {
+ /* the interrupt is masked */
+ }
+}
+
+/* Update interrupts. */
+static void bcm2836_control_update(BCM2836ControlState *s)
+{
+ int i, j;
+
+ /* reset pending IRQs/FIQs */
+ for (i = 0; i < BCM2836_NCORES; i++) {
+ s->irqsrc[i] = s->fiqsrc[i] = 0;
+ }
+
+ /* apply routing logic, update status regs */
+ if (s->gpu_irq) {
+ assert(s->route_gpu_irq < BCM2836_NCORES);
+ s->irqsrc[s->route_gpu_irq] |= (uint32_t)1 << IRQ_GPU;
+ }
+
+ if (s->gpu_fiq) {
+ assert(s->route_gpu_fiq < BCM2836_NCORES);
+ s->fiqsrc[s->route_gpu_fiq] |= (uint32_t)1 << IRQ_GPU;
+ }
+
+ for (i = 0; i < BCM2836_NCORES; i++) {
+ /* handle local timer interrupts for this core */
+ if (s->timerirqs[i]) {
+ assert(s->timerirqs[i] < (1 << (IRQ_CNTVIRQ + 1))); /* sane mask? */
+ for (j = 0; j <= IRQ_CNTVIRQ; j++) {
+ if ((s->timerirqs[i] & (1 << j)) != 0) {
+ /* local interrupt j is set */
+ deliver_local(s, i, j, s->timercontrol[i], j);
+ }
+ }
+ }
+
+ /* handle mailboxes for this core */
+ for (j = 0; j < BCM2836_MBPERCORE; j++) {
+ if (s->mailboxes[i * BCM2836_MBPERCORE + j] != 0) {
+ /* mailbox j is set */
+ deliver_local(s, i, j + IRQ_MAILBOX0, s->mailboxcontrol[i], j);
+ }
+ }
+ }
+
+ /* call set_irq appropriately for each output */
+ for (i = 0; i < BCM2836_NCORES; i++) {
+ qemu_set_irq(s->irq[i], s->irqsrc[i] != 0);
+ qemu_set_irq(s->fiq[i], s->fiqsrc[i] != 0);
+ }
+}
+
+static void bcm2836_control_set_local_irq(void *opaque, int core, int local_irq,
+ int level)
+{
+ BCM2836ControlState *s = opaque;
+
+ assert(core >= 0 && core < BCM2836_NCORES);
+ assert(local_irq >= 0 && local_irq <= IRQ_CNTVIRQ);
+
+ s->timerirqs[core] = deposit32(s->timerirqs[core], local_irq, 1, !!level);
+
+ bcm2836_control_update(s);
+}
+
+/* XXX: the following wrapper functions are a kludgy workaround,
+ * needed because I can't seem to pass useful information in the "irq"
+ * parameter when using named interrupts. Feel free to clean this up!
+ */
+
+static void bcm2836_control_set_local_irq0(void *opaque, int core, int level)
+{
+ bcm2836_control_set_local_irq(opaque, core, 0, level);
+}
+
+static void bcm2836_control_set_local_irq1(void *opaque, int core, int level)
+{
+ bcm2836_control_set_local_irq(opaque, core, 1, level);
+}
+
+static void bcm2836_control_set_local_irq2(void *opaque, int core, int level)
+{
+ bcm2836_control_set_local_irq(opaque, core, 2, level);
+}
+
+static void bcm2836_control_set_local_irq3(void *opaque, int core, int level)
+{
+ bcm2836_control_set_local_irq(opaque, core, 3, level);
+}
+
+static void bcm2836_control_set_gpu_irq(void *opaque, int irq, int level)
+{
+ BCM2836ControlState *s = opaque;
+
+ s->gpu_irq = level;
+
+ bcm2836_control_update(s);
+}
+
+static void bcm2836_control_set_gpu_fiq(void *opaque, int irq, int level)
+{
+ BCM2836ControlState *s = opaque;
+
+ s->gpu_fiq = level;
+
+ bcm2836_control_update(s);
+}
+
+static uint64_t bcm2836_control_read(void *opaque, hwaddr offset, unsigned size)
+{
+ BCM2836ControlState *s = opaque;
+
+ if (offset == REG_GPU_ROUTE) {
+ assert(s->route_gpu_fiq < BCM2836_NCORES
+ && s->route_gpu_irq < BCM2836_NCORES);
+ return ((uint32_t)s->route_gpu_fiq << 2) | s->route_gpu_irq;
+ } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
+ return s->timercontrol[(offset - REG_TIMERCONTROL) >> 2];
+ } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
+ return s->mailboxcontrol[(offset - REG_MBOXCONTROL) >> 2];
+ } else if (offset >= REG_IRQSRC && offset < REG_FIQSRC) {
+ return s->irqsrc[(offset - REG_IRQSRC) >> 2];
+ } else if (offset >= REG_FIQSRC && offset < REG_MBOX0_WR) {
+ return s->fiqsrc[(offset - REG_FIQSRC) >> 2];
+ } else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) {
+ return s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2];
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return 0;
+ }
+}
+
+static void bcm2836_control_write(void *opaque, hwaddr offset,
+ uint64_t val, unsigned size)
+{
+ BCM2836ControlState *s = opaque;
+
+ if (offset == REG_GPU_ROUTE) {
+ s->route_gpu_irq = val & 0x3;
+ s->route_gpu_fiq = (val >> 2) & 0x3;
+ } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
+ s->timercontrol[(offset - REG_TIMERCONTROL) >> 2] = val & 0xff;
+ } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
+ s->mailboxcontrol[(offset - REG_MBOXCONTROL) >> 2] = val & 0xff;
+ } else if (offset >= REG_MBOX0_WR && offset < REG_MBOX0_RDCLR) {
+ s->mailboxes[(offset - REG_MBOX0_WR) >> 2] |= val;
+ } else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) {
+ s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2] &= ~val;
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return;
+ }
+
+ bcm2836_control_update(s);
+}
+
+static const MemoryRegionOps bcm2836_control_ops = {
+ .read = bcm2836_control_read,
+ .write = bcm2836_control_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+static void bcm2836_control_reset(DeviceState *d)
+{
+ BCM2836ControlState *s = BCM2836_CONTROL(d);
+ int i;
+
+ s->route_gpu_irq = s->route_gpu_fiq = 0;
+
+ for (i = 0; i < BCM2836_NCORES; i++) {
+ s->timercontrol[i] = 0;
+ s->mailboxcontrol[i] = 0;
+ }
+
+ for (i = 0; i < BCM2836_NCORES * BCM2836_MBPERCORE; i++) {
+ s->mailboxes[i] = 0;
+ }
+}
+
+static void bcm2836_control_init(Object *obj)
+{
+ BCM2836ControlState *s = BCM2836_CONTROL(obj);
+ DeviceState *dev = DEVICE(obj);
+
+ memory_region_init_io(&s->iomem, obj, &bcm2836_control_ops, s,
+ TYPE_BCM2836_CONTROL, REG_LIMIT);
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
+
+ /* inputs from each CPU core */
+ qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq0, "cntpsirq",
+ BCM2836_NCORES);
+ qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq1, "cntpnsirq",
+ BCM2836_NCORES);
+ qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq2, "cnthpirq",
+ BCM2836_NCORES);
+ qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq3, "cntvirq",
+ BCM2836_NCORES);
+
+ /* IRQ and FIQ inputs from upstream bcm2835 controller */
+ qdev_init_gpio_in_named(dev, bcm2836_control_set_gpu_irq, "gpu-irq", 1);
+ qdev_init_gpio_in_named(dev, bcm2836_control_set_gpu_fiq, "gpu-fiq", 1);
+
+ /* outputs to CPU cores */
+ qdev_init_gpio_out_named(dev, s->irq, "irq", BCM2836_NCORES);
+ qdev_init_gpio_out_named(dev, s->fiq, "fiq", BCM2836_NCORES);
+}
+
+static const VMStateDescription vmstate_bcm2836_control = {
+ .name = TYPE_BCM2836_CONTROL,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(mailboxes, BCM2836ControlState,
+ BCM2836_NCORES * BCM2836_MBPERCORE),
+ VMSTATE_UINT8(route_gpu_irq, BCM2836ControlState),
+ VMSTATE_UINT8(route_gpu_fiq, BCM2836ControlState),
+ VMSTATE_UINT32_ARRAY(timercontrol, BCM2836ControlState, BCM2836_NCORES),
+ VMSTATE_UINT32_ARRAY(mailboxcontrol, BCM2836ControlState,
+ BCM2836_NCORES),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void bcm2836_control_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = bcm2836_control_reset;
+ dc->vmsd = &vmstate_bcm2836_control;
+}
+
+static TypeInfo bcm2836_control_info = {
+ .name = TYPE_BCM2836_CONTROL,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BCM2836ControlState),
+ .class_init = bcm2836_control_class_init,
+ .instance_init = bcm2836_control_init,
+};
+
+static void bcm2836_control_register_types(void)
+{
+ type_register_static(&bcm2836_control_info);
+}
+
+type_init(bcm2836_control_register_types)
diff --git a/qemu/hw/intc/etraxfs_pic.c b/qemu/hw/intc/etraxfs_pic.c
index bd588681e..48f947706 100644
--- a/qemu/hw/intc/etraxfs_pic.c
+++ b/qemu/hw/intc/etraxfs_pic.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/hw.h"
//#include "pc.h"
diff --git a/qemu/hw/intc/exynos4210_combiner.c b/qemu/hw/intc/exynos4210_combiner.c
index a6b702890..dc0c90326 100644
--- a/qemu/hw/intc/exynos4210_combiner.c
+++ b/qemu/hw/intc/exynos4210_combiner.c
@@ -27,6 +27,7 @@
* IRQs are passed to GIC through Combiner.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/arm/exynos4210.h"
diff --git a/qemu/hw/intc/exynos4210_gic.c b/qemu/hw/intc/exynos4210_gic.c
index b2a4950bc..4f7e89f7b 100644
--- a/qemu/hw/intc/exynos4210_gic.c
+++ b/qemu/hw/intc/exynos4210_gic.c
@@ -20,6 +20,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "qemu-common.h"
#include "hw/irq.h"
diff --git a/qemu/hw/intc/grlib_irqmp.c b/qemu/hw/intc/grlib_irqmp.c
index d1813f76b..f5ca8f752 100644
--- a/qemu/hw/intc/grlib_irqmp.c
+++ b/qemu/hw/intc/grlib_irqmp.c
@@ -24,6 +24,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "cpu.h"
diff --git a/qemu/hw/intc/heathrow_pic.c b/qemu/hw/intc/heathrow_pic.c
index 9ff3119ed..171f5ed81 100644
--- a/qemu/hw/intc/heathrow_pic.c
+++ b/qemu/hw/intc/heathrow_pic.c
@@ -22,6 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/ppc/mac.h"
diff --git a/qemu/hw/intc/i8259.c b/qemu/hw/intc/i8259.c
index 0f5c0259a..bb43669b9 100644
--- a/qemu/hw/intc/i8259.c
+++ b/qemu/hw/intc/i8259.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/isa/isa.h"
@@ -229,7 +230,7 @@ int pic_read_irq(DeviceState *d)
printf("IRQ%d latency=%0.3fus\n",
irq,
(double)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) -
- irq_time[irq]) * 1000000.0 / get_ticks_per_sec());
+ irq_time[irq]) * 1000000.0 / NANOSECONDS_PER_SECOND);
#endif
DPRINTF("pic_interrupt: irq=%d\n", irq);
return intno;
diff --git a/qemu/hw/intc/i8259_common.c b/qemu/hw/intc/i8259_common.c
index fbf26e557..3a850b0c6 100644
--- a/qemu/hw/intc/i8259_common.c
+++ b/qemu/hw/intc/i8259_common.c
@@ -22,6 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/i386/pc.h"
#include "hw/isa/i8259_internal.h"
diff --git a/qemu/hw/intc/imx_avic.c b/qemu/hw/intc/imx_avic.c
index e48f66c8f..702765577 100644
--- a/qemu/hw/intc/imx_avic.c
+++ b/qemu/hw/intc/imx_avic.c
@@ -7,6 +7,7 @@
* Copyright (c) 2008 OKL
* Copyright (c) 2011 NICTA Pty Ltd
* Originally written by Hans Jiang
+ * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
*
* This code is licensed under the GPL version 2 or later. See
* the COPYING file in the top-level directory.
@@ -14,67 +15,23 @@
* TODO: implement vectors.
*/
-#include "hw/hw.h"
-#include "hw/sysbus.h"
-#include "qemu/host-utils.h"
+#include "qemu/osdep.h"
+#include "hw/intc/imx_avic.h"
-#define DEBUG_INT 1
-#undef DEBUG_INT /* comment out for debugging */
-
-#ifdef DEBUG_INT
-#define DPRINTF(fmt, args...) \
-do { printf("imx_avic: " fmt , ##args); } while (0)
-#else
-#define DPRINTF(fmt, args...) do {} while (0)
+#ifndef DEBUG_IMX_AVIC
+#define DEBUG_IMX_AVIC 0
#endif
-/*
- * Define to 1 for messages about attempts to
- * access unimplemented registers or similar.
- */
-#define DEBUG_IMPLEMENTATION 1
-#if DEBUG_IMPLEMENTATION
-# define IPRINTF(fmt, args...) \
- do { fprintf(stderr, "imx_avic: " fmt, ##args); } while (0)
-#else
-# define IPRINTF(fmt, args...) do {} while (0)
-#endif
-
-#define IMX_AVIC_NUM_IRQS 64
-
-/* Interrupt Control Bits */
-#define ABFLAG (1<<25)
-#define ABFEN (1<<24)
-#define NIDIS (1<<22) /* Normal Interrupt disable */
-#define FIDIS (1<<21) /* Fast interrupt disable */
-#define NIAD (1<<20) /* Normal Interrupt Arbiter Rise ARM level */
-#define FIAD (1<<19) /* Fast Interrupt Arbiter Rise ARM level */
-#define NM (1<<18) /* Normal interrupt mode */
-
-
-#define PRIO_PER_WORD (sizeof(uint32_t) * 8 / 4)
-#define PRIO_WORDS (IMX_AVIC_NUM_IRQS/PRIO_PER_WORD)
-
-#define TYPE_IMX_AVIC "imx_avic"
-#define IMX_AVIC(obj) \
- OBJECT_CHECK(IMXAVICState, (obj), TYPE_IMX_AVIC)
-
-typedef struct IMXAVICState {
- SysBusDevice parent_obj;
-
- MemoryRegion iomem;
- uint64_t pending;
- uint64_t enabled;
- uint64_t is_fiq;
- uint32_t intcntl;
- uint32_t intmask;
- qemu_irq irq;
- qemu_irq fiq;
- uint32_t prio[PRIO_WORDS]; /* Priorities are 4-bits each */
-} IMXAVICState;
+#define DPRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX_AVIC) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_AVIC, \
+ __func__, ##args); \
+ } \
+ } while (0)
static const VMStateDescription vmstate_imx_avic = {
- .name = "imx-avic",
+ .name = TYPE_IMX_AVIC,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
@@ -88,8 +45,6 @@ static const VMStateDescription vmstate_imx_avic = {
},
};
-
-
static inline int imx_avic_prio(IMXAVICState *s, int irq)
{
uint32_t word = irq / PRIO_PER_WORD;
@@ -151,8 +106,8 @@ static uint64_t imx_avic_read(void *opaque,
{
IMXAVICState *s = (IMXAVICState *)opaque;
+ DPRINTF("read(offset = 0x%" HWADDR_PRIx ")\n", offset);
- DPRINTF("read(offset = 0x%x)\n", offset >> 2);
switch (offset >> 2) {
case 0: /* INTCNTL */
return s->intcntl;
@@ -249,7 +204,8 @@ static uint64_t imx_avic_read(void *opaque,
return 0x4;
default:
- IPRINTF("imx_avic_read: Bad offset 0x%x\n", (int)offset);
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_AVIC, __func__, offset);
return 0;
}
}
@@ -261,13 +217,13 @@ static void imx_avic_write(void *opaque, hwaddr offset,
/* Vector Registers not yet supported */
if (offset >= 0x100 && offset <= 0x2fc) {
- IPRINTF("imx_avic_write to vector register %d ignored\n",
- (unsigned int)((offset - 0x100) >> 2));
+ qemu_log_mask(LOG_UNIMP, "[%s]%s: vector %d ignored\n",
+ TYPE_IMX_AVIC, __func__, (int)((offset - 0x100) >> 2));
return;
}
- DPRINTF("imx_avic_write(0x%x) = %x\n",
- (unsigned int)offset>>2, (unsigned int)val);
+ DPRINTF("(0x%" HWADDR_PRIx ") = 0x%x\n", offset, (unsigned int)val);
+
switch (offset >> 2) {
case 0: /* Interrupt Control Register, INTCNTL */
s->intcntl = val & (ABFEN | NIDIS | FIDIS | NIAD | FIAD | NM);
@@ -341,7 +297,8 @@ static void imx_avic_write(void *opaque, hwaddr offset,
return;
default:
- IPRINTF("imx_avic_write: Bad offset %x\n", (int)offset);
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_AVIC, __func__, offset);
}
imx_avic_update(s);
}
@@ -370,7 +327,7 @@ static int imx_avic_init(SysBusDevice *sbd)
IMXAVICState *s = IMX_AVIC(dev);
memory_region_init_io(&s->iomem, OBJECT(s), &imx_avic_ops, s,
- "imx_avic", 0x1000);
+ TYPE_IMX_AVIC, 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
qdev_init_gpio_in(dev, imx_avic_set_irq, IMX_AVIC_NUM_IRQS);
diff --git a/qemu/hw/intc/ioapic.c b/qemu/hw/intc/ioapic.c
index b52793238..378e663f6 100644
--- a/qemu/hw/intc/ioapic.c
+++ b/qemu/hw/intc/ioapic.c
@@ -20,10 +20,14 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "monitor/monitor.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/i386/ioapic.h"
#include "hw/i386/ioapic_internal.h"
+#include "include/hw/pci/msi.h"
+#include "sysemu/kvm.h"
//#define DEBUG_IOAPIC
@@ -34,6 +38,10 @@
#define DPRINTF(fmt, ...)
#endif
+#define APIC_DELIVERY_MODE_SHIFT 8
+#define APIC_POLARITY_SHIFT 14
+#define APIC_TRIG_MODE_SHIFT 15
+
static IOAPICCommonState *ioapics[MAX_IOAPICS];
/* global variable from ioapic_common.c */
@@ -53,6 +61,8 @@ static void ioapic_service(IOAPICCommonState *s)
for (i = 0; i < IOAPIC_NUM_PINS; i++) {
mask = 1 << i;
if (s->irr & mask) {
+ int coalesce = 0;
+
entry = s->ioredtbl[i];
if (!(entry & IOAPIC_LVT_MASKED)) {
trig_mode = ((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1);
@@ -63,6 +73,7 @@ static void ioapic_service(IOAPICCommonState *s)
if (trig_mode == IOAPIC_TRIGGER_EDGE) {
s->irr &= ~mask;
} else {
+ coalesce = s->ioredtbl[i] & IOAPIC_LVT_REMOTE_IRR;
s->ioredtbl[i] |= IOAPIC_LVT_REMOTE_IRR;
}
if (delivery_mode == IOAPIC_DM_EXTINT) {
@@ -70,8 +81,23 @@ static void ioapic_service(IOAPICCommonState *s)
} else {
vector = entry & IOAPIC_VECTOR_MASK;
}
- apic_deliver_irq(dest, dest_mode, delivery_mode,
- vector, trig_mode);
+#ifdef CONFIG_KVM
+ if (kvm_irqchip_is_split()) {
+ if (trig_mode == IOAPIC_TRIGGER_EDGE) {
+ kvm_set_irq(kvm_state, i, 1);
+ kvm_set_irq(kvm_state, i, 0);
+ } else {
+ if (!coalesce) {
+ kvm_set_irq(kvm_state, i, 1);
+ }
+ }
+ continue;
+ }
+#else
+ (void)coalesce;
+#endif
+ apic_deliver_irq(dest, dest_mode, delivery_mode, vector,
+ trig_mode);
}
}
}
@@ -98,7 +124,9 @@ static void ioapic_set_irq(void *opaque, int vector, int level)
/* level triggered */
if (level) {
s->irr |= mask;
- ioapic_service(s);
+ if (!(entry & IOAPIC_LVT_REMOTE_IRR)) {
+ ioapic_service(s);
+ }
} else {
s->irr &= ~mask;
}
@@ -113,6 +141,44 @@ static void ioapic_set_irq(void *opaque, int vector, int level)
}
}
+static void ioapic_update_kvm_routes(IOAPICCommonState *s)
+{
+#ifdef CONFIG_KVM
+ int i;
+
+ if (kvm_irqchip_is_split()) {
+ for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+ uint64_t entry = s->ioredtbl[i];
+ uint8_t trig_mode;
+ uint8_t delivery_mode;
+ uint8_t dest;
+ uint8_t dest_mode;
+ uint64_t pin_polarity;
+ MSIMessage msg;
+
+ trig_mode = ((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1);
+ dest = entry >> IOAPIC_LVT_DEST_SHIFT;
+ dest_mode = (entry >> IOAPIC_LVT_DEST_MODE_SHIFT) & 1;
+ pin_polarity = (entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1;
+ delivery_mode =
+ (entry >> IOAPIC_LVT_DELIV_MODE_SHIFT) & IOAPIC_DM_MASK;
+
+ msg.address = APIC_DEFAULT_ADDRESS;
+ msg.address |= dest_mode << 2;
+ msg.address |= dest << 12;
+
+ msg.data = entry & IOAPIC_VECTOR_MASK;
+ msg.data |= delivery_mode << APIC_DELIVERY_MODE_SHIFT;
+ msg.data |= pin_polarity << APIC_POLARITY_SHIFT;
+ msg.data |= trig_mode << APIC_TRIG_MODE_SHIFT;
+
+ kvm_irqchip_update_msi_route(kvm_state, i, msg, NULL);
+ }
+ kvm_irqchip_commit_routes(kvm_state);
+ }
+#endif
+}
+
void ioapic_eoi_broadcast(int vector)
{
IOAPICCommonState *s;
@@ -137,6 +203,17 @@ void ioapic_eoi_broadcast(int vector)
}
}
+void ioapic_dump_state(Monitor *mon, const QDict *qdict)
+{
+ int i;
+
+ for (i = 0; i < MAX_IOAPICS; i++) {
+ if (ioapics[i] != 0) {
+ ioapic_print_redtbl(mon, ioapics[i]);
+ }
+ }
+}
+
static uint64_t
ioapic_mem_read(void *opaque, hwaddr addr, unsigned int size)
{
@@ -154,15 +231,13 @@ ioapic_mem_read(void *opaque, hwaddr addr, unsigned int size)
}
switch (s->ioregsel) {
case IOAPIC_REG_ID:
+ case IOAPIC_REG_ARB:
val = s->id << IOAPIC_ID_SHIFT;
break;
case IOAPIC_REG_VER:
val = IOAPIC_VERSION |
((IOAPIC_NUM_PINS - 1) << IOAPIC_VER_ENTRIES_SHIFT);
break;
- case IOAPIC_REG_ARB:
- val = 0;
- break;
default:
index = (s->ioregsel - IOAPIC_REG_REDTBL_BASE) >> 1;
if (index >= 0 && index < IOAPIC_NUM_PINS) {
@@ -217,6 +292,8 @@ ioapic_mem_write(void *opaque, hwaddr addr, uint64_t val,
}
break;
}
+
+ ioapic_update_kvm_routes(s);
}
static const MemoryRegionOps ioapic_io_ops = {
diff --git a/qemu/hw/intc/ioapic_common.c b/qemu/hw/intc/ioapic_common.c
index 8b7d11806..1b7ec5ec2 100644
--- a/qemu/hw/intc/ioapic_common.c
+++ b/qemu/hw/intc/ioapic_common.c
@@ -19,6 +19,9 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "monitor/monitor.h"
#include "hw/i386/ioapic.h"
#include "hw/i386/ioapic_internal.h"
#include "hw/sysbus.h"
@@ -31,6 +34,60 @@
*/
int ioapic_no;
+static void ioapic_irr_dump(Monitor *mon, const char *name, uint32_t bitmap)
+{
+ int i;
+
+ monitor_printf(mon, "%-10s ", name);
+ if (bitmap == 0) {
+ monitor_printf(mon, "(none)\n");
+ return;
+ }
+ for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+ if (bitmap & (1 << i)) {
+ monitor_printf(mon, "%-2u ", i);
+ }
+ }
+ monitor_printf(mon, "\n");
+}
+
+void ioapic_print_redtbl(Monitor *mon, IOAPICCommonState *s)
+{
+ static const char *delm_str[] = {
+ "fixed", "lowest", "SMI", "...", "NMI", "INIT", "...", "extINT"};
+ uint32_t remote_irr = 0;
+ int i;
+
+ monitor_printf(mon, "ioapic id=0x%02x sel=0x%02x", s->id, s->ioregsel);
+ if (s->ioregsel) {
+ monitor_printf(mon, " (redir[%u])\n",
+ (s->ioregsel - IOAPIC_REG_REDTBL_BASE) >> 1);
+ } else {
+ monitor_printf(mon, "\n");
+ }
+ for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+ uint64_t entry = s->ioredtbl[i];
+ uint32_t delm = (uint32_t)((entry & IOAPIC_LVT_DELIV_MODE) >>
+ IOAPIC_LVT_DELIV_MODE_SHIFT);
+ monitor_printf(mon, "pin %-2u 0x%016"PRIx64" dest=%"PRIx64
+ " vec=%-3"PRIu64" %s %-5s %-6s %-6s %s\n",
+ i, entry,
+ (entry >> IOAPIC_LVT_DEST_SHIFT) &
+ (entry & IOAPIC_LVT_DEST_MODE ? 0xff : 0xf),
+ entry & IOAPIC_VECTOR_MASK,
+ entry & IOAPIC_LVT_POLARITY ? "active-lo" : "active-hi",
+ entry & IOAPIC_LVT_TRIGGER_MODE ? "level" : "edge",
+ entry & IOAPIC_LVT_MASKED ? "masked" : "",
+ delm_str[delm],
+ entry & IOAPIC_LVT_DEST_MODE ? "logical" : "physical");
+
+ remote_irr |= entry & IOAPIC_LVT_TRIGGER_MODE ?
+ (entry & IOAPIC_LVT_REMOTE_IRR ? (1 << i) : 0) : 0;
+ }
+ ioapic_irr_dump(mon, "IRR", s->irr);
+ ioapic_irr_dump(mon, "Remote IRR", remote_irr);
+}
+
void ioapic_reset_common(DeviceState *dev)
{
IOAPICCommonState *s = IOAPIC_COMMON(dev);
diff --git a/qemu/hw/intc/lm32_pic.c b/qemu/hw/intc/lm32_pic.c
index 641ee4727..edc08f184 100644
--- a/qemu/hw/intc/lm32_pic.c
+++ b/qemu/hw/intc/lm32_pic.c
@@ -17,7 +17,7 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
-#include <assert.h>
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
diff --git a/qemu/hw/intc/omap_intc.c b/qemu/hw/intc/omap_intc.c
index e9b38a3c6..336882510 100644
--- a/qemu/hw/intc/omap_intc.c
+++ b/qemu/hw/intc/omap_intc.c
@@ -17,9 +17,11 @@
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/arm/omap.h"
#include "hw/sysbus.h"
+#include "qemu/error-report.h"
/* Interrupt Handlers */
struct omap_intr_handler_bank_s {
@@ -367,7 +369,8 @@ static int omap_intc_init(SysBusDevice *sbd)
struct omap_intr_handler_s *s = OMAP_INTC(dev);
if (!s->iclk) {
- hw_error("omap-intc: clk not connected\n");
+ error_report("omap-intc: clk not connected");
+ return -1;
}
s->nbanks = 1;
sysbus_init_irq(sbd, &s->parent_intr[0]);
@@ -608,10 +611,12 @@ static int omap2_intc_init(SysBusDevice *sbd)
struct omap_intr_handler_s *s = OMAP_INTC(dev);
if (!s->iclk) {
- hw_error("omap2-intc: iclk not connected\n");
+ error_report("omap2-intc: iclk not connected");
+ return -1;
}
if (!s->fclk) {
- hw_error("omap2-intc: fclk not connected\n");
+ error_report("omap2-intc: fclk not connected");
+ return -1;
}
s->level_only = 1;
s->nbanks = 3;
diff --git a/qemu/hw/intc/openpic.c b/qemu/hw/intc/openpic.c
index 14ab0e31b..2d3769310 100644
--- a/qemu/hw/intc/openpic.c
+++ b/qemu/hw/intc/openpic.c
@@ -33,6 +33,7 @@
* Serial interrupts, as implemented in Raven chipset are not supported yet.
*
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/ppc/mac.h"
#include "hw/pci/pci.h"
@@ -40,6 +41,7 @@
#include "hw/ppc/ppc_e500.h"
#include "hw/sysbus.h"
#include "hw/pci/msi.h"
+#include "qapi/error.h"
#include "qemu/bitops.h"
#include "qapi/qmp/qerror.h"
@@ -1374,7 +1376,7 @@ static void fsl_common_init(OpenPICState *opp)
opp->irq_msi = 224;
- msi_supported = true;
+ msi_nonbroken = true;
for (i = 0; i < opp->fsl->max_ext; i++) {
opp->src[i].level = false;
}
@@ -1643,6 +1645,7 @@ static void openpic_class_init(ObjectClass *oc, void *data)
dc->props = openpic_properties;
dc->reset = openpic_reset;
dc->vmsd = &vmstate_openpic;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
}
static const TypeInfo openpic_info = {
diff --git a/qemu/hw/intc/openpic_kvm.c b/qemu/hw/intc/openpic_kvm.c
index f7cac585a..e47e94f2c 100644
--- a/qemu/hw/intc/openpic_kvm.c
+++ b/qemu/hw/intc/openpic_kvm.c
@@ -22,6 +22,8 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include <sys/ioctl.h>
#include "exec/address-spaces.h"
#include "hw/hw.h"
@@ -238,7 +240,7 @@ static void kvm_openpic_realize(DeviceState *dev, Error **errp)
memory_listener_register(&opp->mem_listener, &address_space_memory);
/* indicate pic capabilities */
- msi_supported = true;
+ msi_nonbroken = true;
kvm_kernel_irqchip = true;
kvm_async_interrupts_allowed = true;
@@ -275,6 +277,7 @@ static void kvm_openpic_class_init(ObjectClass *oc, void *data)
dc->realize = kvm_openpic_realize;
dc->props = kvm_openpic_properties;
dc->reset = kvm_openpic_reset;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
}
static const TypeInfo kvm_openpic_info = {
diff --git a/qemu/hw/intc/pl190.c b/qemu/hw/intc/pl190.c
index 2bf359a76..5ecbc4a48 100644
--- a/qemu/hw/intc/pl190.c
+++ b/qemu/hw/intc/pl190.c
@@ -7,6 +7,7 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
/* The number of virtual priority levels. 16 user vectors plus the
diff --git a/qemu/hw/intc/puv3_intc.c b/qemu/hw/intc/puv3_intc.c
index c2803d07d..ef8488aac 100644
--- a/qemu/hw/intc/puv3_intc.c
+++ b/qemu/hw/intc/puv3_intc.c
@@ -8,6 +8,7 @@
* published by the Free Software Foundation, or any later version.
* See the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#undef DEBUG_PUV3
diff --git a/qemu/hw/intc/realview_gic.c b/qemu/hw/intc/realview_gic.c
index 6c812961c..50bbab66e 100644
--- a/qemu/hw/intc/realview_gic.c
+++ b/qemu/hw/intc/realview_gic.c
@@ -7,6 +7,8 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/intc/realview_gic.h"
static void realview_gic_set_irq(void *opaque, int irq, int level)
diff --git a/qemu/hw/intc/s390_flic.c b/qemu/hw/intc/s390_flic.c
index 02e10b750..bc75fa7d9 100644
--- a/qemu/hw/intc/s390_flic.c
+++ b/qemu/hw/intc/s390_flic.c
@@ -10,6 +10,7 @@
* directory.
*/
+#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "hw/sysbus.h"
#include "migration/qemu-file.h"
diff --git a/qemu/hw/intc/s390_flic_kvm.c b/qemu/hw/intc/s390_flic_kvm.c
index b471e7a41..02449b390 100644
--- a/qemu/hw/intc/s390_flic_kvm.c
+++ b/qemu/hw/intc/s390_flic_kvm.c
@@ -10,6 +10,7 @@
* directory.
*/
+#include "qemu/osdep.h"
#include <sys/ioctl.h>
#include "qemu/error-report.h"
#include "hw/sysbus.h"
@@ -228,6 +229,8 @@ static int kvm_s390_add_adapter_routes(S390FLICState *fs,
routes->gsi[i] = ret;
routes->adapter.ind_offset++;
}
+ kvm_irqchip_commit_routes(kvm_state);
+
/* Restore passed-in structure to original state. */
routes->adapter.ind_offset = ind_offset;
return 0;
diff --git a/qemu/hw/intc/sh_intc.c b/qemu/hw/intc/sh_intc.c
index 55c76e4af..6ce2a8084 100644
--- a/qemu/hw/intc/sh_intc.c
+++ b/qemu/hw/intc/sh_intc.c
@@ -8,6 +8,9 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/sh4/sh_intc.h"
#include "hw/hw.h"
#include "hw/sh4/sh.h"
diff --git a/qemu/hw/intc/slavio_intctl.c b/qemu/hw/intc/slavio_intctl.c
index f22aba031..c9486ed99 100644
--- a/qemu/hw/intc/slavio_intctl.c
+++ b/qemu/hw/intc/slavio_intctl.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sparc/sun4m.h"
#include "monitor/monitor.h"
#include "hw/sysbus.h"
diff --git a/qemu/hw/intc/vgic_common.h b/qemu/hw/intc/vgic_common.h
new file mode 100644
index 000000000..80d919eb9
--- /dev/null
+++ b/qemu/hw/intc/vgic_common.h
@@ -0,0 +1,35 @@
+/*
+ * ARM KVM vGIC utility functions
+ *
+ * Copyright (c) 2015 Samsung Electronics
+ * Written by Pavel Fedin
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QEMU_ARM_VGIC_COMMON_H
+#define QEMU_ARM_VGIC_COMMON_H
+
+/**
+ * kvm_arm_gic_set_irq - Send an IRQ to the in-kernel vGIC
+ * @num_irq: Total number of IRQs configured for the GIC instance
+ * @irq: qemu internal IRQ line number:
+ * [0..N-1] : external interrupts
+ * [N..N+31] : PPI (internal) interrupts for CPU 0
+ * [N+32..N+63] : PPI (internal interrupts for CPU 1
+ * @level: level of the IRQ line.
+ */
+void kvm_arm_gic_set_irq(uint32_t num_irq, int irq, int level);
+
+#endif
diff --git a/qemu/hw/intc/xics.c b/qemu/hw/intc/xics.c
index 924b1ae3c..8659be017 100644
--- a/qemu/hw/intc/xics.c
+++ b/qemu/hw/intc/xics.c
@@ -25,6 +25,10 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "trace.h"
#include "qemu/timer.h"
@@ -88,24 +92,24 @@ static void xics_common_reset(DeviceState *d)
device_reset(DEVICE(icp->ics));
}
-static void xics_prop_get_nr_irqs(Object *obj, Visitor *v,
- void *opaque, const char *name, Error **errp)
+static void xics_prop_get_nr_irqs(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
XICSState *icp = XICS_COMMON(obj);
int64_t value = icp->nr_irqs;
- visit_type_int(v, &value, name, errp);
+ visit_type_int(v, name, &value, errp);
}
-static void xics_prop_set_nr_irqs(Object *obj, Visitor *v,
- void *opaque, const char *name, Error **errp)
+static void xics_prop_set_nr_irqs(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
XICSState *icp = XICS_COMMON(obj);
XICSStateClass *info = XICS_COMMON_GET_CLASS(icp);
Error *error = NULL;
int64_t value;
- visit_type_int(v, &value, name, &error);
+ visit_type_int(v, name, &value, &error);
if (error) {
error_propagate(errp, error);
return;
@@ -122,17 +126,17 @@ static void xics_prop_set_nr_irqs(Object *obj, Visitor *v,
}
static void xics_prop_get_nr_servers(Object *obj, Visitor *v,
- void *opaque, const char *name,
+ const char *name, void *opaque,
Error **errp)
{
XICSState *icp = XICS_COMMON(obj);
int64_t value = icp->nr_servers;
- visit_type_int(v, &value, name, errp);
+ visit_type_int(v, name, &value, errp);
}
static void xics_prop_set_nr_servers(Object *obj, Visitor *v,
- void *opaque, const char *name,
+ const char *name, void *opaque,
Error **errp)
{
XICSState *icp = XICS_COMMON(obj);
@@ -140,7 +144,7 @@ static void xics_prop_set_nr_servers(Object *obj, Visitor *v,
Error *error = NULL;
int64_t value;
- visit_type_int(v, &value, name, &error);
+ visit_type_int(v, name, &value, &error);
if (error) {
error_propagate(errp, error);
return;
@@ -711,7 +715,7 @@ static int ics_find_free_block(ICSState *ics, int num, int alignnum)
return -1;
}
-int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi)
+int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi, Error **errp)
{
ICSState *ics = &icp->ics[src];
int irq;
@@ -719,14 +723,14 @@ int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi)
if (irq_hint) {
assert(src == xics_find_source(icp, irq_hint));
if (!ICS_IRQ_FREE(ics, irq_hint - ics->offset)) {
- trace_xics_alloc_failed_hint(src, irq_hint);
+ error_setg(errp, "can't allocate IRQ %d: already in use", irq_hint);
return -1;
}
irq = irq_hint;
} else {
irq = ics_find_free_block(ics, 1, 1);
if (irq < 0) {
- trace_xics_alloc_failed_no_left(src);
+ error_setg(errp, "can't allocate IRQ: no IRQ left");
return -1;
}
irq += ics->offset;
@@ -739,10 +743,11 @@ int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi)
}
/*
- * Allocate block of consequtive IRQs, returns a number of the first.
+ * Allocate block of consecutive IRQs, and return the number of the first IRQ in the block.
* If align==true, aligns the first IRQ number to num.
*/
-int xics_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align)
+int xics_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align,
+ Error **errp)
{
int i, first = -1;
ICSState *ics = &icp->ics[src];
@@ -762,6 +767,10 @@ int xics_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align)
} else {
first = ics_find_free_block(ics, num, 1);
}
+ if (first < 0) {
+ error_setg(errp, "can't find a free %d-IRQ block", num);
+ return -1;
+ }
if (first >= 0) {
for (i = first; i < first + num; ++i) {
@@ -848,7 +857,7 @@ static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t xirr = icp_accept(ss);
args[0] = xirr;
- args[1] = cpu_get_real_ticks();
+ args[1] = cpu_get_host_ticks();
return H_SUCCESS;
}
diff --git a/qemu/hw/intc/xics_kvm.c b/qemu/hw/intc/xics_kvm.c
index d58729cfa..9029d9ee0 100644
--- a/qemu/hw/intc/xics_kvm.c
+++ b/qemu/hw/intc/xics_kvm.c
@@ -25,6 +25,10 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "trace.h"
#include "hw/ppc/spapr.h"
diff --git a/qemu/hw/intc/xilinx_intc.c b/qemu/hw/intc/xilinx_intc.c
index 12804ab72..9d8139bc6 100644
--- a/qemu/hw/intc/xilinx_intc.c
+++ b/qemu/hw/intc/xilinx_intc.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/hw.h"
diff --git a/qemu/hw/ipack/ipack.c b/qemu/hw/ipack/ipack.c
index 59bfe2869..5f99ed9a7 100644
--- a/qemu/hw/ipack/ipack.c
+++ b/qemu/hw/ipack/ipack.c
@@ -8,6 +8,8 @@
* later version.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/ipack/ipack.h"
IPackDevice *ipack_device_find(IPackBus *bus, int32_t slot)
diff --git a/qemu/hw/ipack/tpci200.c b/qemu/hw/ipack/tpci200.c
index 1df02ee85..fdda6f414 100644
--- a/qemu/hw/ipack/tpci200.c
+++ b/qemu/hw/ipack/tpci200.c
@@ -8,10 +8,10 @@
* later version.
*/
+#include "qemu/osdep.h"
#include "hw/ipack/ipack.h"
#include "hw/pci/pci.h"
#include "qemu/bitops.h"
-#include <stdio.h>
/* #define DEBUG_TPCI */
diff --git a/qemu/hw/ipmi/Makefile.objs b/qemu/hw/ipmi/Makefile.objs
new file mode 100644
index 000000000..a90318d5b
--- /dev/null
+++ b/qemu/hw/ipmi/Makefile.objs
@@ -0,0 +1,5 @@
+common-obj-$(CONFIG_IPMI) += ipmi.o
+common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_sim.o
+common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_extern.o
+common-obj-$(CONFIG_ISA_IPMI_KCS) += isa_ipmi_kcs.o
+common-obj-$(CONFIG_ISA_IPMI_BT) += isa_ipmi_bt.o
diff --git a/qemu/hw/ipmi/ipmi.c b/qemu/hw/ipmi/ipmi.c
new file mode 100644
index 000000000..6adec1e99
--- /dev/null
+++ b/qemu/hw/ipmi/ipmi.c
@@ -0,0 +1,151 @@
+/*
+ * QEMU IPMI emulation
+ *
+ * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
+ *
+ * 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/hw.h"
+#include "hw/ipmi/ipmi.h"
+#include "sysemu/sysemu.h"
+#include "qmp-commands.h"
+#include "qom/object_interfaces.h"
+#include "qapi/visitor.h"
+
+static int ipmi_do_hw_op(IPMIInterface *s, enum ipmi_op op, int checkonly)
+{
+ switch (op) {
+ case IPMI_RESET_CHASSIS:
+ if (checkonly) {
+ return 0;
+ }
+ qemu_system_reset_request();
+ return 0;
+
+ case IPMI_POWEROFF_CHASSIS:
+ if (checkonly) {
+ return 0;
+ }
+ qemu_system_powerdown_request();
+ return 0;
+
+ case IPMI_SEND_NMI:
+ if (checkonly) {
+ return 0;
+ }
+ qmp_inject_nmi(NULL);
+ return 0;
+
+ case IPMI_POWERCYCLE_CHASSIS:
+ case IPMI_PULSE_DIAG_IRQ:
+ case IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP:
+ case IPMI_POWERON_CHASSIS:
+ default:
+ return IPMI_CC_COMMAND_NOT_SUPPORTED;
+ }
+}
+
+static void ipmi_interface_class_init(ObjectClass *class, void *data)
+{
+ IPMIInterfaceClass *ik = IPMI_INTERFACE_CLASS(class);
+
+ ik->do_hw_op = ipmi_do_hw_op;
+}
+
+static TypeInfo ipmi_interface_type_info = {
+ .name = TYPE_IPMI_INTERFACE,
+ .parent = TYPE_INTERFACE,
+ .class_size = sizeof(IPMIInterfaceClass),
+ .class_init = ipmi_interface_class_init,
+};
+
+static void isa_ipmi_bmc_check(Object *obj, const char *name,
+ Object *val, Error **errp)
+{
+ IPMIBmc *bmc = IPMI_BMC(val);
+
+ if (bmc->intf)
+ error_setg(errp, "BMC object is already in use");
+}
+
+void ipmi_bmc_find_and_link(Object *obj, Object **bmc)
+{
+ object_property_add_link(obj, "bmc", TYPE_IPMI_BMC, bmc,
+ isa_ipmi_bmc_check,
+ OBJ_PROP_LINK_UNREF_ON_RELEASE,
+ &error_abort);
+}
+
+static Property ipmi_bmc_properties[] = {
+ DEFINE_PROP_UINT8("slave_addr", IPMIBmc, slave_addr, 0x20),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void bmc_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->props = ipmi_bmc_properties;
+}
+
+static TypeInfo ipmi_bmc_type_info = {
+ .name = TYPE_IPMI_BMC,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(IPMIBmc),
+ .abstract = true,
+ .class_size = sizeof(IPMIBmcClass),
+ .class_init = bmc_class_init,
+};
+
+static void ipmi_register_types(void)
+{
+ type_register_static(&ipmi_interface_type_info);
+ type_register_static(&ipmi_bmc_type_info);
+}
+
+type_init(ipmi_register_types)
+
+static IPMIFwInfo *ipmi_fw_info;
+static unsigned int ipmi_fw_info_len;
+
+static uint32_t current_uuid = 1;
+
+void ipmi_add_fwinfo(IPMIFwInfo *info, Error **errp)
+{
+ info->uuid = current_uuid++;
+ ipmi_fw_info = g_realloc(ipmi_fw_info,
+ sizeof(*ipmi_fw_info) * (ipmi_fw_info_len + 1));
+ ipmi_fw_info[ipmi_fw_info_len] = *info;
+}
+
+IPMIFwInfo *ipmi_first_fwinfo(void)
+{
+ return ipmi_fw_info;
+}
+
+IPMIFwInfo *ipmi_next_fwinfo(IPMIFwInfo *current)
+{
+ current++;
+ if (current >= &ipmi_fw_info[ipmi_fw_info_len]) {
+ return NULL;
+ }
+ return current;
+}
diff --git a/qemu/hw/ipmi/ipmi_bmc_extern.c b/qemu/hw/ipmi/ipmi_bmc_extern.c
new file mode 100644
index 000000000..fe12112a2
--- /dev/null
+++ b/qemu/hw/ipmi/ipmi_bmc_extern.c
@@ -0,0 +1,519 @@
+/*
+ * IPMI BMC external connection
+ *
+ * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
+ *
+ * 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.
+ */
+
+/*
+ * This is designed to connect with OpenIPMI's lanserv serial interface
+ * using the "VM" connection type. See that for details.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/timer.h"
+#include "sysemu/char.h"
+#include "sysemu/sysemu.h"
+#include "hw/ipmi/ipmi.h"
+
+#define VM_MSG_CHAR 0xA0 /* Marks end of message */
+#define VM_CMD_CHAR 0xA1 /* Marks end of a command */
+#define VM_ESCAPE_CHAR 0xAA /* Set bit 4 from the next byte to 0 */
+
+#define VM_PROTOCOL_VERSION 1
+#define VM_CMD_VERSION 0xff /* A version number byte follows */
+#define VM_CMD_NOATTN 0x00
+#define VM_CMD_ATTN 0x01
+#define VM_CMD_ATTN_IRQ 0x02
+#define VM_CMD_POWEROFF 0x03
+#define VM_CMD_RESET 0x04
+#define VM_CMD_ENABLE_IRQ 0x05 /* Enable/disable the messaging irq */
+#define VM_CMD_DISABLE_IRQ 0x06
+#define VM_CMD_SEND_NMI 0x07
+#define VM_CMD_CAPABILITIES 0x08
+#define VM_CAPABILITIES_POWER 0x01
+#define VM_CAPABILITIES_RESET 0x02
+#define VM_CAPABILITIES_IRQ 0x04
+#define VM_CAPABILITIES_NMI 0x08
+#define VM_CAPABILITIES_ATTN 0x10
+#define VM_CMD_FORCEOFF 0x09
+
+#define TYPE_IPMI_BMC_EXTERN "ipmi-bmc-extern"
+#define IPMI_BMC_EXTERN(obj) OBJECT_CHECK(IPMIBmcExtern, (obj), \
+ TYPE_IPMI_BMC_EXTERN)
+typedef struct IPMIBmcExtern {
+ IPMIBmc parent;
+
+ CharDriverState *chr;
+
+ bool connected;
+
+ unsigned char inbuf[MAX_IPMI_MSG_SIZE + 2];
+ unsigned int inpos;
+ bool in_escape;
+ bool in_too_many;
+ bool waiting_rsp;
+ bool sending_cmd;
+
+ unsigned char outbuf[(MAX_IPMI_MSG_SIZE + 2) * 2 + 1];
+ unsigned int outpos;
+ unsigned int outlen;
+
+ struct QEMUTimer *extern_timer;
+
+ /* A reset event is pending to be sent upstream. */
+ bool send_reset;
+} IPMIBmcExtern;
+
+static int can_receive(void *opaque);
+static void receive(void *opaque, const uint8_t *buf, int size);
+static void chr_event(void *opaque, int event);
+
+static unsigned char
+ipmb_checksum(const unsigned char *data, int size, unsigned char start)
+{
+ unsigned char csum = start;
+
+ for (; size > 0; size--, data++) {
+ csum += *data;
+ }
+ return csum;
+}
+
+static void continue_send(IPMIBmcExtern *ibe)
+{
+ if (ibe->outlen == 0) {
+ goto check_reset;
+ }
+ send:
+ ibe->outpos += qemu_chr_fe_write(ibe->chr, ibe->outbuf + ibe->outpos,
+ ibe->outlen - ibe->outpos);
+ if (ibe->outpos < ibe->outlen) {
+ /* Not fully transmitted, try again in a 10ms */
+ timer_mod_ns(ibe->extern_timer,
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 10000000);
+ } else {
+ /* Sent */
+ ibe->outlen = 0;
+ ibe->outpos = 0;
+ if (!ibe->sending_cmd) {
+ ibe->waiting_rsp = true;
+ } else {
+ ibe->sending_cmd = false;
+ }
+ check_reset:
+ if (ibe->connected && ibe->send_reset) {
+ /* Send the reset */
+ ibe->outbuf[0] = VM_CMD_RESET;
+ ibe->outbuf[1] = VM_CMD_CHAR;
+ ibe->outlen = 2;
+ ibe->outpos = 0;
+ ibe->send_reset = false;
+ ibe->sending_cmd = true;
+ goto send;
+ }
+
+ if (ibe->waiting_rsp) {
+ /* Make sure we get a response within 4 seconds. */
+ timer_mod_ns(ibe->extern_timer,
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 4000000000ULL);
+ }
+ }
+ return;
+}
+
+static void extern_timeout(void *opaque)
+{
+ IPMIBmcExtern *ibe = opaque;
+ IPMIInterface *s = ibe->parent.intf;
+
+ if (ibe->connected) {
+ if (ibe->waiting_rsp && (ibe->outlen == 0)) {
+ IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+ /* The message response timed out, return an error. */
+ ibe->waiting_rsp = false;
+ ibe->inbuf[1] = ibe->outbuf[1] | 0x04;
+ ibe->inbuf[2] = ibe->outbuf[2];
+ ibe->inbuf[3] = IPMI_CC_TIMEOUT;
+ k->handle_rsp(s, ibe->outbuf[0], ibe->inbuf + 1, 3);
+ } else {
+ continue_send(ibe);
+ }
+ }
+}
+
+static void addchar(IPMIBmcExtern *ibe, unsigned char ch)
+{
+ switch (ch) {
+ case VM_MSG_CHAR:
+ case VM_CMD_CHAR:
+ case VM_ESCAPE_CHAR:
+ ibe->outbuf[ibe->outlen] = VM_ESCAPE_CHAR;
+ ibe->outlen++;
+ ch |= 0x10;
+ /* No break */
+
+ default:
+ ibe->outbuf[ibe->outlen] = ch;
+ ibe->outlen++;
+ }
+}
+
+static void ipmi_bmc_extern_handle_command(IPMIBmc *b,
+ uint8_t *cmd, unsigned int cmd_len,
+ unsigned int max_cmd_len,
+ uint8_t msg_id)
+{
+ IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(b);
+ IPMIInterface *s = ibe->parent.intf;
+ uint8_t err = 0, csum;
+ unsigned int i;
+
+ if (ibe->outlen) {
+ /* We already have a command queued. Shouldn't ever happen. */
+ fprintf(stderr, "IPMI KCS: Got command when not finished with the"
+ " previous commmand\n");
+ abort();
+ }
+
+ /* If it's too short or it was truncated, return an error. */
+ if (cmd_len < 2) {
+ err = IPMI_CC_REQUEST_DATA_LENGTH_INVALID;
+ } else if ((cmd_len > max_cmd_len) || (cmd_len > MAX_IPMI_MSG_SIZE)) {
+ err = IPMI_CC_REQUEST_DATA_TRUNCATED;
+ } else if (!ibe->connected) {
+ err = IPMI_CC_BMC_INIT_IN_PROGRESS;
+ }
+ if (err) {
+ IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+ unsigned char rsp[3];
+ rsp[0] = cmd[0] | 0x04;
+ rsp[1] = cmd[1];
+ rsp[2] = err;
+ ibe->waiting_rsp = false;
+ k->handle_rsp(s, msg_id, rsp, 3);
+ goto out;
+ }
+
+ addchar(ibe, msg_id);
+ for (i = 0; i < cmd_len; i++) {
+ addchar(ibe, cmd[i]);
+ }
+ csum = ipmb_checksum(&msg_id, 1, 0);
+ addchar(ibe, -ipmb_checksum(cmd, cmd_len, csum));
+
+ ibe->outbuf[ibe->outlen] = VM_MSG_CHAR;
+ ibe->outlen++;
+
+ /* Start the transmit */
+ continue_send(ibe);
+
+ out:
+ return;
+}
+
+static void handle_hw_op(IPMIBmcExtern *ibe, unsigned char hw_op)
+{
+ IPMIInterface *s = ibe->parent.intf;
+ IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+
+ switch (hw_op) {
+ case VM_CMD_VERSION:
+ /* We only support one version at this time. */
+ break;
+
+ case VM_CMD_NOATTN:
+ k->set_atn(s, 0, 0);
+ break;
+
+ case VM_CMD_ATTN:
+ k->set_atn(s, 1, 0);
+ break;
+
+ case VM_CMD_ATTN_IRQ:
+ k->set_atn(s, 1, 1);
+ break;
+
+ case VM_CMD_POWEROFF:
+ k->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 0);
+ break;
+
+ case VM_CMD_RESET:
+ k->do_hw_op(s, IPMI_RESET_CHASSIS, 0);
+ break;
+
+ case VM_CMD_ENABLE_IRQ:
+ k->set_irq_enable(s, 1);
+ break;
+
+ case VM_CMD_DISABLE_IRQ:
+ k->set_irq_enable(s, 0);
+ break;
+
+ case VM_CMD_SEND_NMI:
+ k->do_hw_op(s, IPMI_SEND_NMI, 0);
+ break;
+
+ case VM_CMD_FORCEOFF:
+ qemu_system_shutdown_request();
+ break;
+ }
+}
+
+static void handle_msg(IPMIBmcExtern *ibe)
+{
+ IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(ibe->parent.intf);
+
+ if (ibe->in_escape) {
+ ipmi_debug("msg escape not ended\n");
+ return;
+ }
+ if (ibe->inpos < 5) {
+ ipmi_debug("msg too short\n");
+ return;
+ }
+ if (ibe->in_too_many) {
+ ibe->inbuf[3] = IPMI_CC_REQUEST_DATA_TRUNCATED;
+ ibe->inpos = 4;
+ } else if (ipmb_checksum(ibe->inbuf, ibe->inpos, 0) != 0) {
+ ipmi_debug("msg checksum failure\n");
+ return;
+ } else {
+ ibe->inpos--; /* Remove checkum */
+ }
+
+ timer_del(ibe->extern_timer);
+ ibe->waiting_rsp = false;
+ k->handle_rsp(ibe->parent.intf, ibe->inbuf[0], ibe->inbuf + 1, ibe->inpos - 1);
+}
+
+static int can_receive(void *opaque)
+{
+ return 1;
+}
+
+static void receive(void *opaque, const uint8_t *buf, int size)
+{
+ IPMIBmcExtern *ibe = opaque;
+ int i;
+ unsigned char hw_op;
+
+ for (i = 0; i < size; i++) {
+ unsigned char ch = buf[i];
+
+ switch (ch) {
+ case VM_MSG_CHAR:
+ handle_msg(ibe);
+ ibe->in_too_many = false;
+ ibe->inpos = 0;
+ break;
+
+ case VM_CMD_CHAR:
+ if (ibe->in_too_many) {
+ ipmi_debug("cmd in too many\n");
+ ibe->in_too_many = false;
+ ibe->inpos = 0;
+ break;
+ }
+ if (ibe->in_escape) {
+ ipmi_debug("cmd in escape\n");
+ ibe->in_too_many = false;
+ ibe->inpos = 0;
+ ibe->in_escape = false;
+ break;
+ }
+ ibe->in_too_many = false;
+ if (ibe->inpos < 1) {
+ break;
+ }
+ hw_op = ibe->inbuf[0];
+ ibe->inpos = 0;
+ goto out_hw_op;
+ break;
+
+ case VM_ESCAPE_CHAR:
+ ibe->in_escape = true;
+ break;
+
+ default:
+ if (ibe->in_escape) {
+ ch &= ~0x10;
+ ibe->in_escape = false;
+ }
+ if (ibe->in_too_many) {
+ break;
+ }
+ if (ibe->inpos >= sizeof(ibe->inbuf)) {
+ ibe->in_too_many = true;
+ break;
+ }
+ ibe->inbuf[ibe->inpos] = ch;
+ ibe->inpos++;
+ break;
+ }
+ }
+ return;
+
+ out_hw_op:
+ handle_hw_op(ibe, hw_op);
+}
+
+static void chr_event(void *opaque, int event)
+{
+ IPMIBmcExtern *ibe = opaque;
+ IPMIInterface *s = ibe->parent.intf;
+ IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+ unsigned char v;
+
+ switch (event) {
+ case CHR_EVENT_OPENED:
+ ibe->connected = true;
+ ibe->outpos = 0;
+ ibe->outlen = 0;
+ addchar(ibe, VM_CMD_VERSION);
+ addchar(ibe, VM_PROTOCOL_VERSION);
+ ibe->outbuf[ibe->outlen] = VM_CMD_CHAR;
+ ibe->outlen++;
+ addchar(ibe, VM_CMD_CAPABILITIES);
+ v = VM_CAPABILITIES_IRQ | VM_CAPABILITIES_ATTN;
+ if (k->do_hw_op(ibe->parent.intf, IPMI_POWEROFF_CHASSIS, 1) == 0) {
+ v |= VM_CAPABILITIES_POWER;
+ }
+ if (k->do_hw_op(ibe->parent.intf, IPMI_RESET_CHASSIS, 1) == 0) {
+ v |= VM_CAPABILITIES_RESET;
+ }
+ if (k->do_hw_op(ibe->parent.intf, IPMI_SEND_NMI, 1) == 0) {
+ v |= VM_CAPABILITIES_NMI;
+ }
+ addchar(ibe, v);
+ ibe->outbuf[ibe->outlen] = VM_CMD_CHAR;
+ ibe->outlen++;
+ ibe->sending_cmd = false;
+ continue_send(ibe);
+ break;
+
+ case CHR_EVENT_CLOSED:
+ if (!ibe->connected) {
+ return;
+ }
+ ibe->connected = false;
+ if (ibe->waiting_rsp) {
+ ibe->waiting_rsp = false;
+ ibe->inbuf[1] = ibe->outbuf[1] | 0x04;
+ ibe->inbuf[2] = ibe->outbuf[2];
+ ibe->inbuf[3] = IPMI_CC_BMC_INIT_IN_PROGRESS;
+ k->handle_rsp(s, ibe->outbuf[0], ibe->inbuf + 1, 3);
+ }
+ break;
+ }
+}
+
+static void ipmi_bmc_extern_handle_reset(IPMIBmc *b)
+{
+ IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(b);
+
+ ibe->send_reset = true;
+ continue_send(ibe);
+}
+
+static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp)
+{
+ IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev);
+
+ if (!ibe->chr) {
+ error_setg(errp, "IPMI external bmc requires chardev attribute");
+ return;
+ }
+
+ qemu_chr_add_handlers(ibe->chr, can_receive, receive, chr_event, ibe);
+}
+
+static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id)
+{
+ IPMIBmcExtern *ibe = opaque;
+
+ /*
+ * We don't directly restore waiting_rsp, Instead, we return an
+ * error on the interface if a response was being waited for.
+ */
+ if (ibe->waiting_rsp) {
+ IPMIInterface *ii = ibe->parent.intf;
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+
+ ibe->waiting_rsp = false;
+ ibe->inbuf[1] = ibe->outbuf[1] | 0x04;
+ ibe->inbuf[2] = ibe->outbuf[2];
+ ibe->inbuf[3] = IPMI_CC_BMC_INIT_IN_PROGRESS;
+ iic->handle_rsp(ii, ibe->outbuf[0], ibe->inbuf + 1, 3);
+ }
+ return 0;
+}
+
+static const VMStateDescription vmstate_ipmi_bmc_extern = {
+ .name = TYPE_IPMI_BMC_EXTERN,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .post_load = ipmi_bmc_extern_post_migrate,
+ .fields = (VMStateField[]) {
+ VMSTATE_BOOL(send_reset, IPMIBmcExtern),
+ VMSTATE_BOOL(waiting_rsp, IPMIBmcExtern),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void ipmi_bmc_extern_init(Object *obj)
+{
+ IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj);
+
+ ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe);
+ vmstate_register(NULL, 0, &vmstate_ipmi_bmc_extern, ibe);
+}
+
+static Property ipmi_bmc_extern_properties[] = {
+ DEFINE_PROP_CHR("chardev", IPMIBmcExtern, chr),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void ipmi_bmc_extern_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ IPMIBmcClass *bk = IPMI_BMC_CLASS(oc);
+
+ bk->handle_command = ipmi_bmc_extern_handle_command;
+ bk->handle_reset = ipmi_bmc_extern_handle_reset;
+ dc->realize = ipmi_bmc_extern_realize;
+ dc->props = ipmi_bmc_extern_properties;
+}
+
+static const TypeInfo ipmi_bmc_extern_type = {
+ .name = TYPE_IPMI_BMC_EXTERN,
+ .parent = TYPE_IPMI_BMC,
+ .instance_size = sizeof(IPMIBmcExtern),
+ .instance_init = ipmi_bmc_extern_init,
+ .class_init = ipmi_bmc_extern_class_init,
+ };
+
+static void ipmi_bmc_extern_register_types(void)
+{
+ type_register_static(&ipmi_bmc_extern_type);
+}
+
+type_init(ipmi_bmc_extern_register_types)
diff --git a/qemu/hw/ipmi/ipmi_bmc_sim.c b/qemu/hw/ipmi/ipmi_bmc_sim.c
new file mode 100644
index 000000000..dc9c14cd2
--- /dev/null
+++ b/qemu/hw/ipmi/ipmi_bmc_sim.c
@@ -0,0 +1,1810 @@
+/*
+ * IPMI BMC emulation
+ *
+ * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
+ *
+ * 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 "sysemu/sysemu.h"
+#include "qemu/timer.h"
+#include "hw/ipmi/ipmi.h"
+#include "qemu/error-report.h"
+
+#define IPMI_NETFN_CHASSIS 0x00
+
+#define IPMI_CMD_GET_CHASSIS_CAPABILITIES 0x00
+#define IPMI_CMD_GET_CHASSIS_STATUS 0x01
+#define IPMI_CMD_CHASSIS_CONTROL 0x02
+#define IPMI_CMD_GET_SYS_RESTART_CAUSE 0x09
+
+#define IPMI_NETFN_SENSOR_EVENT 0x04
+
+#define IPMI_CMD_SET_SENSOR_EVT_ENABLE 0x28
+#define IPMI_CMD_GET_SENSOR_EVT_ENABLE 0x29
+#define IPMI_CMD_REARM_SENSOR_EVTS 0x2a
+#define IPMI_CMD_GET_SENSOR_EVT_STATUS 0x2b
+#define IPMI_CMD_GET_SENSOR_READING 0x2d
+#define IPMI_CMD_SET_SENSOR_TYPE 0x2e
+#define IPMI_CMD_GET_SENSOR_TYPE 0x2f
+
+/* #define IPMI_NETFN_APP 0x06 In ipmi.h */
+
+#define IPMI_CMD_GET_DEVICE_ID 0x01
+#define IPMI_CMD_COLD_RESET 0x02
+#define IPMI_CMD_WARM_RESET 0x03
+#define IPMI_CMD_SET_ACPI_POWER_STATE 0x06
+#define IPMI_CMD_GET_ACPI_POWER_STATE 0x07
+#define IPMI_CMD_GET_DEVICE_GUID 0x08
+#define IPMI_CMD_RESET_WATCHDOG_TIMER 0x22
+#define IPMI_CMD_SET_WATCHDOG_TIMER 0x24
+#define IPMI_CMD_GET_WATCHDOG_TIMER 0x25
+#define IPMI_CMD_SET_BMC_GLOBAL_ENABLES 0x2e
+#define IPMI_CMD_GET_BMC_GLOBAL_ENABLES 0x2f
+#define IPMI_CMD_CLR_MSG_FLAGS 0x30
+#define IPMI_CMD_GET_MSG_FLAGS 0x31
+#define IPMI_CMD_GET_MSG 0x33
+#define IPMI_CMD_SEND_MSG 0x34
+#define IPMI_CMD_READ_EVT_MSG_BUF 0x35
+
+#define IPMI_NETFN_STORAGE 0x0a
+
+#define IPMI_CMD_GET_SDR_REP_INFO 0x20
+#define IPMI_CMD_GET_SDR_REP_ALLOC_INFO 0x21
+#define IPMI_CMD_RESERVE_SDR_REP 0x22
+#define IPMI_CMD_GET_SDR 0x23
+#define IPMI_CMD_ADD_SDR 0x24
+#define IPMI_CMD_PARTIAL_ADD_SDR 0x25
+#define IPMI_CMD_DELETE_SDR 0x26
+#define IPMI_CMD_CLEAR_SDR_REP 0x27
+#define IPMI_CMD_GET_SDR_REP_TIME 0x28
+#define IPMI_CMD_SET_SDR_REP_TIME 0x29
+#define IPMI_CMD_ENTER_SDR_REP_UPD_MODE 0x2A
+#define IPMI_CMD_EXIT_SDR_REP_UPD_MODE 0x2B
+#define IPMI_CMD_RUN_INIT_AGENT 0x2C
+#define IPMI_CMD_GET_SEL_INFO 0x40
+#define IPMI_CMD_GET_SEL_ALLOC_INFO 0x41
+#define IPMI_CMD_RESERVE_SEL 0x42
+#define IPMI_CMD_GET_SEL_ENTRY 0x43
+#define IPMI_CMD_ADD_SEL_ENTRY 0x44
+#define IPMI_CMD_PARTIAL_ADD_SEL_ENTRY 0x45
+#define IPMI_CMD_DELETE_SEL_ENTRY 0x46
+#define IPMI_CMD_CLEAR_SEL 0x47
+#define IPMI_CMD_GET_SEL_TIME 0x48
+#define IPMI_CMD_SET_SEL_TIME 0x49
+
+
+/* Same as a timespec struct. */
+struct ipmi_time {
+ long tv_sec;
+ long tv_nsec;
+};
+
+#define MAX_SEL_SIZE 128
+
+typedef struct IPMISel {
+ uint8_t sel[MAX_SEL_SIZE][16];
+ unsigned int next_free;
+ long time_offset;
+ uint16_t reservation;
+ uint8_t last_addition[4];
+ uint8_t last_clear[4];
+ uint8_t overflow;
+} IPMISel;
+
+#define MAX_SDR_SIZE 16384
+
+typedef struct IPMISdr {
+ uint8_t sdr[MAX_SDR_SIZE];
+ unsigned int next_free;
+ uint16_t next_rec_id;
+ uint16_t reservation;
+ uint8_t last_addition[4];
+ uint8_t last_clear[4];
+ uint8_t overflow;
+} IPMISdr;
+
+typedef struct IPMISensor {
+ uint8_t status;
+ uint8_t reading;
+ uint16_t states_suppt;
+ uint16_t assert_suppt;
+ uint16_t deassert_suppt;
+ uint16_t states;
+ uint16_t assert_states;
+ uint16_t deassert_states;
+ uint16_t assert_enable;
+ uint16_t deassert_enable;
+ uint8_t sensor_type;
+ uint8_t evt_reading_type_code;
+} IPMISensor;
+#define IPMI_SENSOR_GET_PRESENT(s) ((s)->status & 0x01)
+#define IPMI_SENSOR_SET_PRESENT(s, v) ((s)->status = (s->status & ~0x01) | \
+ !!(v))
+#define IPMI_SENSOR_GET_SCAN_ON(s) ((s)->status & 0x40)
+#define IPMI_SENSOR_SET_SCAN_ON(s, v) ((s)->status = (s->status & ~0x40) | \
+ ((!!(v)) << 6))
+#define IPMI_SENSOR_GET_EVENTS_ON(s) ((s)->status & 0x80)
+#define IPMI_SENSOR_SET_EVENTS_ON(s, v) ((s)->status = (s->status & ~0x80) | \
+ ((!!(v)) << 7))
+#define IPMI_SENSOR_GET_RET_STATUS(s) ((s)->status & 0xc0)
+#define IPMI_SENSOR_SET_RET_STATUS(s, v) ((s)->status = (s->status & ~0xc0) | \
+ (v & 0xc0))
+#define IPMI_SENSOR_IS_DISCRETE(s) ((s)->evt_reading_type_code != 1)
+
+#define MAX_SENSORS 20
+#define IPMI_WATCHDOG_SENSOR 0
+
+typedef struct IPMIBmcSim IPMIBmcSim;
+typedef struct RspBuffer RspBuffer;
+
+#define MAX_NETFNS 64
+
+typedef struct IPMICmdHandler {
+ void (*cmd_handler)(IPMIBmcSim *s,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp);
+ unsigned int cmd_len_min;
+} IPMICmdHandler;
+
+typedef struct IPMINetfn {
+ unsigned int cmd_nums;
+ const IPMICmdHandler *cmd_handlers;
+} IPMINetfn;
+
+typedef struct IPMIRcvBufEntry {
+ QTAILQ_ENTRY(IPMIRcvBufEntry) entry;
+ uint8_t len;
+ uint8_t buf[MAX_IPMI_MSG_SIZE];
+} IPMIRcvBufEntry;
+
+#define TYPE_IPMI_BMC_SIMULATOR "ipmi-bmc-sim"
+#define IPMI_BMC_SIMULATOR(obj) OBJECT_CHECK(IPMIBmcSim, (obj), \
+ TYPE_IPMI_BMC_SIMULATOR)
+struct IPMIBmcSim {
+ IPMIBmc parent;
+
+ QEMUTimer *timer;
+
+ uint8_t bmc_global_enables;
+ uint8_t msg_flags;
+
+ bool watchdog_initialized;
+ uint8_t watchdog_use;
+ uint8_t watchdog_action;
+ uint8_t watchdog_pretimeout; /* In seconds */
+ bool watchdog_expired;
+ uint16_t watchdog_timeout; /* in 100's of milliseconds */
+
+ bool watchdog_running;
+ bool watchdog_preaction_ran;
+ int64_t watchdog_expiry;
+
+ uint8_t device_id;
+ uint8_t ipmi_version;
+ uint8_t device_rev;
+ uint8_t fwrev1;
+ uint8_t fwrev2;
+ uint8_t mfg_id[3];
+ uint8_t product_id[2];
+
+ uint8_t restart_cause;
+
+ uint8_t acpi_power_state[2];
+ uint8_t uuid[16];
+
+ IPMISel sel;
+ IPMISdr sdr;
+ IPMISensor sensors[MAX_SENSORS];
+
+ /* Odd netfns are for responses, so we only need the even ones. */
+ const IPMINetfn *netfns[MAX_NETFNS / 2];
+
+ QemuMutex lock;
+ /* We allow one event in the buffer */
+ uint8_t evtbuf[16];
+
+ QTAILQ_HEAD(, IPMIRcvBufEntry) rcvbufs;
+};
+
+#define IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK (1 << 3)
+#define IPMI_BMC_MSG_FLAG_EVT_BUF_FULL (1 << 1)
+#define IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE (1 << 0)
+#define IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK_SET(s) \
+ (IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK & (s)->msg_flags)
+#define IPMI_BMC_MSG_FLAG_EVT_BUF_FULL_SET(s) \
+ (IPMI_BMC_MSG_FLAG_EVT_BUF_FULL & (s)->msg_flags)
+#define IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE_SET(s) \
+ (IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE & (s)->msg_flags)
+
+#define IPMI_BMC_RCV_MSG_QUEUE_INT_BIT 0
+#define IPMI_BMC_EVBUF_FULL_INT_BIT 1
+#define IPMI_BMC_EVENT_MSG_BUF_BIT 2
+#define IPMI_BMC_EVENT_LOG_BIT 3
+#define IPMI_BMC_MSG_INTS_ON(s) ((s)->bmc_global_enables & \
+ (1 << IPMI_BMC_RCV_MSG_QUEUE_INT_BIT))
+#define IPMI_BMC_EVBUF_FULL_INT_ENABLED(s) ((s)->bmc_global_enables & \
+ (1 << IPMI_BMC_EVBUF_FULL_INT_BIT))
+#define IPMI_BMC_EVENT_LOG_ENABLED(s) ((s)->bmc_global_enables & \
+ (1 << IPMI_BMC_EVENT_LOG_BIT))
+#define IPMI_BMC_EVENT_MSG_BUF_ENABLED(s) ((s)->bmc_global_enables & \
+ (1 << IPMI_BMC_EVENT_MSG_BUF_BIT))
+
+#define IPMI_BMC_WATCHDOG_USE_MASK 0xc7
+#define IPMI_BMC_WATCHDOG_ACTION_MASK 0x77
+#define IPMI_BMC_WATCHDOG_GET_USE(s) ((s)->watchdog_use & 0x7)
+#define IPMI_BMC_WATCHDOG_GET_DONT_LOG(s) (((s)->watchdog_use >> 7) & 0x1)
+#define IPMI_BMC_WATCHDOG_GET_DONT_STOP(s) (((s)->watchdog_use >> 6) & 0x1)
+#define IPMI_BMC_WATCHDOG_GET_PRE_ACTION(s) (((s)->watchdog_action >> 4) & 0x7)
+#define IPMI_BMC_WATCHDOG_PRE_NONE 0
+#define IPMI_BMC_WATCHDOG_PRE_SMI 1
+#define IPMI_BMC_WATCHDOG_PRE_NMI 2
+#define IPMI_BMC_WATCHDOG_PRE_MSG_INT 3
+#define IPMI_BMC_WATCHDOG_GET_ACTION(s) ((s)->watchdog_action & 0x7)
+#define IPMI_BMC_WATCHDOG_ACTION_NONE 0
+#define IPMI_BMC_WATCHDOG_ACTION_RESET 1
+#define IPMI_BMC_WATCHDOG_ACTION_POWER_DOWN 2
+#define IPMI_BMC_WATCHDOG_ACTION_POWER_CYCLE 3
+
+struct RspBuffer {
+ uint8_t buffer[MAX_IPMI_MSG_SIZE];
+ unsigned int len;
+};
+
+#define RSP_BUFFER_INITIALIZER { }
+
+static inline void rsp_buffer_set_error(RspBuffer *rsp, uint8_t byte)
+{
+ rsp->buffer[2] = byte;
+}
+
+/* Add a byte to the response. */
+static inline void rsp_buffer_push(RspBuffer *rsp, uint8_t byte)
+{
+ if (rsp->len >= sizeof(rsp->buffer)) {
+ rsp_buffer_set_error(rsp, IPMI_CC_REQUEST_DATA_TRUNCATED);
+ return;
+ }
+ rsp->buffer[rsp->len++] = byte;
+}
+
+static inline void rsp_buffer_pushmore(RspBuffer *rsp, uint8_t *bytes,
+ unsigned int n)
+{
+ if (rsp->len + n >= sizeof(rsp->buffer)) {
+ rsp_buffer_set_error(rsp, IPMI_CC_REQUEST_DATA_TRUNCATED);
+ return;
+ }
+
+ memcpy(&rsp->buffer[rsp->len], bytes, n);
+ rsp->len += n;
+}
+
+static void ipmi_sim_handle_timeout(IPMIBmcSim *ibs);
+
+static void ipmi_gettime(struct ipmi_time *time)
+{
+ int64_t stime;
+
+ stime = qemu_clock_get_ns(QEMU_CLOCK_HOST);
+ time->tv_sec = stime / 1000000000LL;
+ time->tv_nsec = stime % 1000000000LL;
+}
+
+static int64_t ipmi_getmonotime(void)
+{
+ return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+}
+
+static void ipmi_timeout(void *opaque)
+{
+ IPMIBmcSim *ibs = opaque;
+
+ ipmi_sim_handle_timeout(ibs);
+}
+
+static void set_timestamp(IPMIBmcSim *ibs, uint8_t *ts)
+{
+ unsigned int val;
+ struct ipmi_time now;
+
+ ipmi_gettime(&now);
+ val = now.tv_sec + ibs->sel.time_offset;
+ ts[0] = val & 0xff;
+ ts[1] = (val >> 8) & 0xff;
+ ts[2] = (val >> 16) & 0xff;
+ ts[3] = (val >> 24) & 0xff;
+}
+
+static void sdr_inc_reservation(IPMISdr *sdr)
+{
+ sdr->reservation++;
+ if (sdr->reservation == 0) {
+ sdr->reservation = 1;
+ }
+}
+
+static int sdr_add_entry(IPMIBmcSim *ibs,
+ const struct ipmi_sdr_header *sdrh_entry,
+ unsigned int len, uint16_t *recid)
+{
+ struct ipmi_sdr_header *sdrh =
+ (struct ipmi_sdr_header *) &ibs->sdr.sdr[ibs->sdr.next_free];
+
+ if ((len < IPMI_SDR_HEADER_SIZE) || (len > 255)) {
+ return 1;
+ }
+
+ if (ipmi_sdr_length(sdrh_entry) != len) {
+ return 1;
+ }
+
+ if (ibs->sdr.next_free + len > MAX_SDR_SIZE) {
+ ibs->sdr.overflow = 1;
+ return 1;
+ }
+
+ memcpy(sdrh, sdrh_entry, len);
+ sdrh->rec_id[0] = ibs->sdr.next_rec_id & 0xff;
+ sdrh->rec_id[1] = (ibs->sdr.next_rec_id >> 8) & 0xff;
+ sdrh->sdr_version = 0x51; /* Conform to IPMI 1.5 spec */
+
+ if (recid) {
+ *recid = ibs->sdr.next_rec_id;
+ }
+ ibs->sdr.next_rec_id++;
+ set_timestamp(ibs, ibs->sdr.last_addition);
+ ibs->sdr.next_free += len;
+ sdr_inc_reservation(&ibs->sdr);
+ return 0;
+}
+
+static int sdr_find_entry(IPMISdr *sdr, uint16_t recid,
+ unsigned int *retpos, uint16_t *nextrec)
+{
+ unsigned int pos = *retpos;
+
+ while (pos < sdr->next_free) {
+ struct ipmi_sdr_header *sdrh =
+ (struct ipmi_sdr_header *) &sdr->sdr[pos];
+ uint16_t trec = ipmi_sdr_recid(sdrh);
+ unsigned int nextpos = pos + ipmi_sdr_length(sdrh);
+
+ if (trec == recid) {
+ if (nextrec) {
+ if (nextpos >= sdr->next_free) {
+ *nextrec = 0xffff;
+ } else {
+ *nextrec = (sdr->sdr[nextpos] |
+ (sdr->sdr[nextpos + 1] << 8));
+ }
+ }
+ *retpos = pos;
+ return 0;
+ }
+ pos = nextpos;
+ }
+ return 1;
+}
+
+static void sel_inc_reservation(IPMISel *sel)
+{
+ sel->reservation++;
+ if (sel->reservation == 0) {
+ sel->reservation = 1;
+ }
+}
+
+/* Returns 1 if the SEL is full and can't hold the event. */
+static int sel_add_event(IPMIBmcSim *ibs, uint8_t *event)
+{
+ event[0] = 0xff;
+ event[1] = 0xff;
+ set_timestamp(ibs, event + 3);
+ if (ibs->sel.next_free == MAX_SEL_SIZE) {
+ ibs->sel.overflow = 1;
+ return 1;
+ }
+ event[0] = ibs->sel.next_free & 0xff;
+ event[1] = (ibs->sel.next_free >> 8) & 0xff;
+ memcpy(ibs->sel.last_addition, event + 3, 4);
+ memcpy(ibs->sel.sel[ibs->sel.next_free], event, 16);
+ ibs->sel.next_free++;
+ sel_inc_reservation(&ibs->sel);
+ return 0;
+}
+
+static int attn_set(IPMIBmcSim *ibs)
+{
+ return IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE_SET(ibs)
+ || IPMI_BMC_MSG_FLAG_EVT_BUF_FULL_SET(ibs)
+ || IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK_SET(ibs);
+}
+
+static int attn_irq_enabled(IPMIBmcSim *ibs)
+{
+ return (IPMI_BMC_MSG_INTS_ON(ibs) && IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE_SET(ibs))
+ || (IPMI_BMC_EVBUF_FULL_INT_ENABLED(ibs) &&
+ IPMI_BMC_MSG_FLAG_EVT_BUF_FULL_SET(ibs));
+}
+
+static void gen_event(IPMIBmcSim *ibs, unsigned int sens_num, uint8_t deassert,
+ uint8_t evd1, uint8_t evd2, uint8_t evd3)
+{
+ IPMIInterface *s = ibs->parent.intf;
+ IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+ uint8_t evt[16];
+ IPMISensor *sens = ibs->sensors + sens_num;
+
+ if (!IPMI_BMC_EVENT_MSG_BUF_ENABLED(ibs)) {
+ return;
+ }
+ if (!IPMI_SENSOR_GET_EVENTS_ON(sens)) {
+ return;
+ }
+
+ evt[2] = 0x2; /* System event record */
+ evt[7] = ibs->parent.slave_addr;
+ evt[8] = 0;
+ evt[9] = 0x04; /* Format version */
+ evt[10] = sens->sensor_type;
+ evt[11] = sens_num;
+ evt[12] = sens->evt_reading_type_code | (!!deassert << 7);
+ evt[13] = evd1;
+ evt[14] = evd2;
+ evt[15] = evd3;
+
+ if (IPMI_BMC_EVENT_LOG_ENABLED(ibs)) {
+ sel_add_event(ibs, evt);
+ }
+
+ if (ibs->msg_flags & IPMI_BMC_MSG_FLAG_EVT_BUF_FULL) {
+ return;
+ }
+
+ memcpy(ibs->evtbuf, evt, 16);
+ ibs->msg_flags |= IPMI_BMC_MSG_FLAG_EVT_BUF_FULL;
+ k->set_atn(s, 1, attn_irq_enabled(ibs));
+}
+
+static void sensor_set_discrete_bit(IPMIBmcSim *ibs, unsigned int sensor,
+ unsigned int bit, unsigned int val,
+ uint8_t evd1, uint8_t evd2, uint8_t evd3)
+{
+ IPMISensor *sens;
+ uint16_t mask;
+
+ if (sensor >= MAX_SENSORS) {
+ return;
+ }
+ if (bit >= 16) {
+ return;
+ }
+
+ mask = (1 << bit);
+ sens = ibs->sensors + sensor;
+ if (val) {
+ sens->states |= mask & sens->states_suppt;
+ if (sens->assert_states & mask) {
+ return; /* Already asserted */
+ }
+ sens->assert_states |= mask & sens->assert_suppt;
+ if (sens->assert_enable & mask & sens->assert_states) {
+ /* Send an event on assert */
+ gen_event(ibs, sensor, 0, evd1, evd2, evd3);
+ }
+ } else {
+ sens->states &= ~(mask & sens->states_suppt);
+ if (sens->deassert_states & mask) {
+ return; /* Already deasserted */
+ }
+ sens->deassert_states |= mask & sens->deassert_suppt;
+ if (sens->deassert_enable & mask & sens->deassert_states) {
+ /* Send an event on deassert */
+ gen_event(ibs, sensor, 1, evd1, evd2, evd3);
+ }
+ }
+}
+
+static void ipmi_init_sensors_from_sdrs(IPMIBmcSim *s)
+{
+ unsigned int i, pos;
+ IPMISensor *sens;
+
+ for (i = 0; i < MAX_SENSORS; i++) {
+ memset(s->sensors + i, 0, sizeof(*sens));
+ }
+
+ pos = 0;
+ for (i = 0; !sdr_find_entry(&s->sdr, i, &pos, NULL); i++) {
+ struct ipmi_sdr_compact *sdr =
+ (struct ipmi_sdr_compact *) &s->sdr.sdr[pos];
+ unsigned int len = sdr->header.rec_length;
+
+ if (len < 20) {
+ continue;
+ }
+ if (sdr->header.rec_type != IPMI_SDR_COMPACT_TYPE) {
+ continue; /* Not a sensor SDR we set from */
+ }
+
+ if (sdr->sensor_owner_number >= MAX_SENSORS) {
+ continue;
+ }
+ sens = s->sensors + sdr->sensor_owner_number;
+
+ IPMI_SENSOR_SET_PRESENT(sens, 1);
+ IPMI_SENSOR_SET_SCAN_ON(sens, (sdr->sensor_init >> 6) & 1);
+ IPMI_SENSOR_SET_EVENTS_ON(sens, (sdr->sensor_init >> 5) & 1);
+ sens->assert_suppt = sdr->assert_mask[0] | (sdr->assert_mask[1] << 8);
+ sens->deassert_suppt =
+ sdr->deassert_mask[0] | (sdr->deassert_mask[1] << 8);
+ sens->states_suppt =
+ sdr->discrete_mask[0] | (sdr->discrete_mask[1] << 8);
+ sens->sensor_type = sdr->sensor_type;
+ sens->evt_reading_type_code = sdr->reading_type & 0x7f;
+
+ /* Enable all the events that are supported. */
+ sens->assert_enable = sens->assert_suppt;
+ sens->deassert_enable = sens->deassert_suppt;
+ }
+}
+
+static int ipmi_register_netfn(IPMIBmcSim *s, unsigned int netfn,
+ const IPMINetfn *netfnd)
+{
+ if ((netfn & 1) || (netfn >= MAX_NETFNS) || (s->netfns[netfn / 2])) {
+ return -1;
+ }
+ s->netfns[netfn / 2] = netfnd;
+ return 0;
+}
+
+static const IPMICmdHandler *ipmi_get_handler(IPMIBmcSim *ibs,
+ unsigned int netfn,
+ unsigned int cmd)
+{
+ const IPMICmdHandler *hdl;
+
+ if (netfn & 1 || netfn >= MAX_NETFNS || !ibs->netfns[netfn / 2]) {
+ return NULL;
+ }
+
+ if (cmd >= ibs->netfns[netfn / 2]->cmd_nums) {
+ return NULL;
+ }
+
+ hdl = &ibs->netfns[netfn / 2]->cmd_handlers[cmd];
+ if (!hdl->cmd_handler) {
+ return NULL;
+ }
+
+ return hdl;
+}
+
+static void next_timeout(IPMIBmcSim *ibs)
+{
+ int64_t next;
+ if (ibs->watchdog_running) {
+ next = ibs->watchdog_expiry;
+ } else {
+ /* Wait a minute */
+ next = ipmi_getmonotime() + 60 * 1000000000LL;
+ }
+ timer_mod_ns(ibs->timer, next);
+}
+
+static void ipmi_sim_handle_command(IPMIBmc *b,
+ uint8_t *cmd, unsigned int cmd_len,
+ unsigned int max_cmd_len,
+ uint8_t msg_id)
+{
+ IPMIBmcSim *ibs = IPMI_BMC_SIMULATOR(b);
+ IPMIInterface *s = ibs->parent.intf;
+ IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+ const IPMICmdHandler *hdl;
+ RspBuffer rsp = RSP_BUFFER_INITIALIZER;
+
+ /* Set up the response, set the low bit of NETFN. */
+ /* Note that max_rsp_len must be at least 3 */
+ if (sizeof(rsp.buffer) < 3) {
+ rsp_buffer_set_error(&rsp, IPMI_CC_REQUEST_DATA_TRUNCATED);
+ goto out;
+ }
+
+ rsp_buffer_push(&rsp, cmd[0] | 0x04);
+ rsp_buffer_push(&rsp, cmd[1]);
+ rsp_buffer_push(&rsp, 0); /* Assume success */
+
+ /* If it's too short or it was truncated, return an error. */
+ if (cmd_len < 2) {
+ rsp_buffer_set_error(&rsp, IPMI_CC_REQUEST_DATA_LENGTH_INVALID);
+ goto out;
+ }
+ if (cmd_len > max_cmd_len) {
+ rsp_buffer_set_error(&rsp, IPMI_CC_REQUEST_DATA_TRUNCATED);
+ goto out;
+ }
+
+ if ((cmd[0] & 0x03) != 0) {
+ /* Only have stuff on LUN 0 */
+ rsp_buffer_set_error(&rsp, IPMI_CC_COMMAND_INVALID_FOR_LUN);
+ goto out;
+ }
+
+ hdl = ipmi_get_handler(ibs, cmd[0] >> 2, cmd[1]);
+ if (!hdl) {
+ rsp_buffer_set_error(&rsp, IPMI_CC_INVALID_CMD);
+ goto out;
+ }
+
+ if (cmd_len < hdl->cmd_len_min) {
+ rsp_buffer_set_error(&rsp, IPMI_CC_REQUEST_DATA_LENGTH_INVALID);
+ goto out;
+ }
+
+ hdl->cmd_handler(ibs, cmd, cmd_len, &rsp);
+
+ out:
+ k->handle_rsp(s, msg_id, rsp.buffer, rsp.len);
+
+ next_timeout(ibs);
+}
+
+static void ipmi_sim_handle_timeout(IPMIBmcSim *ibs)
+{
+ IPMIInterface *s = ibs->parent.intf;
+ IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+
+ if (!ibs->watchdog_running) {
+ goto out;
+ }
+
+ if (!ibs->watchdog_preaction_ran) {
+ switch (IPMI_BMC_WATCHDOG_GET_PRE_ACTION(ibs)) {
+ case IPMI_BMC_WATCHDOG_PRE_NMI:
+ ibs->msg_flags |= IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK;
+ k->do_hw_op(s, IPMI_SEND_NMI, 0);
+ sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 8, 1,
+ 0xc8, (2 << 4) | 0xf, 0xff);
+ break;
+
+ case IPMI_BMC_WATCHDOG_PRE_MSG_INT:
+ ibs->msg_flags |= IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK;
+ k->set_atn(s, 1, attn_irq_enabled(ibs));
+ sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 8, 1,
+ 0xc8, (3 << 4) | 0xf, 0xff);
+ break;
+
+ default:
+ goto do_full_expiry;
+ }
+
+ ibs->watchdog_preaction_ran = 1;
+ /* Issued the pretimeout, do the rest of the timeout now. */
+ ibs->watchdog_expiry = ipmi_getmonotime();
+ ibs->watchdog_expiry += ibs->watchdog_pretimeout * 1000000000LL;
+ goto out;
+ }
+
+ do_full_expiry:
+ ibs->watchdog_running = 0; /* Stop the watchdog on a timeout */
+ ibs->watchdog_expired |= (1 << IPMI_BMC_WATCHDOG_GET_USE(ibs));
+ switch (IPMI_BMC_WATCHDOG_GET_ACTION(ibs)) {
+ case IPMI_BMC_WATCHDOG_ACTION_NONE:
+ sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 0, 1,
+ 0xc0, ibs->watchdog_use & 0xf, 0xff);
+ break;
+
+ case IPMI_BMC_WATCHDOG_ACTION_RESET:
+ sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 1, 1,
+ 0xc1, ibs->watchdog_use & 0xf, 0xff);
+ k->do_hw_op(s, IPMI_RESET_CHASSIS, 0);
+ break;
+
+ case IPMI_BMC_WATCHDOG_ACTION_POWER_DOWN:
+ sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 2, 1,
+ 0xc2, ibs->watchdog_use & 0xf, 0xff);
+ k->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 0);
+ break;
+
+ case IPMI_BMC_WATCHDOG_ACTION_POWER_CYCLE:
+ sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 2, 1,
+ 0xc3, ibs->watchdog_use & 0xf, 0xff);
+ k->do_hw_op(s, IPMI_POWERCYCLE_CHASSIS, 0);
+ break;
+ }
+
+ out:
+ next_timeout(ibs);
+}
+
+static void chassis_capabilities(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ rsp_buffer_push(rsp, 0);
+ rsp_buffer_push(rsp, ibs->parent.slave_addr);
+ rsp_buffer_push(rsp, ibs->parent.slave_addr);
+ rsp_buffer_push(rsp, ibs->parent.slave_addr);
+ rsp_buffer_push(rsp, ibs->parent.slave_addr);
+}
+
+static void chassis_status(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ rsp_buffer_push(rsp, 0x61); /* Unknown power restore, power is on */
+ rsp_buffer_push(rsp, 0);
+ rsp_buffer_push(rsp, 0);
+ rsp_buffer_push(rsp, 0);
+}
+
+static void chassis_control(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ IPMIInterface *s = ibs->parent.intf;
+ IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+
+ switch (cmd[2] & 0xf) {
+ case 0: /* power down */
+ rsp_buffer_set_error(rsp, k->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 0));
+ break;
+ case 1: /* power up */
+ rsp_buffer_set_error(rsp, k->do_hw_op(s, IPMI_POWERON_CHASSIS, 0));
+ break;
+ case 2: /* power cycle */
+ rsp_buffer_set_error(rsp, k->do_hw_op(s, IPMI_POWERCYCLE_CHASSIS, 0));
+ break;
+ case 3: /* hard reset */
+ rsp_buffer_set_error(rsp, k->do_hw_op(s, IPMI_RESET_CHASSIS, 0));
+ break;
+ case 4: /* pulse diagnostic interrupt */
+ rsp_buffer_set_error(rsp, k->do_hw_op(s, IPMI_PULSE_DIAG_IRQ, 0));
+ break;
+ case 5: /* soft shutdown via ACPI by overtemp emulation */
+ rsp_buffer_set_error(rsp, k->do_hw_op(s,
+ IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP, 0));
+ break;
+ default:
+ rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
+ return;
+ }
+}
+
+static void chassis_get_sys_restart_cause(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+
+{
+ rsp_buffer_push(rsp, ibs->restart_cause & 0xf); /* Restart Cause */
+ rsp_buffer_push(rsp, 0); /* Channel 0 */
+}
+
+static void get_device_id(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ rsp_buffer_push(rsp, ibs->device_id);
+ rsp_buffer_push(rsp, ibs->device_rev & 0xf);
+ rsp_buffer_push(rsp, ibs->fwrev1 & 0x7f);
+ rsp_buffer_push(rsp, ibs->fwrev2);
+ rsp_buffer_push(rsp, ibs->ipmi_version);
+ rsp_buffer_push(rsp, 0x07); /* sensor, SDR, and SEL. */
+ rsp_buffer_push(rsp, ibs->mfg_id[0]);
+ rsp_buffer_push(rsp, ibs->mfg_id[1]);
+ rsp_buffer_push(rsp, ibs->mfg_id[2]);
+ rsp_buffer_push(rsp, ibs->product_id[0]);
+ rsp_buffer_push(rsp, ibs->product_id[1]);
+}
+
+static void set_global_enables(IPMIBmcSim *ibs, uint8_t val)
+{
+ IPMIInterface *s = ibs->parent.intf;
+ IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+ bool irqs_on;
+
+ ibs->bmc_global_enables = val;
+
+ irqs_on = val & (IPMI_BMC_EVBUF_FULL_INT_BIT |
+ IPMI_BMC_RCV_MSG_QUEUE_INT_BIT);
+
+ k->set_irq_enable(s, irqs_on);
+}
+
+static void cold_reset(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ IPMIInterface *s = ibs->parent.intf;
+ IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+
+ /* Disable all interrupts */
+ set_global_enables(ibs, 1 << IPMI_BMC_EVENT_LOG_BIT);
+
+ if (k->reset) {
+ k->reset(s, true);
+ }
+}
+
+static void warm_reset(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ IPMIInterface *s = ibs->parent.intf;
+ IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+
+ if (k->reset) {
+ k->reset(s, false);
+ }
+}
+static void set_acpi_power_state(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ ibs->acpi_power_state[0] = cmd[2];
+ ibs->acpi_power_state[1] = cmd[3];
+}
+
+static void get_acpi_power_state(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ rsp_buffer_push(rsp, ibs->acpi_power_state[0]);
+ rsp_buffer_push(rsp, ibs->acpi_power_state[1]);
+}
+
+static void get_device_guid(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ unsigned int i;
+
+ for (i = 0; i < 16; i++) {
+ rsp_buffer_push(rsp, ibs->uuid[i]);
+ }
+}
+
+static void set_bmc_global_enables(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ set_global_enables(ibs, cmd[2]);
+}
+
+static void get_bmc_global_enables(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ rsp_buffer_push(rsp, ibs->bmc_global_enables);
+}
+
+static void clr_msg_flags(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ IPMIInterface *s = ibs->parent.intf;
+ IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+
+ ibs->msg_flags &= ~cmd[2];
+ k->set_atn(s, attn_set(ibs), attn_irq_enabled(ibs));
+}
+
+static void get_msg_flags(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ rsp_buffer_push(rsp, ibs->msg_flags);
+}
+
+static void read_evt_msg_buf(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ IPMIInterface *s = ibs->parent.intf;
+ IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+ unsigned int i;
+
+ if (!(ibs->msg_flags & IPMI_BMC_MSG_FLAG_EVT_BUF_FULL)) {
+ rsp_buffer_set_error(rsp, 0x80);
+ return;
+ }
+ for (i = 0; i < 16; i++) {
+ rsp_buffer_push(rsp, ibs->evtbuf[i]);
+ }
+ ibs->msg_flags &= ~IPMI_BMC_MSG_FLAG_EVT_BUF_FULL;
+ k->set_atn(s, attn_set(ibs), attn_irq_enabled(ibs));
+}
+
+static void get_msg(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ IPMIRcvBufEntry *msg;
+
+ qemu_mutex_lock(&ibs->lock);
+ if (QTAILQ_EMPTY(&ibs->rcvbufs)) {
+ rsp_buffer_set_error(rsp, 0x80); /* Queue empty */
+ goto out;
+ }
+ rsp_buffer_push(rsp, 0); /* Channel 0 */
+ msg = QTAILQ_FIRST(&ibs->rcvbufs);
+ rsp_buffer_pushmore(rsp, msg->buf, msg->len);
+ QTAILQ_REMOVE(&ibs->rcvbufs, msg, entry);
+ g_free(msg);
+
+ if (QTAILQ_EMPTY(&ibs->rcvbufs)) {
+ IPMIInterface *s = ibs->parent.intf;
+ IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+
+ ibs->msg_flags &= ~IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE;
+ k->set_atn(s, attn_set(ibs), attn_irq_enabled(ibs));
+ }
+
+out:
+ qemu_mutex_unlock(&ibs->lock);
+ return;
+}
+
+static unsigned char
+ipmb_checksum(unsigned char *data, int size, unsigned char csum)
+{
+ for (; size > 0; size--, data++) {
+ csum += *data;
+ }
+
+ return -csum;
+}
+
+static void send_msg(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ IPMIInterface *s = ibs->parent.intf;
+ IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+ IPMIRcvBufEntry *msg;
+ uint8_t *buf;
+ uint8_t netfn, rqLun, rsLun, rqSeq;
+
+ if (cmd[2] != 0) {
+ /* We only handle channel 0 with no options */
+ rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
+ return;
+ }
+
+ if (cmd_len < 10) {
+ rsp_buffer_set_error(rsp, IPMI_CC_REQUEST_DATA_LENGTH_INVALID);
+ return;
+ }
+
+ if (cmd[3] != 0x40) {
+ /* We only emulate a MC at address 0x40. */
+ rsp_buffer_set_error(rsp, 0x83); /* NAK on write */
+ return;
+ }
+
+ cmd += 3; /* Skip the header. */
+ cmd_len -= 3;
+
+ /*
+ * At this point we "send" the message successfully. Any error will
+ * be returned in the response.
+ */
+ if (ipmb_checksum(cmd, cmd_len, 0) != 0 ||
+ cmd[3] != 0x20) { /* Improper response address */
+ return; /* No response */
+ }
+
+ netfn = cmd[1] >> 2;
+ rqLun = cmd[4] & 0x3;
+ rsLun = cmd[1] & 0x3;
+ rqSeq = cmd[4] >> 2;
+
+ if (rqLun != 2) {
+ /* We only support LUN 2 coming back to us. */
+ return;
+ }
+
+ msg = g_malloc(sizeof(*msg));
+ msg->buf[0] = ((netfn | 1) << 2) | rqLun; /* NetFN, and make a response */
+ msg->buf[1] = ipmb_checksum(msg->buf, 1, 0);
+ msg->buf[2] = cmd[0]; /* rsSA */
+ msg->buf[3] = (rqSeq << 2) | rsLun;
+ msg->buf[4] = cmd[5]; /* Cmd */
+ msg->buf[5] = 0; /* Completion Code */
+ msg->len = 6;
+
+ if ((cmd[1] >> 2) != IPMI_NETFN_APP || cmd[5] != IPMI_CMD_GET_DEVICE_ID) {
+ /* Not a command we handle. */
+ msg->buf[5] = IPMI_CC_INVALID_CMD;
+ goto end_msg;
+ }
+
+ buf = msg->buf + msg->len; /* After the CC */
+ buf[0] = 0;
+ buf[1] = 0;
+ buf[2] = 0;
+ buf[3] = 0;
+ buf[4] = 0x51;
+ buf[5] = 0;
+ buf[6] = 0;
+ buf[7] = 0;
+ buf[8] = 0;
+ buf[9] = 0;
+ buf[10] = 0;
+ msg->len += 11;
+
+ end_msg:
+ msg->buf[msg->len] = ipmb_checksum(msg->buf, msg->len, 0);
+ msg->len++;
+ qemu_mutex_lock(&ibs->lock);
+ QTAILQ_INSERT_TAIL(&ibs->rcvbufs, msg, entry);
+ ibs->msg_flags |= IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE;
+ k->set_atn(s, 1, attn_irq_enabled(ibs));
+ qemu_mutex_unlock(&ibs->lock);
+}
+
+static void do_watchdog_reset(IPMIBmcSim *ibs)
+{
+ if (IPMI_BMC_WATCHDOG_GET_ACTION(ibs) ==
+ IPMI_BMC_WATCHDOG_ACTION_NONE) {
+ ibs->watchdog_running = 0;
+ return;
+ }
+ ibs->watchdog_preaction_ran = 0;
+
+
+ /* Timeout is in tenths of a second, offset is in seconds */
+ ibs->watchdog_expiry = ipmi_getmonotime();
+ ibs->watchdog_expiry += ibs->watchdog_timeout * 100000000LL;
+ if (IPMI_BMC_WATCHDOG_GET_PRE_ACTION(ibs) != IPMI_BMC_WATCHDOG_PRE_NONE) {
+ ibs->watchdog_expiry -= ibs->watchdog_pretimeout * 1000000000LL;
+ }
+ ibs->watchdog_running = 1;
+}
+
+static void reset_watchdog_timer(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ if (!ibs->watchdog_initialized) {
+ rsp_buffer_set_error(rsp, 0x80);
+ return;
+ }
+ do_watchdog_reset(ibs);
+}
+
+static void set_watchdog_timer(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ IPMIInterface *s = ibs->parent.intf;
+ IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
+ unsigned int val;
+
+ val = cmd[2] & 0x7; /* Validate use */
+ if (val == 0 || val > 5) {
+ rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
+ return;
+ }
+ val = cmd[3] & 0x7; /* Validate action */
+ switch (val) {
+ case IPMI_BMC_WATCHDOG_ACTION_NONE:
+ break;
+
+ case IPMI_BMC_WATCHDOG_ACTION_RESET:
+ rsp_buffer_set_error(rsp, k->do_hw_op(s, IPMI_RESET_CHASSIS, 1));
+ break;
+
+ case IPMI_BMC_WATCHDOG_ACTION_POWER_DOWN:
+ rsp_buffer_set_error(rsp, k->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 1));
+ break;
+
+ case IPMI_BMC_WATCHDOG_ACTION_POWER_CYCLE:
+ rsp_buffer_set_error(rsp, k->do_hw_op(s, IPMI_POWERCYCLE_CHASSIS, 1));
+ break;
+
+ default:
+ rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
+ }
+ if (rsp->buffer[2]) {
+ rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
+ return;
+ }
+
+ val = (cmd[3] >> 4) & 0x7; /* Validate preaction */
+ switch (val) {
+ case IPMI_BMC_WATCHDOG_PRE_MSG_INT:
+ case IPMI_BMC_WATCHDOG_PRE_NONE:
+ break;
+
+ case IPMI_BMC_WATCHDOG_PRE_NMI:
+ if (!k->do_hw_op(s, IPMI_SEND_NMI, 1)) {
+ /* NMI not supported. */
+ rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
+ return;
+ }
+ break;
+
+ default:
+ /* We don't support PRE_SMI */
+ rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
+ return;
+ }
+
+ ibs->watchdog_initialized = 1;
+ ibs->watchdog_use = cmd[2] & IPMI_BMC_WATCHDOG_USE_MASK;
+ ibs->watchdog_action = cmd[3] & IPMI_BMC_WATCHDOG_ACTION_MASK;
+ ibs->watchdog_pretimeout = cmd[4];
+ ibs->watchdog_expired &= ~cmd[5];
+ ibs->watchdog_timeout = cmd[6] | (((uint16_t) cmd[7]) << 8);
+ if (ibs->watchdog_running & IPMI_BMC_WATCHDOG_GET_DONT_STOP(ibs)) {
+ do_watchdog_reset(ibs);
+ } else {
+ ibs->watchdog_running = 0;
+ }
+}
+
+static void get_watchdog_timer(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ rsp_buffer_push(rsp, ibs->watchdog_use);
+ rsp_buffer_push(rsp, ibs->watchdog_action);
+ rsp_buffer_push(rsp, ibs->watchdog_pretimeout);
+ rsp_buffer_push(rsp, ibs->watchdog_expired);
+ if (ibs->watchdog_running) {
+ long timeout;
+ timeout = ((ibs->watchdog_expiry - ipmi_getmonotime() + 50000000)
+ / 100000000);
+ rsp_buffer_push(rsp, timeout & 0xff);
+ rsp_buffer_push(rsp, (timeout >> 8) & 0xff);
+ } else {
+ rsp_buffer_push(rsp, 0);
+ rsp_buffer_push(rsp, 0);
+ }
+}
+
+static void get_sdr_rep_info(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ unsigned int i;
+
+ rsp_buffer_push(rsp, 0x51); /* Conform to IPMI 1.5 spec */
+ rsp_buffer_push(rsp, ibs->sdr.next_rec_id & 0xff);
+ rsp_buffer_push(rsp, (ibs->sdr.next_rec_id >> 8) & 0xff);
+ rsp_buffer_push(rsp, (MAX_SDR_SIZE - ibs->sdr.next_free) & 0xff);
+ rsp_buffer_push(rsp, ((MAX_SDR_SIZE - ibs->sdr.next_free) >> 8) & 0xff);
+ for (i = 0; i < 4; i++) {
+ rsp_buffer_push(rsp, ibs->sdr.last_addition[i]);
+ }
+ for (i = 0; i < 4; i++) {
+ rsp_buffer_push(rsp, ibs->sdr.last_clear[i]);
+ }
+ /* Only modal support, reserve supported */
+ rsp_buffer_push(rsp, (ibs->sdr.overflow << 7) | 0x22);
+}
+
+static void reserve_sdr_rep(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ rsp_buffer_push(rsp, ibs->sdr.reservation & 0xff);
+ rsp_buffer_push(rsp, (ibs->sdr.reservation >> 8) & 0xff);
+}
+
+static void get_sdr(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ unsigned int pos;
+ uint16_t nextrec;
+ struct ipmi_sdr_header *sdrh;
+
+ if (cmd[6]) {
+ if ((cmd[2] | (cmd[3] << 8)) != ibs->sdr.reservation) {
+ rsp_buffer_set_error(rsp, IPMI_CC_INVALID_RESERVATION);
+ return;
+ }
+ }
+
+ pos = 0;
+ if (sdr_find_entry(&ibs->sdr, cmd[4] | (cmd[5] << 8),
+ &pos, &nextrec)) {
+ rsp_buffer_set_error(rsp, IPMI_CC_REQ_ENTRY_NOT_PRESENT);
+ return;
+ }
+
+ sdrh = (struct ipmi_sdr_header *) &ibs->sdr.sdr[pos];
+
+ if (cmd[6] > ipmi_sdr_length(sdrh)) {
+ rsp_buffer_set_error(rsp, IPMI_CC_PARM_OUT_OF_RANGE);
+ return;
+ }
+
+ rsp_buffer_push(rsp, nextrec & 0xff);
+ rsp_buffer_push(rsp, (nextrec >> 8) & 0xff);
+
+ if (cmd[7] == 0xff) {
+ cmd[7] = ipmi_sdr_length(sdrh) - cmd[6];
+ }
+
+ if ((cmd[7] + rsp->len) > sizeof(rsp->buffer)) {
+ rsp_buffer_set_error(rsp, IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES);
+ return;
+ }
+
+ rsp_buffer_pushmore(rsp, ibs->sdr.sdr + pos + cmd[6], cmd[7]);
+}
+
+static void add_sdr(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ uint16_t recid;
+ struct ipmi_sdr_header *sdrh = (struct ipmi_sdr_header *) cmd + 2;
+
+ if (sdr_add_entry(ibs, sdrh, cmd_len - 2, &recid)) {
+ rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
+ return;
+ }
+ rsp_buffer_push(rsp, recid & 0xff);
+ rsp_buffer_push(rsp, (recid >> 8) & 0xff);
+}
+
+static void clear_sdr_rep(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ if ((cmd[2] | (cmd[3] << 8)) != ibs->sdr.reservation) {
+ rsp_buffer_set_error(rsp, IPMI_CC_INVALID_RESERVATION);
+ return;
+ }
+
+ if (cmd[4] != 'C' || cmd[5] != 'L' || cmd[6] != 'R') {
+ rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
+ return;
+ }
+ if (cmd[7] == 0xaa) {
+ ibs->sdr.next_free = 0;
+ ibs->sdr.overflow = 0;
+ set_timestamp(ibs, ibs->sdr.last_clear);
+ rsp_buffer_push(rsp, 1); /* Erasure complete */
+ sdr_inc_reservation(&ibs->sdr);
+ } else if (cmd[7] == 0) {
+ rsp_buffer_push(rsp, 1); /* Erasure complete */
+ } else {
+ rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
+ return;
+ }
+}
+
+static void get_sel_info(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ unsigned int i, val;
+
+ rsp_buffer_push(rsp, 0x51); /* Conform to IPMI 1.5 */
+ rsp_buffer_push(rsp, ibs->sel.next_free & 0xff);
+ rsp_buffer_push(rsp, (ibs->sel.next_free >> 8) & 0xff);
+ val = (MAX_SEL_SIZE - ibs->sel.next_free) * 16;
+ rsp_buffer_push(rsp, val & 0xff);
+ rsp_buffer_push(rsp, (val >> 8) & 0xff);
+ for (i = 0; i < 4; i++) {
+ rsp_buffer_push(rsp, ibs->sel.last_addition[i]);
+ }
+ for (i = 0; i < 4; i++) {
+ rsp_buffer_push(rsp, ibs->sel.last_clear[i]);
+ }
+ /* Only support Reserve SEL */
+ rsp_buffer_push(rsp, (ibs->sel.overflow << 7) | 0x02);
+}
+
+static void reserve_sel(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ rsp_buffer_push(rsp, ibs->sel.reservation & 0xff);
+ rsp_buffer_push(rsp, (ibs->sel.reservation >> 8) & 0xff);
+}
+
+static void get_sel_entry(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ unsigned int val;
+
+ if (cmd[6]) {
+ if ((cmd[2] | (cmd[3] << 8)) != ibs->sel.reservation) {
+ rsp_buffer_set_error(rsp, IPMI_CC_INVALID_RESERVATION);
+ return;
+ }
+ }
+ if (ibs->sel.next_free == 0) {
+ rsp_buffer_set_error(rsp, IPMI_CC_REQ_ENTRY_NOT_PRESENT);
+ return;
+ }
+ if (cmd[6] > 15) {
+ rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
+ return;
+ }
+ if (cmd[7] == 0xff) {
+ cmd[7] = 16;
+ } else if ((cmd[7] + cmd[6]) > 16) {
+ rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
+ return;
+ } else {
+ cmd[7] += cmd[6];
+ }
+
+ val = cmd[4] | (cmd[5] << 8);
+ if (val == 0xffff) {
+ val = ibs->sel.next_free - 1;
+ } else if (val >= ibs->sel.next_free) {
+ rsp_buffer_set_error(rsp, IPMI_CC_REQ_ENTRY_NOT_PRESENT);
+ return;
+ }
+ if ((val + 1) == ibs->sel.next_free) {
+ rsp_buffer_push(rsp, 0xff);
+ rsp_buffer_push(rsp, 0xff);
+ } else {
+ rsp_buffer_push(rsp, (val + 1) & 0xff);
+ rsp_buffer_push(rsp, ((val + 1) >> 8) & 0xff);
+ }
+ for (; cmd[6] < cmd[7]; cmd[6]++) {
+ rsp_buffer_push(rsp, ibs->sel.sel[val][cmd[6]]);
+ }
+}
+
+static void add_sel_entry(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ if (sel_add_event(ibs, cmd + 2)) {
+ rsp_buffer_set_error(rsp, IPMI_CC_OUT_OF_SPACE);
+ return;
+ }
+ /* sel_add_event fills in the record number. */
+ rsp_buffer_push(rsp, cmd[2]);
+ rsp_buffer_push(rsp, cmd[3]);
+}
+
+static void clear_sel(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ if ((cmd[2] | (cmd[3] << 8)) != ibs->sel.reservation) {
+ rsp_buffer_set_error(rsp, IPMI_CC_INVALID_RESERVATION);
+ return;
+ }
+
+ if (cmd[4] != 'C' || cmd[5] != 'L' || cmd[6] != 'R') {
+ rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
+ return;
+ }
+ if (cmd[7] == 0xaa) {
+ ibs->sel.next_free = 0;
+ ibs->sel.overflow = 0;
+ set_timestamp(ibs, ibs->sdr.last_clear);
+ rsp_buffer_push(rsp, 1); /* Erasure complete */
+ sel_inc_reservation(&ibs->sel);
+ } else if (cmd[7] == 0) {
+ rsp_buffer_push(rsp, 1); /* Erasure complete */
+ } else {
+ rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
+ return;
+ }
+}
+
+static void get_sel_time(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ uint32_t val;
+ struct ipmi_time now;
+
+ ipmi_gettime(&now);
+ val = now.tv_sec + ibs->sel.time_offset;
+ rsp_buffer_push(rsp, val & 0xff);
+ rsp_buffer_push(rsp, (val >> 8) & 0xff);
+ rsp_buffer_push(rsp, (val >> 16) & 0xff);
+ rsp_buffer_push(rsp, (val >> 24) & 0xff);
+}
+
+static void set_sel_time(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ uint32_t val;
+ struct ipmi_time now;
+
+ val = cmd[2] | (cmd[3] << 8) | (cmd[4] << 16) | (cmd[5] << 24);
+ ipmi_gettime(&now);
+ ibs->sel.time_offset = now.tv_sec - ((long) val);
+}
+
+static void set_sensor_evt_enable(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ IPMISensor *sens;
+
+ if ((cmd[2] >= MAX_SENSORS) ||
+ !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) {
+ rsp_buffer_set_error(rsp, IPMI_CC_REQ_ENTRY_NOT_PRESENT);
+ return;
+ }
+ sens = ibs->sensors + cmd[2];
+ switch ((cmd[3] >> 4) & 0x3) {
+ case 0: /* Do not change */
+ break;
+ case 1: /* Enable bits */
+ if (cmd_len > 4) {
+ sens->assert_enable |= cmd[4];
+ }
+ if (cmd_len > 5) {
+ sens->assert_enable |= cmd[5] << 8;
+ }
+ if (cmd_len > 6) {
+ sens->deassert_enable |= cmd[6];
+ }
+ if (cmd_len > 7) {
+ sens->deassert_enable |= cmd[7] << 8;
+ }
+ break;
+ case 2: /* Disable bits */
+ if (cmd_len > 4) {
+ sens->assert_enable &= ~cmd[4];
+ }
+ if (cmd_len > 5) {
+ sens->assert_enable &= ~(cmd[5] << 8);
+ }
+ if (cmd_len > 6) {
+ sens->deassert_enable &= ~cmd[6];
+ }
+ if (cmd_len > 7) {
+ sens->deassert_enable &= ~(cmd[7] << 8);
+ }
+ break;
+ case 3:
+ rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
+ return;
+ }
+ IPMI_SENSOR_SET_RET_STATUS(sens, cmd[3]);
+}
+
+static void get_sensor_evt_enable(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ IPMISensor *sens;
+
+ if ((cmd[2] >= MAX_SENSORS) ||
+ !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) {
+ rsp_buffer_set_error(rsp, IPMI_CC_REQ_ENTRY_NOT_PRESENT);
+ return;
+ }
+ sens = ibs->sensors + cmd[2];
+ rsp_buffer_push(rsp, IPMI_SENSOR_GET_RET_STATUS(sens));
+ rsp_buffer_push(rsp, sens->assert_enable & 0xff);
+ rsp_buffer_push(rsp, (sens->assert_enable >> 8) & 0xff);
+ rsp_buffer_push(rsp, sens->deassert_enable & 0xff);
+ rsp_buffer_push(rsp, (sens->deassert_enable >> 8) & 0xff);
+}
+
+static void rearm_sensor_evts(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ IPMISensor *sens;
+
+ if ((cmd[2] >= MAX_SENSORS) ||
+ !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) {
+ rsp_buffer_set_error(rsp, IPMI_CC_REQ_ENTRY_NOT_PRESENT);
+ return;
+ }
+ sens = ibs->sensors + cmd[2];
+
+ if ((cmd[3] & 0x80) == 0) {
+ /* Just clear everything */
+ sens->states = 0;
+ return;
+ }
+}
+
+static void get_sensor_evt_status(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ IPMISensor *sens;
+
+ if ((cmd[2] >= MAX_SENSORS) ||
+ !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) {
+ rsp_buffer_set_error(rsp, IPMI_CC_REQ_ENTRY_NOT_PRESENT);
+ return;
+ }
+ sens = ibs->sensors + cmd[2];
+ rsp_buffer_push(rsp, sens->reading);
+ rsp_buffer_push(rsp, IPMI_SENSOR_GET_RET_STATUS(sens));
+ rsp_buffer_push(rsp, sens->assert_states & 0xff);
+ rsp_buffer_push(rsp, (sens->assert_states >> 8) & 0xff);
+ rsp_buffer_push(rsp, sens->deassert_states & 0xff);
+ rsp_buffer_push(rsp, (sens->deassert_states >> 8) & 0xff);
+}
+
+static void get_sensor_reading(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ IPMISensor *sens;
+
+ if ((cmd[2] >= MAX_SENSORS) ||
+ !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) {
+ rsp_buffer_set_error(rsp, IPMI_CC_REQ_ENTRY_NOT_PRESENT);
+ return;
+ }
+ sens = ibs->sensors + cmd[2];
+ rsp_buffer_push(rsp, sens->reading);
+ rsp_buffer_push(rsp, IPMI_SENSOR_GET_RET_STATUS(sens));
+ rsp_buffer_push(rsp, sens->states & 0xff);
+ if (IPMI_SENSOR_IS_DISCRETE(sens)) {
+ rsp_buffer_push(rsp, (sens->states >> 8) & 0xff);
+ }
+}
+
+static void set_sensor_type(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ IPMISensor *sens;
+
+
+ if ((cmd[2] >= MAX_SENSORS) ||
+ !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) {
+ rsp_buffer_set_error(rsp, IPMI_CC_REQ_ENTRY_NOT_PRESENT);
+ return;
+ }
+ sens = ibs->sensors + cmd[2];
+ sens->sensor_type = cmd[3];
+ sens->evt_reading_type_code = cmd[4] & 0x7f;
+}
+
+static void get_sensor_type(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ IPMISensor *sens;
+
+
+ if ((cmd[2] >= MAX_SENSORS) ||
+ !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) {
+ rsp_buffer_set_error(rsp, IPMI_CC_REQ_ENTRY_NOT_PRESENT);
+ return;
+ }
+ sens = ibs->sensors + cmd[2];
+ rsp_buffer_push(rsp, sens->sensor_type);
+ rsp_buffer_push(rsp, sens->evt_reading_type_code);
+}
+
+
+static const IPMICmdHandler chassis_cmds[] = {
+ [IPMI_CMD_GET_CHASSIS_CAPABILITIES] = { chassis_capabilities },
+ [IPMI_CMD_GET_CHASSIS_STATUS] = { chassis_status },
+ [IPMI_CMD_CHASSIS_CONTROL] = { chassis_control, 3 },
+ [IPMI_CMD_GET_SYS_RESTART_CAUSE] = { chassis_get_sys_restart_cause }
+};
+static const IPMINetfn chassis_netfn = {
+ .cmd_nums = ARRAY_SIZE(chassis_cmds),
+ .cmd_handlers = chassis_cmds
+};
+
+static const IPMICmdHandler sensor_event_cmds[] = {
+ [IPMI_CMD_SET_SENSOR_EVT_ENABLE] = { set_sensor_evt_enable, 4 },
+ [IPMI_CMD_GET_SENSOR_EVT_ENABLE] = { get_sensor_evt_enable, 3 },
+ [IPMI_CMD_REARM_SENSOR_EVTS] = { rearm_sensor_evts, 4 },
+ [IPMI_CMD_GET_SENSOR_EVT_STATUS] = { get_sensor_evt_status, 3 },
+ [IPMI_CMD_GET_SENSOR_READING] = { get_sensor_reading, 3 },
+ [IPMI_CMD_SET_SENSOR_TYPE] = { set_sensor_type, 5 },
+ [IPMI_CMD_GET_SENSOR_TYPE] = { get_sensor_type, 3 },
+};
+static const IPMINetfn sensor_event_netfn = {
+ .cmd_nums = ARRAY_SIZE(sensor_event_cmds),
+ .cmd_handlers = sensor_event_cmds
+};
+
+static const IPMICmdHandler app_cmds[] = {
+ [IPMI_CMD_GET_DEVICE_ID] = { get_device_id },
+ [IPMI_CMD_COLD_RESET] = { cold_reset },
+ [IPMI_CMD_WARM_RESET] = { warm_reset },
+ [IPMI_CMD_SET_ACPI_POWER_STATE] = { set_acpi_power_state, 4 },
+ [IPMI_CMD_GET_ACPI_POWER_STATE] = { get_acpi_power_state },
+ [IPMI_CMD_GET_DEVICE_GUID] = { get_device_guid },
+ [IPMI_CMD_SET_BMC_GLOBAL_ENABLES] = { set_bmc_global_enables, 3 },
+ [IPMI_CMD_GET_BMC_GLOBAL_ENABLES] = { get_bmc_global_enables },
+ [IPMI_CMD_CLR_MSG_FLAGS] = { clr_msg_flags, 3 },
+ [IPMI_CMD_GET_MSG_FLAGS] = { get_msg_flags },
+ [IPMI_CMD_GET_MSG] = { get_msg },
+ [IPMI_CMD_SEND_MSG] = { send_msg, 3 },
+ [IPMI_CMD_READ_EVT_MSG_BUF] = { read_evt_msg_buf },
+ [IPMI_CMD_RESET_WATCHDOG_TIMER] = { reset_watchdog_timer },
+ [IPMI_CMD_SET_WATCHDOG_TIMER] = { set_watchdog_timer, 8 },
+ [IPMI_CMD_GET_WATCHDOG_TIMER] = { get_watchdog_timer },
+};
+static const IPMINetfn app_netfn = {
+ .cmd_nums = ARRAY_SIZE(app_cmds),
+ .cmd_handlers = app_cmds
+};
+
+static const IPMICmdHandler storage_cmds[] = {
+ [IPMI_CMD_GET_SDR_REP_INFO] = { get_sdr_rep_info },
+ [IPMI_CMD_RESERVE_SDR_REP] = { reserve_sdr_rep },
+ [IPMI_CMD_GET_SDR] = { get_sdr, 8 },
+ [IPMI_CMD_ADD_SDR] = { add_sdr },
+ [IPMI_CMD_CLEAR_SDR_REP] = { clear_sdr_rep, 8 },
+ [IPMI_CMD_GET_SEL_INFO] = { get_sel_info },
+ [IPMI_CMD_RESERVE_SEL] = { reserve_sel },
+ [IPMI_CMD_GET_SEL_ENTRY] = { get_sel_entry, 8 },
+ [IPMI_CMD_ADD_SEL_ENTRY] = { add_sel_entry, 18 },
+ [IPMI_CMD_CLEAR_SEL] = { clear_sel, 8 },
+ [IPMI_CMD_GET_SEL_TIME] = { get_sel_time, 6 },
+ [IPMI_CMD_SET_SEL_TIME] = { set_sel_time },
+};
+
+static const IPMINetfn storage_netfn = {
+ .cmd_nums = ARRAY_SIZE(storage_cmds),
+ .cmd_handlers = storage_cmds
+};
+
+static void register_cmds(IPMIBmcSim *s)
+{
+ ipmi_register_netfn(s, IPMI_NETFN_CHASSIS, &chassis_netfn);
+ ipmi_register_netfn(s, IPMI_NETFN_SENSOR_EVENT, &sensor_event_netfn);
+ ipmi_register_netfn(s, IPMI_NETFN_APP, &app_netfn);
+ ipmi_register_netfn(s, IPMI_NETFN_STORAGE, &storage_netfn);
+}
+
+static uint8_t init_sdrs[] = {
+ /* Watchdog device */
+ 0x00, 0x00, 0x51, 0x02, 35, 0x20, 0x00, 0x00,
+ 0x23, 0x01, 0x63, 0x00, 0x23, 0x6f, 0x0f, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8,
+ 'W', 'a', 't', 'c', 'h', 'd', 'o', 'g',
+};
+
+static void ipmi_sdr_init(IPMIBmcSim *ibs)
+{
+ unsigned int i;
+ int len;
+ size_t sdrs_size;
+ uint8_t *sdrs;
+
+ sdrs_size = sizeof(init_sdrs);
+ sdrs = init_sdrs;
+
+ for (i = 0; i < sdrs_size; i += len) {
+ struct ipmi_sdr_header *sdrh;
+
+ if (i + IPMI_SDR_HEADER_SIZE > sdrs_size) {
+ error_report("Problem with recid 0x%4.4x", i);
+ return;
+ }
+ sdrh = (struct ipmi_sdr_header *) &sdrs[i];
+ len = ipmi_sdr_length(sdrh);
+ if (i + len > sdrs_size) {
+ error_report("Problem with recid 0x%4.4x", i);
+ return;
+ }
+ sdr_add_entry(ibs, sdrh, len, NULL);
+ }
+}
+
+static const VMStateDescription vmstate_ipmi_sim = {
+ .name = TYPE_IPMI_BMC_SIMULATOR,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(bmc_global_enables, IPMIBmcSim),
+ VMSTATE_UINT8(msg_flags, IPMIBmcSim),
+ VMSTATE_BOOL(watchdog_initialized, IPMIBmcSim),
+ VMSTATE_UINT8(watchdog_use, IPMIBmcSim),
+ VMSTATE_UINT8(watchdog_action, IPMIBmcSim),
+ VMSTATE_UINT8(watchdog_pretimeout, IPMIBmcSim),
+ VMSTATE_BOOL(watchdog_expired, IPMIBmcSim),
+ VMSTATE_UINT16(watchdog_timeout, IPMIBmcSim),
+ VMSTATE_BOOL(watchdog_running, IPMIBmcSim),
+ VMSTATE_BOOL(watchdog_preaction_ran, IPMIBmcSim),
+ VMSTATE_INT64(watchdog_expiry, IPMIBmcSim),
+ VMSTATE_UINT8_ARRAY(evtbuf, IPMIBmcSim, 16),
+ VMSTATE_UINT8(sensors[IPMI_WATCHDOG_SENSOR].status, IPMIBmcSim),
+ VMSTATE_UINT8(sensors[IPMI_WATCHDOG_SENSOR].reading, IPMIBmcSim),
+ VMSTATE_UINT16(sensors[IPMI_WATCHDOG_SENSOR].states, IPMIBmcSim),
+ VMSTATE_UINT16(sensors[IPMI_WATCHDOG_SENSOR].assert_states, IPMIBmcSim),
+ VMSTATE_UINT16(sensors[IPMI_WATCHDOG_SENSOR].deassert_states,
+ IPMIBmcSim),
+ VMSTATE_UINT16(sensors[IPMI_WATCHDOG_SENSOR].assert_enable, IPMIBmcSim),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void ipmi_sim_realize(DeviceState *dev, Error **errp)
+{
+ IPMIBmc *b = IPMI_BMC(dev);
+ unsigned int i;
+ IPMIBmcSim *ibs = IPMI_BMC_SIMULATOR(b);
+
+ qemu_mutex_init(&ibs->lock);
+ QTAILQ_INIT(&ibs->rcvbufs);
+
+ ibs->bmc_global_enables = (1 << IPMI_BMC_EVENT_LOG_BIT);
+ ibs->device_id = 0x20;
+ ibs->ipmi_version = 0x02; /* IPMI 2.0 */
+ ibs->restart_cause = 0;
+ for (i = 0; i < 4; i++) {
+ ibs->sel.last_addition[i] = 0xff;
+ ibs->sel.last_clear[i] = 0xff;
+ ibs->sdr.last_addition[i] = 0xff;
+ ibs->sdr.last_clear[i] = 0xff;
+ }
+
+ ipmi_sdr_init(ibs);
+
+ ibs->acpi_power_state[0] = 0;
+ ibs->acpi_power_state[1] = 0;
+
+ if (qemu_uuid_set) {
+ memcpy(&ibs->uuid, qemu_uuid, 16);
+ } else {
+ memset(&ibs->uuid, 0, 16);
+ }
+
+ ipmi_init_sensors_from_sdrs(ibs);
+ register_cmds(ibs);
+
+ ibs->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ipmi_timeout, ibs);
+
+ vmstate_register(NULL, 0, &vmstate_ipmi_sim, ibs);
+}
+
+static void ipmi_sim_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ IPMIBmcClass *bk = IPMI_BMC_CLASS(oc);
+
+ dc->realize = ipmi_sim_realize;
+ bk->handle_command = ipmi_sim_handle_command;
+}
+
+static const TypeInfo ipmi_sim_type = {
+ .name = TYPE_IPMI_BMC_SIMULATOR,
+ .parent = TYPE_IPMI_BMC,
+ .instance_size = sizeof(IPMIBmcSim),
+ .class_init = ipmi_sim_class_init,
+};
+
+static void ipmi_sim_register_types(void)
+{
+ type_register_static(&ipmi_sim_type);
+}
+
+type_init(ipmi_sim_register_types)
diff --git a/qemu/hw/ipmi/isa_ipmi_bt.c b/qemu/hw/ipmi/isa_ipmi_bt.c
new file mode 100644
index 000000000..aaea12ecd
--- /dev/null
+++ b/qemu/hw/ipmi/isa_ipmi_bt.c
@@ -0,0 +1,530 @@
+/*
+ * QEMU ISA IPMI BT emulation
+ *
+ * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
+ *
+ * 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/hw.h"
+#include "hw/ipmi/ipmi.h"
+#include "hw/isa/isa.h"
+#include "hw/i386/pc.h"
+
+/* Control register */
+#define IPMI_BT_CLR_WR_BIT 0
+#define IPMI_BT_CLR_RD_BIT 1
+#define IPMI_BT_H2B_ATN_BIT 2
+#define IPMI_BT_B2H_ATN_BIT 3
+#define IPMI_BT_SMS_ATN_BIT 4
+#define IPMI_BT_HBUSY_BIT 6
+#define IPMI_BT_BBUSY_BIT 7
+
+#define IPMI_BT_CLR_WR_MASK (1 << IPMI_BT_CLR_WR_BIT)
+#define IPMI_BT_GET_CLR_WR(d) (((d) >> IPMI_BT_CLR_WR_BIT) & 0x1)
+#define IPMI_BT_SET_CLR_WR(d, v) (d) = (((d) & ~IPMI_BT_CLR_WR_MASK) | \
+ (((v & 1) << IPMI_BT_CLR_WR_BIT)))
+
+#define IPMI_BT_CLR_RD_MASK (1 << IPMI_BT_CLR_RD_BIT)
+#define IPMI_BT_GET_CLR_RD(d) (((d) >> IPMI_BT_CLR_RD_BIT) & 0x1)
+#define IPMI_BT_SET_CLR_RD(d, v) (d) = (((d) & ~IPMI_BT_CLR_RD_MASK) | \
+ (((v & 1) << IPMI_BT_CLR_RD_BIT)))
+
+#define IPMI_BT_H2B_ATN_MASK (1 << IPMI_BT_H2B_ATN_BIT)
+#define IPMI_BT_GET_H2B_ATN(d) (((d) >> IPMI_BT_H2B_ATN_BIT) & 0x1)
+#define IPMI_BT_SET_H2B_ATN(d, v) (d) = (((d) & ~IPMI_BT_H2B_ATN_MASK) | \
+ (((v & 1) << IPMI_BT_H2B_ATN_BIT)))
+
+#define IPMI_BT_B2H_ATN_MASK (1 << IPMI_BT_B2H_ATN_BIT)
+#define IPMI_BT_GET_B2H_ATN(d) (((d) >> IPMI_BT_B2H_ATN_BIT) & 0x1)
+#define IPMI_BT_SET_B2H_ATN(d, v) (d) = (((d) & ~IPMI_BT_B2H_ATN_MASK) | \
+ (((v & 1) << IPMI_BT_B2H_ATN_BIT)))
+
+#define IPMI_BT_SMS_ATN_MASK (1 << IPMI_BT_SMS_ATN_BIT)
+#define IPMI_BT_GET_SMS_ATN(d) (((d) >> IPMI_BT_SMS_ATN_BIT) & 0x1)
+#define IPMI_BT_SET_SMS_ATN(d, v) (d) = (((d) & ~IPMI_BT_SMS_ATN_MASK) | \
+ (((v & 1) << IPMI_BT_SMS_ATN_BIT)))
+
+#define IPMI_BT_HBUSY_MASK (1 << IPMI_BT_HBUSY_BIT)
+#define IPMI_BT_GET_HBUSY(d) (((d) >> IPMI_BT_HBUSY_BIT) & 0x1)
+#define IPMI_BT_SET_HBUSY(d, v) (d) = (((d) & ~IPMI_BT_HBUSY_MASK) | \
+ (((v & 1) << IPMI_BT_HBUSY_BIT)))
+
+#define IPMI_BT_BBUSY_MASK (1 << IPMI_BT_BBUSY_BIT)
+#define IPMI_BT_GET_BBUSY(d) (((d) >> IPMI_BT_BBUSY_BIT) & 0x1)
+#define IPMI_BT_SET_BBUSY(d, v) (d) = (((d) & ~IPMI_BT_BBUSY_MASK) | \
+ (((v & 1) << IPMI_BT_BBUSY_BIT)))
+
+
+/* Mask register */
+#define IPMI_BT_B2H_IRQ_EN_BIT 0
+#define IPMI_BT_B2H_IRQ_BIT 1
+
+#define IPMI_BT_B2H_IRQ_EN_MASK (1 << IPMI_BT_B2H_IRQ_EN_BIT)
+#define IPMI_BT_GET_B2H_IRQ_EN(d) (((d) >> IPMI_BT_B2H_IRQ_EN_BIT) & 0x1)
+#define IPMI_BT_SET_B2H_IRQ_EN(d, v) (d) = (((d) & ~IPMI_BT_B2H_IRQ_EN_MASK) | \
+ (((v & 1) << IPMI_BT_B2H_IRQ_EN_BIT)))
+
+#define IPMI_BT_B2H_IRQ_MASK (1 << IPMI_BT_B2H_IRQ_BIT)
+#define IPMI_BT_GET_B2H_IRQ(d) (((d) >> IPMI_BT_B2H_IRQ_BIT) & 0x1)
+#define IPMI_BT_SET_B2H_IRQ(d, v) (d) = (((d) & ~IPMI_BT_B2H_IRQ_MASK) | \
+ (((v & 1) << IPMI_BT_B2H_IRQ_BIT)))
+
+typedef struct IPMIBT {
+ IPMIBmc *bmc;
+
+ bool do_wake;
+
+ qemu_irq irq;
+
+ uint32_t io_base;
+ unsigned long io_length;
+ MemoryRegion io;
+
+ bool obf_irq_set;
+ bool atn_irq_set;
+ bool use_irq;
+ bool irqs_enabled;
+
+ uint8_t outmsg[MAX_IPMI_MSG_SIZE];
+ uint32_t outpos;
+ uint32_t outlen;
+
+ uint8_t inmsg[MAX_IPMI_MSG_SIZE];
+ uint32_t inlen;
+
+ uint8_t control_reg;
+ uint8_t mask_reg;
+
+ /*
+ * This is a response number that we send with the command to make
+ * sure that the response matches the command.
+ */
+ uint8_t waiting_rsp;
+ uint8_t waiting_seq;
+} IPMIBT;
+
+#define IPMI_CMD_GET_BT_INTF_CAP 0x36
+
+static void ipmi_bt_handle_event(IPMIInterface *ii)
+{
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+ IPMIBT *ib = iic->get_backend_data(ii);
+
+ if (ib->inlen < 4) {
+ goto out;
+ }
+ /* Note that overruns are handled by handle_command */
+ if (ib->inmsg[0] != (ib->inlen - 1)) {
+ /* Length mismatch, just ignore. */
+ IPMI_BT_SET_BBUSY(ib->control_reg, 1);
+ ib->inlen = 0;
+ goto out;
+ }
+ if ((ib->inmsg[1] == (IPMI_NETFN_APP << 2)) &&
+ (ib->inmsg[3] == IPMI_CMD_GET_BT_INTF_CAP)) {
+ /* We handle this one ourselves. */
+ ib->outmsg[0] = 9;
+ ib->outmsg[1] = ib->inmsg[1] | 0x04;
+ ib->outmsg[2] = ib->inmsg[2];
+ ib->outmsg[3] = ib->inmsg[3];
+ ib->outmsg[4] = 0;
+ ib->outmsg[5] = 1; /* Only support 1 outstanding request. */
+ if (sizeof(ib->inmsg) > 0xff) { /* Input buffer size */
+ ib->outmsg[6] = 0xff;
+ } else {
+ ib->outmsg[6] = (unsigned char) sizeof(ib->inmsg);
+ }
+ if (sizeof(ib->outmsg) > 0xff) { /* Output buffer size */
+ ib->outmsg[7] = 0xff;
+ } else {
+ ib->outmsg[7] = (unsigned char) sizeof(ib->outmsg);
+ }
+ ib->outmsg[8] = 10; /* Max request to response time */
+ ib->outmsg[9] = 0; /* Don't recommend retries */
+ ib->outlen = 10;
+ IPMI_BT_SET_BBUSY(ib->control_reg, 0);
+ IPMI_BT_SET_B2H_ATN(ib->control_reg, 1);
+ if (ib->use_irq && ib->irqs_enabled &&
+ !IPMI_BT_GET_B2H_IRQ(ib->mask_reg) &&
+ IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) {
+ IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1);
+ qemu_irq_raise(ib->irq);
+ }
+ goto out;
+ }
+ ib->waiting_seq = ib->inmsg[2];
+ ib->inmsg[2] = ib->inmsg[1];
+ {
+ IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(ib->bmc);
+ bk->handle_command(ib->bmc, ib->inmsg + 2, ib->inlen - 2,
+ sizeof(ib->inmsg), ib->waiting_rsp);
+ }
+ out:
+ return;
+}
+
+static void ipmi_bt_handle_rsp(IPMIInterface *ii, uint8_t msg_id,
+ unsigned char *rsp, unsigned int rsp_len)
+{
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+ IPMIBT *ib = iic->get_backend_data(ii);
+
+ if (ib->waiting_rsp == msg_id) {
+ ib->waiting_rsp++;
+ if (rsp_len > (sizeof(ib->outmsg) - 2)) {
+ ib->outmsg[0] = 4;
+ ib->outmsg[1] = rsp[0];
+ ib->outmsg[2] = ib->waiting_seq;
+ ib->outmsg[3] = rsp[1];
+ ib->outmsg[4] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES;
+ ib->outlen = 5;
+ } else {
+ ib->outmsg[0] = rsp_len + 1;
+ ib->outmsg[1] = rsp[0];
+ ib->outmsg[2] = ib->waiting_seq;
+ memcpy(ib->outmsg + 3, rsp + 1, rsp_len - 1);
+ ib->outlen = rsp_len + 2;
+ }
+ IPMI_BT_SET_BBUSY(ib->control_reg, 0);
+ IPMI_BT_SET_B2H_ATN(ib->control_reg, 1);
+ if (ib->use_irq && ib->irqs_enabled &&
+ !IPMI_BT_GET_B2H_IRQ(ib->mask_reg) &&
+ IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) {
+ IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1);
+ qemu_irq_raise(ib->irq);
+ }
+ }
+}
+
+
+static uint64_t ipmi_bt_ioport_read(void *opaque, hwaddr addr, unsigned size)
+{
+ IPMIInterface *ii = opaque;
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+ IPMIBT *ib = iic->get_backend_data(ii);
+ uint32_t ret = 0xff;
+
+ switch (addr & 3) {
+ case 0:
+ ret = ib->control_reg;
+ break;
+ case 1:
+ if (ib->outpos < ib->outlen) {
+ ret = ib->outmsg[ib->outpos];
+ ib->outpos++;
+ if (ib->outpos == ib->outlen) {
+ ib->outpos = 0;
+ ib->outlen = 0;
+ }
+ } else {
+ ret = 0xff;
+ }
+ break;
+ case 2:
+ ret = ib->mask_reg;
+ break;
+ }
+ return ret;
+}
+
+static void ipmi_bt_signal(IPMIBT *ib, IPMIInterface *ii)
+{
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+
+ ib->do_wake = 1;
+ while (ib->do_wake) {
+ ib->do_wake = 0;
+ iic->handle_if_event(ii);
+ }
+}
+
+static void ipmi_bt_ioport_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ IPMIInterface *ii = opaque;
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+ IPMIBT *ib = iic->get_backend_data(ii);
+
+ switch (addr & 3) {
+ case 0:
+ if (IPMI_BT_GET_CLR_WR(val)) {
+ ib->inlen = 0;
+ }
+ if (IPMI_BT_GET_CLR_RD(val)) {
+ ib->outpos = 0;
+ }
+ if (IPMI_BT_GET_B2H_ATN(val)) {
+ IPMI_BT_SET_B2H_ATN(ib->control_reg, 0);
+ }
+ if (IPMI_BT_GET_SMS_ATN(val)) {
+ IPMI_BT_SET_SMS_ATN(ib->control_reg, 0);
+ }
+ if (IPMI_BT_GET_HBUSY(val)) {
+ /* Toggle */
+ IPMI_BT_SET_HBUSY(ib->control_reg,
+ !IPMI_BT_GET_HBUSY(ib->control_reg));
+ }
+ if (IPMI_BT_GET_H2B_ATN(val)) {
+ IPMI_BT_SET_BBUSY(ib->control_reg, 1);
+ ipmi_bt_signal(ib, ii);
+ }
+ break;
+
+ case 1:
+ if (ib->inlen < sizeof(ib->inmsg)) {
+ ib->inmsg[ib->inlen] = val;
+ }
+ ib->inlen++;
+ break;
+
+ case 2:
+ if (IPMI_BT_GET_B2H_IRQ_EN(val) !=
+ IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) {
+ if (IPMI_BT_GET_B2H_IRQ_EN(val)) {
+ if (IPMI_BT_GET_B2H_ATN(ib->control_reg) ||
+ IPMI_BT_GET_SMS_ATN(ib->control_reg)) {
+ IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1);
+ qemu_irq_raise(ib->irq);
+ }
+ IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 1);
+ } else {
+ if (IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) {
+ IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0);
+ qemu_irq_lower(ib->irq);
+ }
+ IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 0);
+ }
+ }
+ if (IPMI_BT_GET_B2H_IRQ(val) && IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) {
+ IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0);
+ qemu_irq_lower(ib->irq);
+ }
+ break;
+ }
+}
+
+static const MemoryRegionOps ipmi_bt_io_ops = {
+ .read = ipmi_bt_ioport_read,
+ .write = ipmi_bt_ioport_write,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 1,
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void ipmi_bt_set_atn(IPMIInterface *ii, int val, int irq)
+{
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+ IPMIBT *ib = iic->get_backend_data(ii);
+
+ if (!!val == IPMI_BT_GET_SMS_ATN(ib->control_reg)) {
+ return;
+ }
+
+ IPMI_BT_SET_SMS_ATN(ib->control_reg, val);
+ if (val) {
+ if (irq && ib->use_irq && ib->irqs_enabled &&
+ !IPMI_BT_GET_B2H_ATN(ib->control_reg) &&
+ IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) {
+ IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1);
+ qemu_irq_raise(ib->irq);
+ }
+ } else {
+ if (!IPMI_BT_GET_B2H_ATN(ib->control_reg) &&
+ IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) {
+ IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0);
+ qemu_irq_lower(ib->irq);
+ }
+ }
+}
+
+static void ipmi_bt_handle_reset(IPMIInterface *ii, bool is_cold)
+{
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+ IPMIBT *ib = iic->get_backend_data(ii);
+
+ if (is_cold) {
+ /* Disable the BT interrupt on reset */
+ if (IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) {
+ IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0);
+ qemu_irq_lower(ib->irq);
+ }
+ IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 0);
+ }
+}
+
+static void ipmi_bt_set_irq_enable(IPMIInterface *ii, int val)
+{
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+ IPMIBT *ib = iic->get_backend_data(ii);
+
+ ib->irqs_enabled = val;
+}
+
+static void ipmi_bt_init(IPMIInterface *ii, Error **errp)
+{
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+ IPMIBT *ib = iic->get_backend_data(ii);
+
+ ib->io_length = 3;
+
+ memory_region_init_io(&ib->io, NULL, &ipmi_bt_io_ops, ii, "ipmi-bt", 3);
+}
+
+static void ipmi_bt_class_init(IPMIInterfaceClass *iic)
+{
+ iic->init = ipmi_bt_init;
+ iic->set_atn = ipmi_bt_set_atn;
+ iic->handle_rsp = ipmi_bt_handle_rsp;
+ iic->handle_if_event = ipmi_bt_handle_event;
+ iic->set_irq_enable = ipmi_bt_set_irq_enable;
+ iic->reset = ipmi_bt_handle_reset;
+}
+
+
+#define TYPE_ISA_IPMI_BT "isa-ipmi-bt"
+#define ISA_IPMI_BT(obj) OBJECT_CHECK(ISAIPMIBTDevice, (obj), \
+ TYPE_ISA_IPMI_BT)
+
+typedef struct ISAIPMIBTDevice {
+ ISADevice dev;
+ int32_t isairq;
+ IPMIBT bt;
+ IPMIFwInfo fwinfo;
+} ISAIPMIBTDevice;
+
+static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp)
+{
+ ISADevice *isadev = ISA_DEVICE(dev);
+ ISAIPMIBTDevice *iib = ISA_IPMI_BT(dev);
+ IPMIInterface *ii = IPMI_INTERFACE(dev);
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+
+ if (!iib->bt.bmc) {
+ error_setg(errp, "IPMI device requires a bmc attribute to be set");
+ return;
+ }
+
+ iib->bt.bmc->intf = ii;
+
+ iic->init(ii, errp);
+ if (*errp)
+ return;
+
+ if (iib->isairq > 0) {
+ isa_init_irq(isadev, &iib->bt.irq, iib->isairq);
+ iib->bt.use_irq = 1;
+ }
+
+ qdev_set_legacy_instance_id(dev, iib->bt.io_base, iib->bt.io_length);
+
+ isa_register_ioport(isadev, &iib->bt.io, iib->bt.io_base);
+
+ iib->fwinfo.interface_name = "bt";
+ iib->fwinfo.interface_type = IPMI_SMBIOS_BT;
+ iib->fwinfo.ipmi_spec_major_revision = 2;
+ iib->fwinfo.ipmi_spec_minor_revision = 0;
+ iib->fwinfo.base_address = iib->bt.io_base;
+ iib->fwinfo.register_length = iib->bt.io_length;
+ iib->fwinfo.register_spacing = 1;
+ iib->fwinfo.memspace = IPMI_MEMSPACE_IO;
+ iib->fwinfo.irq_type = IPMI_LEVEL_IRQ;
+ iib->fwinfo.interrupt_number = iib->isairq;
+ iib->fwinfo.acpi_parent = "\\_SB.PCI0.ISA";
+ iib->fwinfo.i2c_slave_address = iib->bt.bmc->slave_addr;
+ ipmi_add_fwinfo(&iib->fwinfo, errp);
+}
+
+static const VMStateDescription vmstate_ISAIPMIBTDevice = {
+ .name = TYPE_IPMI_INTERFACE,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_BOOL(bt.obf_irq_set, ISAIPMIBTDevice),
+ VMSTATE_BOOL(bt.atn_irq_set, ISAIPMIBTDevice),
+ VMSTATE_BOOL(bt.use_irq, ISAIPMIBTDevice),
+ VMSTATE_BOOL(bt.irqs_enabled, ISAIPMIBTDevice),
+ VMSTATE_UINT32(bt.outpos, ISAIPMIBTDevice),
+ VMSTATE_VBUFFER_UINT32(bt.outmsg, ISAIPMIBTDevice, 1, NULL, 0,
+ bt.outlen),
+ VMSTATE_VBUFFER_UINT32(bt.inmsg, ISAIPMIBTDevice, 1, NULL, 0,
+ bt.inlen),
+ VMSTATE_UINT8(bt.control_reg, ISAIPMIBTDevice),
+ VMSTATE_UINT8(bt.mask_reg, ISAIPMIBTDevice),
+ VMSTATE_UINT8(bt.waiting_rsp, ISAIPMIBTDevice),
+ VMSTATE_UINT8(bt.waiting_seq, ISAIPMIBTDevice),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void isa_ipmi_bt_init(Object *obj)
+{
+ ISAIPMIBTDevice *iib = ISA_IPMI_BT(obj);
+
+ ipmi_bmc_find_and_link(obj, (Object **) &iib->bt.bmc);
+
+ vmstate_register(NULL, 0, &vmstate_ISAIPMIBTDevice, iib);
+}
+
+static void *isa_ipmi_bt_get_backend_data(IPMIInterface *ii)
+{
+ ISAIPMIBTDevice *iib = ISA_IPMI_BT(ii);
+
+ return &iib->bt;
+}
+
+static Property ipmi_isa_properties[] = {
+ DEFINE_PROP_UINT32("ioport", ISAIPMIBTDevice, bt.io_base, 0xe4),
+ DEFINE_PROP_INT32("irq", ISAIPMIBTDevice, isairq, 5),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void isa_ipmi_bt_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc);
+
+ dc->realize = isa_ipmi_bt_realize;
+ dc->props = ipmi_isa_properties;
+
+ iic->get_backend_data = isa_ipmi_bt_get_backend_data;
+ ipmi_bt_class_init(iic);
+}
+
+static const TypeInfo isa_ipmi_bt_info = {
+ .name = TYPE_ISA_IPMI_BT,
+ .parent = TYPE_ISA_DEVICE,
+ .instance_size = sizeof(ISAIPMIBTDevice),
+ .instance_init = isa_ipmi_bt_init,
+ .class_init = isa_ipmi_bt_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_IPMI_INTERFACE },
+ { }
+ }
+};
+
+static void ipmi_register_types(void)
+{
+ type_register_static(&isa_ipmi_bt_info);
+}
+
+type_init(ipmi_register_types)
diff --git a/qemu/hw/ipmi/isa_ipmi_kcs.c b/qemu/hw/ipmi/isa_ipmi_kcs.c
new file mode 100644
index 000000000..2742ce06c
--- /dev/null
+++ b/qemu/hw/ipmi/isa_ipmi_kcs.c
@@ -0,0 +1,495 @@
+/*
+ * QEMU ISA IPMI KCS emulation
+ *
+ * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
+ *
+ * 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/hw.h"
+#include "hw/ipmi/ipmi.h"
+#include "hw/isa/isa.h"
+#include "hw/i386/pc.h"
+
+#define IPMI_KCS_OBF_BIT 0
+#define IPMI_KCS_IBF_BIT 1
+#define IPMI_KCS_SMS_ATN_BIT 2
+#define IPMI_KCS_CD_BIT 3
+
+#define IPMI_KCS_OBF_MASK (1 << IPMI_KCS_OBF_BIT)
+#define IPMI_KCS_GET_OBF(d) (((d) >> IPMI_KCS_OBF_BIT) & 0x1)
+#define IPMI_KCS_SET_OBF(d, v) (d) = (((d) & ~IPMI_KCS_OBF_MASK) | \
+ (((v) & 1) << IPMI_KCS_OBF_BIT))
+#define IPMI_KCS_IBF_MASK (1 << IPMI_KCS_IBF_BIT)
+#define IPMI_KCS_GET_IBF(d) (((d) >> IPMI_KCS_IBF_BIT) & 0x1)
+#define IPMI_KCS_SET_IBF(d, v) (d) = (((d) & ~IPMI_KCS_IBF_MASK) | \
+ (((v) & 1) << IPMI_KCS_IBF_BIT))
+#define IPMI_KCS_SMS_ATN_MASK (1 << IPMI_KCS_SMS_ATN_BIT)
+#define IPMI_KCS_GET_SMS_ATN(d) (((d) >> IPMI_KCS_SMS_ATN_BIT) & 0x1)
+#define IPMI_KCS_SET_SMS_ATN(d, v) (d) = (((d) & ~IPMI_KCS_SMS_ATN_MASK) | \
+ (((v) & 1) << IPMI_KCS_SMS_ATN_BIT))
+#define IPMI_KCS_CD_MASK (1 << IPMI_KCS_CD_BIT)
+#define IPMI_KCS_GET_CD(d) (((d) >> IPMI_KCS_CD_BIT) & 0x1)
+#define IPMI_KCS_SET_CD(d, v) (d) = (((d) & ~IPMI_KCS_CD_MASK) | \
+ (((v) & 1) << IPMI_KCS_CD_BIT))
+
+#define IPMI_KCS_IDLE_STATE 0
+#define IPMI_KCS_READ_STATE 1
+#define IPMI_KCS_WRITE_STATE 2
+#define IPMI_KCS_ERROR_STATE 3
+
+#define IPMI_KCS_GET_STATE(d) (((d) >> 6) & 0x3)
+#define IPMI_KCS_SET_STATE(d, v) ((d) = ((d) & ~0xc0) | (((v) & 0x3) << 6))
+
+#define IPMI_KCS_ABORT_STATUS_CMD 0x60
+#define IPMI_KCS_WRITE_START_CMD 0x61
+#define IPMI_KCS_WRITE_END_CMD 0x62
+#define IPMI_KCS_READ_CMD 0x68
+
+#define IPMI_KCS_STATUS_NO_ERR 0x00
+#define IPMI_KCS_STATUS_ABORTED_ERR 0x01
+#define IPMI_KCS_STATUS_BAD_CC_ERR 0x02
+#define IPMI_KCS_STATUS_LENGTH_ERR 0x06
+
+typedef struct IPMIKCS {
+ IPMIBmc *bmc;
+
+ bool do_wake;
+
+ qemu_irq irq;
+
+ uint32_t io_base;
+ unsigned long io_length;
+ MemoryRegion io;
+
+ bool obf_irq_set;
+ bool atn_irq_set;
+ bool use_irq;
+ bool irqs_enabled;
+
+ uint8_t outmsg[MAX_IPMI_MSG_SIZE];
+ uint32_t outpos;
+ uint32_t outlen;
+
+ uint8_t inmsg[MAX_IPMI_MSG_SIZE];
+ uint32_t inlen;
+ bool write_end;
+
+ uint8_t status_reg;
+ uint8_t data_out_reg;
+
+ int16_t data_in_reg; /* -1 means not written */
+ int16_t cmd_reg;
+
+ /*
+ * This is a response number that we send with the command to make
+ * sure that the response matches the command.
+ */
+ uint8_t waiting_rsp;
+} IPMIKCS;
+
+#define SET_OBF() \
+ do { \
+ IPMI_KCS_SET_OBF(ik->status_reg, 1); \
+ if (ik->use_irq && ik->irqs_enabled && !ik->obf_irq_set) { \
+ ik->obf_irq_set = 1; \
+ if (!ik->atn_irq_set) { \
+ qemu_irq_raise(ik->irq); \
+ } \
+ } \
+ } while (0)
+
+static void ipmi_kcs_signal(IPMIKCS *ik, IPMIInterface *ii)
+{
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+
+ ik->do_wake = 1;
+ while (ik->do_wake) {
+ ik->do_wake = 0;
+ iic->handle_if_event(ii);
+ }
+}
+
+static void ipmi_kcs_handle_event(IPMIInterface *ii)
+{
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+ IPMIKCS *ik = iic->get_backend_data(ii);
+
+ if (ik->cmd_reg == IPMI_KCS_ABORT_STATUS_CMD) {
+ if (IPMI_KCS_GET_STATE(ik->status_reg) != IPMI_KCS_ERROR_STATE) {
+ ik->waiting_rsp++; /* Invalidate the message */
+ ik->outmsg[0] = IPMI_KCS_STATUS_ABORTED_ERR;
+ ik->outlen = 1;
+ ik->outpos = 0;
+ IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE);
+ SET_OBF();
+ }
+ goto out;
+ }
+
+ switch (IPMI_KCS_GET_STATE(ik->status_reg)) {
+ case IPMI_KCS_IDLE_STATE:
+ if (ik->cmd_reg == IPMI_KCS_WRITE_START_CMD) {
+ IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_WRITE_STATE);
+ ik->cmd_reg = -1;
+ ik->write_end = 0;
+ ik->inlen = 0;
+ SET_OBF();
+ }
+ break;
+
+ case IPMI_KCS_READ_STATE:
+ handle_read:
+ if (ik->outpos >= ik->outlen) {
+ IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_IDLE_STATE);
+ SET_OBF();
+ } else if (ik->data_in_reg == IPMI_KCS_READ_CMD) {
+ ik->data_out_reg = ik->outmsg[ik->outpos];
+ ik->outpos++;
+ SET_OBF();
+ } else {
+ ik->outmsg[0] = IPMI_KCS_STATUS_BAD_CC_ERR;
+ ik->outlen = 1;
+ ik->outpos = 0;
+ IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE);
+ SET_OBF();
+ goto out;
+ }
+ break;
+
+ case IPMI_KCS_WRITE_STATE:
+ if (ik->data_in_reg != -1) {
+ /*
+ * Don't worry about input overrun here, that will be
+ * handled in the BMC.
+ */
+ if (ik->inlen < sizeof(ik->inmsg)) {
+ ik->inmsg[ik->inlen] = ik->data_in_reg;
+ }
+ ik->inlen++;
+ }
+ if (ik->write_end) {
+ IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(ik->bmc);
+ ik->outlen = 0;
+ ik->write_end = 0;
+ ik->outpos = 0;
+ bk->handle_command(ik->bmc, ik->inmsg, ik->inlen, sizeof(ik->inmsg),
+ ik->waiting_rsp);
+ goto out_noibf;
+ } else if (ik->cmd_reg == IPMI_KCS_WRITE_END_CMD) {
+ ik->cmd_reg = -1;
+ ik->write_end = 1;
+ }
+ SET_OBF();
+ break;
+
+ case IPMI_KCS_ERROR_STATE:
+ if (ik->data_in_reg != -1) {
+ IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_READ_STATE);
+ ik->data_in_reg = IPMI_KCS_READ_CMD;
+ goto handle_read;
+ }
+ break;
+ }
+
+ if (ik->cmd_reg != -1) {
+ /* Got an invalid command */
+ ik->outmsg[0] = IPMI_KCS_STATUS_BAD_CC_ERR;
+ ik->outlen = 1;
+ ik->outpos = 0;
+ IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE);
+ }
+
+ out:
+ ik->cmd_reg = -1;
+ ik->data_in_reg = -1;
+ IPMI_KCS_SET_IBF(ik->status_reg, 0);
+ out_noibf:
+ return;
+}
+
+static void ipmi_kcs_handle_rsp(IPMIInterface *ii, uint8_t msg_id,
+ unsigned char *rsp, unsigned int rsp_len)
+{
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+ IPMIKCS *ik = iic->get_backend_data(ii);
+
+ if (ik->waiting_rsp == msg_id) {
+ ik->waiting_rsp++;
+ if (rsp_len > sizeof(ik->outmsg)) {
+ ik->outmsg[0] = rsp[0];
+ ik->outmsg[1] = rsp[1];
+ ik->outmsg[2] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES;
+ ik->outlen = 3;
+ } else {
+ memcpy(ik->outmsg, rsp, rsp_len);
+ ik->outlen = rsp_len;
+ }
+ IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_READ_STATE);
+ ik->data_in_reg = IPMI_KCS_READ_CMD;
+ ipmi_kcs_signal(ik, ii);
+ }
+}
+
+
+static uint64_t ipmi_kcs_ioport_read(void *opaque, hwaddr addr, unsigned size)
+{
+ IPMIInterface *ii = opaque;
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+ IPMIKCS *ik = iic->get_backend_data(ii);
+ uint32_t ret;
+
+ switch (addr & 1) {
+ case 0:
+ ret = ik->data_out_reg;
+ IPMI_KCS_SET_OBF(ik->status_reg, 0);
+ if (ik->obf_irq_set) {
+ ik->obf_irq_set = 0;
+ if (!ik->atn_irq_set) {
+ qemu_irq_lower(ik->irq);
+ }
+ }
+ break;
+ case 1:
+ ret = ik->status_reg;
+ if (ik->atn_irq_set) {
+ ik->atn_irq_set = 0;
+ if (!ik->obf_irq_set) {
+ qemu_irq_lower(ik->irq);
+ }
+ }
+ break;
+ }
+ return ret;
+}
+
+static void ipmi_kcs_ioport_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ IPMIInterface *ii = opaque;
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+ IPMIKCS *ik = iic->get_backend_data(ii);
+
+ if (IPMI_KCS_GET_IBF(ik->status_reg)) {
+ return;
+ }
+
+ switch (addr & 1) {
+ case 0:
+ ik->data_in_reg = val;
+ break;
+
+ case 1:
+ ik->cmd_reg = val;
+ break;
+ }
+ IPMI_KCS_SET_IBF(ik->status_reg, 1);
+ ipmi_kcs_signal(ik, ii);
+}
+
+const MemoryRegionOps ipmi_kcs_io_ops = {
+ .read = ipmi_kcs_ioport_read,
+ .write = ipmi_kcs_ioport_write,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 1,
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void ipmi_kcs_set_atn(IPMIInterface *ii, int val, int irq)
+{
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+ IPMIKCS *ik = iic->get_backend_data(ii);
+
+ IPMI_KCS_SET_SMS_ATN(ik->status_reg, val);
+ if (val) {
+ if (irq && !ik->atn_irq_set && ik->use_irq && ik->irqs_enabled) {
+ ik->atn_irq_set = 1;
+ if (!ik->obf_irq_set) {
+ qemu_irq_raise(ik->irq);
+ }
+ }
+ } else {
+ if (ik->atn_irq_set) {
+ ik->atn_irq_set = 0;
+ if (!ik->obf_irq_set) {
+ qemu_irq_lower(ik->irq);
+ }
+ }
+ }
+}
+
+static void ipmi_kcs_set_irq_enable(IPMIInterface *ii, int val)
+{
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+ IPMIKCS *ik = iic->get_backend_data(ii);
+
+ ik->irqs_enabled = val;
+}
+
+static void ipmi_kcs_init(IPMIInterface *ii, Error **errp)
+{
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+ IPMIKCS *ik = iic->get_backend_data(ii);
+
+ ik->io_length = 2;
+ memory_region_init_io(&ik->io, NULL, &ipmi_kcs_io_ops, ii, "ipmi-kcs", 2);
+}
+
+static void ipmi_kcs_class_init(IPMIInterfaceClass *iic)
+{
+ iic->init = ipmi_kcs_init;
+ iic->set_atn = ipmi_kcs_set_atn;
+ iic->handle_rsp = ipmi_kcs_handle_rsp;
+ iic->handle_if_event = ipmi_kcs_handle_event;
+ iic->set_irq_enable = ipmi_kcs_set_irq_enable;
+}
+
+
+#define TYPE_ISA_IPMI_KCS "isa-ipmi-kcs"
+#define ISA_IPMI_KCS(obj) OBJECT_CHECK(ISAIPMIKCSDevice, (obj), \
+ TYPE_ISA_IPMI_KCS)
+
+typedef struct ISAIPMIKCSDevice {
+ ISADevice dev;
+ int32_t isairq;
+ IPMIKCS kcs;
+ IPMIFwInfo fwinfo;
+} ISAIPMIKCSDevice;
+
+static void ipmi_isa_realize(DeviceState *dev, Error **errp)
+{
+ ISADevice *isadev = ISA_DEVICE(dev);
+ ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(dev);
+ IPMIInterface *ii = IPMI_INTERFACE(dev);
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
+
+ if (!iik->kcs.bmc) {
+ error_setg(errp, "IPMI device requires a bmc attribute to be set");
+ return;
+ }
+
+ iik->kcs.bmc->intf = ii;
+
+ iic->init(ii, errp);
+ if (*errp)
+ return;
+
+ if (iik->isairq > 0) {
+ isa_init_irq(isadev, &iik->kcs.irq, iik->isairq);
+ iik->kcs.use_irq = 1;
+ }
+
+ qdev_set_legacy_instance_id(dev, iik->kcs.io_base, iik->kcs.io_length);
+
+ isa_register_ioport(isadev, &iik->kcs.io, iik->kcs.io_base);
+
+ iik->fwinfo.interface_name = "kcs";
+ iik->fwinfo.interface_type = IPMI_SMBIOS_KCS;
+ iik->fwinfo.ipmi_spec_major_revision = 2;
+ iik->fwinfo.ipmi_spec_minor_revision = 0;
+ iik->fwinfo.base_address = iik->kcs.io_base;
+ iik->fwinfo.i2c_slave_address = iik->kcs.bmc->slave_addr;
+ iik->fwinfo.register_length = iik->kcs.io_length;
+ iik->fwinfo.register_spacing = 1;
+ iik->fwinfo.memspace = IPMI_MEMSPACE_IO;
+ iik->fwinfo.irq_type = IPMI_LEVEL_IRQ;
+ iik->fwinfo.interrupt_number = iik->isairq;
+ iik->fwinfo.acpi_parent = "\\_SB.PCI0.ISA";
+ ipmi_add_fwinfo(&iik->fwinfo, errp);
+}
+
+const VMStateDescription vmstate_ISAIPMIKCSDevice = {
+ .name = TYPE_IPMI_INTERFACE,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_BOOL(kcs.obf_irq_set, ISAIPMIKCSDevice),
+ VMSTATE_BOOL(kcs.atn_irq_set, ISAIPMIKCSDevice),
+ VMSTATE_BOOL(kcs.use_irq, ISAIPMIKCSDevice),
+ VMSTATE_BOOL(kcs.irqs_enabled, ISAIPMIKCSDevice),
+ VMSTATE_UINT32(kcs.outpos, ISAIPMIKCSDevice),
+ VMSTATE_VBUFFER_UINT32(kcs.outmsg, ISAIPMIKCSDevice, 1, NULL, 0,
+ kcs.outlen),
+ VMSTATE_VBUFFER_UINT32(kcs.inmsg, ISAIPMIKCSDevice, 1, NULL, 0,
+ kcs.inlen),
+ VMSTATE_BOOL(kcs.write_end, ISAIPMIKCSDevice),
+ VMSTATE_UINT8(kcs.status_reg, ISAIPMIKCSDevice),
+ VMSTATE_UINT8(kcs.data_out_reg, ISAIPMIKCSDevice),
+ VMSTATE_INT16(kcs.data_in_reg, ISAIPMIKCSDevice),
+ VMSTATE_INT16(kcs.cmd_reg, ISAIPMIKCSDevice),
+ VMSTATE_UINT8(kcs.waiting_rsp, ISAIPMIKCSDevice),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void isa_ipmi_kcs_init(Object *obj)
+{
+ ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(obj);
+
+ ipmi_bmc_find_and_link(obj, (Object **) &iik->kcs.bmc);
+
+ vmstate_register(NULL, 0, &vmstate_ISAIPMIKCSDevice, iik);
+}
+
+static void *isa_ipmi_kcs_get_backend_data(IPMIInterface *ii)
+{
+ ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(ii);
+
+ return &iik->kcs;
+}
+
+static Property ipmi_isa_properties[] = {
+ DEFINE_PROP_UINT32("ioport", ISAIPMIKCSDevice, kcs.io_base, 0xca2),
+ DEFINE_PROP_INT32("irq", ISAIPMIKCSDevice, isairq, 5),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void isa_ipmi_kcs_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc);
+
+ dc->realize = ipmi_isa_realize;
+ dc->props = ipmi_isa_properties;
+
+ iic->get_backend_data = isa_ipmi_kcs_get_backend_data;
+ ipmi_kcs_class_init(iic);
+}
+
+static const TypeInfo isa_ipmi_kcs_info = {
+ .name = TYPE_ISA_IPMI_KCS,
+ .parent = TYPE_ISA_DEVICE,
+ .instance_size = sizeof(ISAIPMIKCSDevice),
+ .instance_init = isa_ipmi_kcs_init,
+ .class_init = isa_ipmi_kcs_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_IPMI_INTERFACE },
+ { }
+ }
+};
+
+static void ipmi_register_types(void)
+{
+ type_register_static(&isa_ipmi_kcs_info);
+}
+
+type_init(ipmi_register_types)
diff --git a/qemu/hw/isa/apm.c b/qemu/hw/isa/apm.c
index 26ab17021..e232b0da0 100644
--- a/qemu/hw/isa/apm.c
+++ b/qemu/hw/isa/apm.c
@@ -20,6 +20,7 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "hw/isa/apm.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
diff --git a/qemu/hw/isa/i82378.c b/qemu/hw/isa/i82378.c
index fcf97d86a..4d29a9900 100644
--- a/qemu/hw/isa/i82378.c
+++ b/qemu/hw/isa/i82378.c
@@ -17,6 +17,7 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/pci/pci.h"
#include "hw/i386/pc.h"
#include "hw/timer/i8254.h"
@@ -75,7 +76,10 @@ static void i82378_realize(PCIDevice *pci, Error **errp)
pci_config_set_interrupt_pin(pci_conf, 1); /* interrupt pin 0 */
isabus = isa_bus_new(dev, get_system_memory(),
- pci_address_space_io(pci));
+ pci_address_space_io(pci), errp);
+ if (!isabus) {
+ return;
+ }
/* This device has:
2 82C59 (irq)
@@ -100,7 +104,6 @@ static void i82378_realize(PCIDevice *pci, Error **errp)
/* 2 82C37 (dma) */
isa = isa_create_simple(isabus, "i82374");
- qdev_connect_gpio_out(DEVICE(isa), 0, s->out[1]);
/* timer */
isa_create_simple(isabus, "mc146818rtc");
@@ -111,7 +114,7 @@ static void i82378_init(Object *obj)
DeviceState *dev = DEVICE(obj);
I82378State *s = I82378(obj);
- qdev_init_gpio_out(dev, s->out, 2);
+ qdev_init_gpio_out(dev, s->out, 1);
qdev_init_gpio_in(dev, i82378_request_pic_irq, 16);
}
diff --git a/qemu/hw/isa/isa-bus.c b/qemu/hw/isa/isa-bus.c
index 43e0cd8dd..7aa115caf 100644
--- a/qemu/hw/isa/isa-bus.c
+++ b/qemu/hw/isa/isa-bus.c
@@ -16,6 +16,8 @@
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "monitor/monitor.h"
#include "hw/sysbus.h"
@@ -36,6 +38,12 @@ static void isa_bus_class_init(ObjectClass *klass, void *data)
k->get_fw_dev_path = isabus_get_fw_dev_path;
}
+static const TypeInfo isa_dma_info = {
+ .name = TYPE_ISADMA,
+ .parent = TYPE_INTERFACE,
+ .class_size = sizeof(IsaDmaClass),
+};
+
static const TypeInfo isa_bus_info = {
.name = TYPE_ISA_BUS,
.parent = TYPE_BUS,
@@ -44,10 +52,10 @@ static const TypeInfo isa_bus_info = {
};
ISABus *isa_bus_new(DeviceState *dev, MemoryRegion* address_space,
- MemoryRegion *address_space_io)
+ MemoryRegion *address_space_io, Error **errp)
{
if (isabus) {
- fprintf(stderr, "Can't create a second ISA bus\n");
+ error_setg(errp, "Can't create a second ISA bus");
return NULL;
}
if (!dev) {
@@ -63,9 +71,6 @@ ISABus *isa_bus_new(DeviceState *dev, MemoryRegion* address_space,
void isa_bus_irqs(ISABus *bus, qemu_irq *irqs)
{
- if (!bus) {
- hw_error("Can't set isa irqs with no isa bus present.");
- }
bus->irqs = irqs;
}
@@ -92,6 +97,20 @@ void isa_init_irq(ISADevice *dev, qemu_irq *p, int isairq)
dev->nirqs++;
}
+void isa_bus_dma(ISABus *bus, IsaDma *dma8, IsaDma *dma16)
+{
+ assert(bus && dma8 && dma16);
+ assert(!bus->dma[0] && !bus->dma[1]);
+ bus->dma[0] = dma8;
+ bus->dma[1] = dma16;
+}
+
+IsaDma *isa_get_dma(ISABus *bus, int nchan)
+{
+ assert(bus);
+ return bus->dma[nchan > 3 ? 1 : 0];
+}
+
static inline void isa_init_ioport(ISADevice *dev, uint16_t ioport)
{
if (dev && (dev->ioport_id == 0 || ioport < dev->ioport_id)) {
@@ -137,10 +156,6 @@ ISADevice *isa_create(ISABus *bus, const char *name)
{
DeviceState *dev;
- if (!bus) {
- hw_error("Tried to create isa device %s with no isa bus present.",
- name);
- }
dev = qdev_create(BUS(bus), name);
return ISA_DEVICE(dev);
}
@@ -149,10 +164,6 @@ ISADevice *isa_try_create(ISABus *bus, const char *name)
{
DeviceState *dev;
- if (!bus) {
- hw_error("Tried to create isa device %s with no isa bus present.",
- name);
- }
dev = qdev_try_create(BUS(bus), name);
return ISA_DEVICE(dev);
}
@@ -233,6 +244,7 @@ static const TypeInfo isa_device_type_info = {
static void isabus_register_types(void)
{
+ type_register_static(&isa_dma_info);
type_register_static(&isa_bus_info);
type_register_static(&isabus_bridge_info);
type_register_static(&isa_device_type_info);
diff --git a/qemu/hw/isa/lpc_ich9.c b/qemu/hw/isa/lpc_ich9.c
index 360699f6f..99cd3ba9e 100644
--- a/qemu/hw/isa/lpc_ich9.c
+++ b/qemu/hw/isa/lpc_ich9.c
@@ -27,7 +27,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "qapi/visitor.h"
#include "qemu/range.h"
@@ -368,13 +370,13 @@ static void ich9_set_sci(void *opaque, int irq_num, int level)
}
}
-void ich9_lpc_pm_init(PCIDevice *lpc_pci, bool smm_enabled, bool enable_tco)
+void ich9_lpc_pm_init(PCIDevice *lpc_pci, bool smm_enabled)
{
ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pci);
qemu_irq sci_irq;
sci_irq = qemu_allocate_irq(ich9_set_sci, lpc, 0);
- ich9_pm_init(lpc_pci, &lpc->pm, smm_enabled, enable_tco, sci_irq);
+ ich9_pm_init(lpc_pci, &lpc->pm, smm_enabled, sci_irq);
ich9_lpc_reset(&lpc->d.qdev);
}
@@ -394,7 +396,7 @@ static void ich9_apm_ctrl_changed(uint32_t val, void *arg)
/* SMI_EN = PMBASE + 30. SMI control and enable register */
if (lpc->pm.smi_en & ICH9_PMIO_SMI_EN_APMC_EN) {
- cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI);
+ cpu_interrupt(current_cpu, CPU_INTERRUPT_SMI);
}
}
@@ -408,18 +410,18 @@ ich9_lpc_pmbase_update(ICH9LPCState *lpc)
ich9_pm_iospace_update(&lpc->pm, pm_io_base);
}
-/* config:RBCA */
-static void ich9_lpc_rcba_update(ICH9LPCState *lpc, uint32_t rbca_old)
+/* config:RCBA */
+static void ich9_lpc_rcba_update(ICH9LPCState *lpc, uint32_t rcba_old)
{
- uint32_t rbca = pci_get_long(lpc->d.config + ICH9_LPC_RCBA);
+ uint32_t rcba = pci_get_long(lpc->d.config + ICH9_LPC_RCBA);
- if (rbca_old & ICH9_LPC_RCBA_EN) {
- memory_region_del_subregion(get_system_memory(), &lpc->rbca_mem);
+ if (rcba_old & ICH9_LPC_RCBA_EN) {
+ memory_region_del_subregion(get_system_memory(), &lpc->rcrb_mem);
}
- if (rbca & ICH9_LPC_RCBA_EN) {
- memory_region_add_subregion_overlap(get_system_memory(),
- rbca & ICH9_LPC_RCBA_BA_MASK,
- &lpc->rbca_mem, 1);
+ if (rcba & ICH9_LPC_RCBA_EN) {
+ memory_region_add_subregion_overlap(get_system_memory(),
+ rcba & ICH9_LPC_RCBA_BA_MASK,
+ &lpc->rcrb_mem, 1);
}
}
@@ -443,7 +445,7 @@ static int ich9_lpc_post_load(void *opaque, int version_id)
ICH9LPCState *lpc = opaque;
ich9_lpc_pmbase_update(lpc);
- ich9_lpc_rcba_update(lpc, 0 /* disabled ICH9_LPC_RBCA_EN */);
+ ich9_lpc_rcba_update(lpc, 0 /* disabled ICH9_LPC_RCBA_EN */);
ich9_lpc_pmcon_update(lpc);
return 0;
}
@@ -452,14 +454,14 @@ static void ich9_lpc_config_write(PCIDevice *d,
uint32_t addr, uint32_t val, int len)
{
ICH9LPCState *lpc = ICH9_LPC_DEVICE(d);
- uint32_t rbca_old = pci_get_long(d->config + ICH9_LPC_RCBA);
+ uint32_t rcba_old = pci_get_long(d->config + ICH9_LPC_RCBA);
pci_default_write_config(d, addr, val, len);
if (ranges_overlap(addr, len, ICH9_LPC_PMBASE, 4)) {
ich9_lpc_pmbase_update(lpc);
}
if (ranges_overlap(addr, len, ICH9_LPC_RCBA, 4)) {
- ich9_lpc_rcba_update(lpc, rbca_old);
+ ich9_lpc_rcba_update(lpc, rcba_old);
}
if (ranges_overlap(addr, len, ICH9_LPC_PIRQA_ROUT, 4)) {
pci_bus_fire_intx_routing_notifier(lpc->d.bus);
@@ -476,7 +478,7 @@ static void ich9_lpc_reset(DeviceState *qdev)
{
PCIDevice *d = PCI_DEVICE(qdev);
ICH9LPCState *lpc = ICH9_LPC_DEVICE(d);
- uint32_t rbca_old = pci_get_long(d->config + ICH9_LPC_RCBA);
+ uint32_t rcba_old = pci_get_long(d->config + ICH9_LPC_RCBA);
int i;
for (i = 0; i < 4; i++) {
@@ -495,13 +497,14 @@ static void ich9_lpc_reset(DeviceState *qdev)
ich9_cc_reset(lpc);
ich9_lpc_pmbase_update(lpc);
- ich9_lpc_rcba_update(lpc, rbca_old);
+ ich9_lpc_rcba_update(lpc, rcba_old);
lpc->sci_level = 0;
lpc->rst_cnt = 0;
}
-static const MemoryRegionOps rbca_mmio_ops = {
+/* root complex register block is mapped into memory space */
+static const MemoryRegionOps rcrb_mmio_ops = {
.read = ich9_cc_read,
.write = ich9_cc_write,
.endianness = DEVICE_LITTLE_ENDIAN,
@@ -569,14 +572,13 @@ Object *ich9_lpc_find(void)
return o;
}
-static void ich9_lpc_get_sci_int(Object *obj, Visitor *v,
- void *opaque, const char *name,
- Error **errp)
+static void ich9_lpc_get_sci_int(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
ICH9LPCState *lpc = ICH9_LPC_DEVICE(obj);
uint32_t value = ich9_lpc_sci_irq(lpc);
- visit_type_uint32(v, &value, name, errp);
+ visit_type_uint32(v, name, &value, errp);
}
static void ich9_lpc_add_properties(ICH9LPCState *lpc)
@@ -602,18 +604,22 @@ static void ich9_lpc_initfn(Object *obj)
ich9_lpc_add_properties(lpc);
}
-static int ich9_lpc_init(PCIDevice *d)
+static void ich9_lpc_realize(PCIDevice *d, Error **errp)
{
ICH9LPCState *lpc = ICH9_LPC_DEVICE(d);
ISABus *isa_bus;
- isa_bus = isa_bus_new(DEVICE(d), get_system_memory(), get_system_io());
+ isa_bus = isa_bus_new(DEVICE(d), get_system_memory(), get_system_io(),
+ errp);
+ if (!isa_bus) {
+ return;
+ }
pci_set_long(d->wmask + ICH9_LPC_PMBASE,
ICH9_LPC_PMBASE_BASE_ADDRESS_MASK);
- memory_region_init_io(&lpc->rbca_mem, OBJECT(d), &rbca_mmio_ops, lpc,
- "lpc-rbca-mmio", ICH9_CC_SIZE);
+ memory_region_init_io(&lpc->rcrb_mem, OBJECT(d), &rcrb_mmio_ops, lpc,
+ "lpc-rcrb-mmio", ICH9_CC_SIZE);
lpc->isa_bus = isa_bus;
@@ -628,7 +634,6 @@ static int ich9_lpc_init(PCIDevice *d)
memory_region_add_subregion_overlap(pci_address_space_io(d),
ICH9_RST_CNT_IOPORT, &lpc->rst_cnt_mem,
1);
- return 0;
}
static void ich9_device_plug_cb(HotplugHandler *hotplug_dev,
@@ -706,7 +711,7 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data)
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
dc->reset = ich9_lpc_reset;
- k->init = ich9_lpc_init;
+ k->realize = ich9_lpc_realize;
dc->vmsd = &vmstate_ich9_lpc;
dc->props = ich9_lpc_properties;
k->config_write = ich9_lpc_config_write;
diff --git a/qemu/hw/isa/pc87312.c b/qemu/hw/isa/pc87312.c
index 3b1fcec53..c3ebf3e7a 100644
--- a/qemu/hw/isa/pc87312.c
+++ b/qemu/hw/isa/pc87312.c
@@ -23,7 +23,9 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/isa/pc87312.h"
+#include "qapi/error.h"
#include "qemu/error-report.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
@@ -324,14 +326,14 @@ static void pc87312_realize(DeviceState *dev, Error **errp)
/* FIXME use a qdev drive property instead of drive_get() */
drive = drive_get(IF_FLOPPY, 0, 0);
if (drive != NULL) {
- qdev_prop_set_drive_nofail(d, "driveA",
- blk_by_legacy_dinfo(drive));
+ qdev_prop_set_drive(d, "driveA", blk_by_legacy_dinfo(drive),
+ &error_fatal);
}
/* FIXME use a qdev drive property instead of drive_get() */
drive = drive_get(IF_FLOPPY, 0, 1);
if (drive != NULL) {
- qdev_prop_set_drive_nofail(d, "driveB",
- blk_by_legacy_dinfo(drive));
+ qdev_prop_set_drive(d, "driveB", blk_by_legacy_dinfo(drive),
+ &error_fatal);
}
qdev_init_nofail(d);
s->fdc.dev = isa;
diff --git a/qemu/hw/isa/piix4.c b/qemu/hw/isa/piix4.c
index 2c59e91ff..5500fcc4d 100644
--- a/qemu/hw/isa/piix4.c
+++ b/qemu/hw/isa/piix4.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/pci/pci.h"
@@ -90,8 +91,10 @@ static void piix4_realize(PCIDevice *dev, Error **errp)
{
PIIX4State *d = PIIX4_PCI_DEVICE(dev);
- isa_bus_new(DEVICE(d), pci_address_space(dev),
- pci_address_space_io(dev));
+ if (!isa_bus_new(DEVICE(d), pci_address_space(dev),
+ pci_address_space_io(dev), errp)) {
+ return;
+ }
piix4_dev = &d->dev;
qemu_register_reset(piix4_reset, d);
}
diff --git a/qemu/hw/isa/vt82c686.c b/qemu/hw/isa/vt82c686.c
index 252e1d714..41d5254f8 100644
--- a/qemu/hw/isa/vt82c686.c
+++ b/qemu/hw/isa/vt82c686.c
@@ -10,6 +10,7 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/isa/vt82c686.h"
@@ -440,7 +441,10 @@ static void vt82c686b_realize(PCIDevice *d, Error **errp)
int i;
isa_bus = isa_bus_new(DEVICE(d), get_system_memory(),
- pci_address_space_io(d));
+ pci_address_space_io(d), errp);
+ if (!isa_bus) {
+ return;
+ }
pci_conf = d->config;
pci_config_set_prog_interface(pci_conf, 0x0);
diff --git a/qemu/hw/lm32/lm32_boards.c b/qemu/hw/lm32/lm32_boards.c
index 70f48d3b1..c0290560f 100644
--- a/qemu/hw/lm32/lm32_boards.c
+++ b/qemu/hw/lm32/lm32_boards.c
@@ -17,6 +17,9 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/sysbus.h"
#include "hw/hw.h"
#include "hw/block/flash.h"
@@ -142,7 +145,7 @@ static void lm32_evr_init(MachineState *machine)
int kernel_size;
kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL,
- 1, ELF_MACHINE, 0);
+ 1, EM_LATTICEMICO32, 0, 0);
reset_info->bootstrap_pc = entry;
if (kernel_size < 0) {
@@ -244,7 +247,7 @@ static void lm32_uclinux_init(MachineState *machine)
int kernel_size;
kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL,
- 1, ELF_MACHINE, 0);
+ 1, EM_LATTICEMICO32, 0, 0);
reset_info->bootstrap_pc = entry;
if (kernel_size < 0) {
@@ -292,24 +295,40 @@ static void lm32_uclinux_init(MachineState *machine)
qemu_register_reset(main_cpu_reset, reset_info);
}
-static QEMUMachine lm32_evr_machine = {
- .name = "lm32-evr",
- .desc = "LatticeMico32 EVR32 eval system",
- .init = lm32_evr_init,
- .is_default = 1,
+static void lm32_evr_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "LatticeMico32 EVR32 eval system";
+ mc->init = lm32_evr_init;
+ mc->is_default = 1;
+}
+
+static const TypeInfo lm32_evr_type = {
+ .name = MACHINE_TYPE_NAME("lm32-evr"),
+ .parent = TYPE_MACHINE,
+ .class_init = lm32_evr_class_init,
};
-static QEMUMachine lm32_uclinux_machine = {
- .name = "lm32-uclinux",
- .desc = "lm32 platform for uClinux and u-boot by Theobroma Systems",
- .init = lm32_uclinux_init,
- .is_default = 0,
+static void lm32_uclinux_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "lm32 platform for uClinux and u-boot by Theobroma Systems";
+ mc->init = lm32_uclinux_init;
+ mc->is_default = 0;
+}
+
+static const TypeInfo lm32_uclinux_type = {
+ .name = MACHINE_TYPE_NAME("lm32-uclinux"),
+ .parent = TYPE_MACHINE,
+ .class_init = lm32_uclinux_class_init,
};
static void lm32_machine_init(void)
{
- qemu_register_machine(&lm32_uclinux_machine);
- qemu_register_machine(&lm32_evr_machine);
+ type_register_static(&lm32_evr_type);
+ type_register_static(&lm32_uclinux_type);
}
-machine_init(lm32_machine_init);
+type_init(lm32_machine_init)
diff --git a/qemu/hw/lm32/lm32_hwsetup.h b/qemu/hw/lm32/lm32_hwsetup.h
index 838754d5d..b71e6eafb 100644
--- a/qemu/hw/lm32/lm32_hwsetup.h
+++ b/qemu/hw/lm32/lm32_hwsetup.h
@@ -26,6 +26,7 @@
#define QEMU_HW_LM32_HWSETUP_H
#include "qemu-common.h"
+#include "qemu/cutils.h"
#include "hw/loader.h"
typedef struct {
diff --git a/qemu/hw/lm32/milkymist-hw.h b/qemu/hw/lm32/milkymist-hw.h
index 8d20cac1d..c8dfb4d2d 100644
--- a/qemu/hw/lm32/milkymist-hw.h
+++ b/qemu/hw/lm32/milkymist-hw.h
@@ -88,7 +88,8 @@ static inline DeviceState *milkymist_pfpu_create(hwaddr base,
#ifdef CONFIG_OPENGL
#include <X11/Xlib.h>
-#include <GL/glx.h>
+#include <epoxy/gl.h>
+#include <epoxy/glx.h>
static const int glx_fbconfig_attr[] = {
GLX_GREEN_SIZE, 5,
GLX_GREEN_SIZE, 6,
diff --git a/qemu/hw/lm32/milkymist.c b/qemu/hw/lm32/milkymist.c
index e755f5b24..96e6f4dc2 100644
--- a/qemu/hw/lm32/milkymist.c
+++ b/qemu/hw/lm32/milkymist.c
@@ -17,6 +17,9 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/sysbus.h"
#include "hw/hw.h"
#include "hw/block/flash.h"
@@ -30,6 +33,7 @@
#include "milkymist-hw.h"
#include "lm32.h"
#include "exec/address-spaces.h"
+#include "qemu/cutils.h"
#define BIOS_FILENAME "mmone-bios.bin"
#define BIOS_OFFSET 0x00860000
@@ -176,7 +180,7 @@ milkymist_init(MachineState *machine)
/* Boots a kernel elf binary. */
kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL,
- 1, ELF_MACHINE, 0);
+ 1, EM_LATTICEMICO32, 0, 0);
reset_info->bootstrap_pc = entry;
if (kernel_size < 0) {
@@ -209,16 +213,11 @@ milkymist_init(MachineState *machine)
qemu_register_reset(main_cpu_reset, reset_info);
}
-static QEMUMachine milkymist_machine = {
- .name = "milkymist",
- .desc = "Milkymist One",
- .init = milkymist_init,
- .is_default = 0,
-};
-
-static void milkymist_machine_init(void)
+static void milkymist_machine_init(MachineClass *mc)
{
- qemu_register_machine(&milkymist_machine);
+ mc->desc = "Milkymist One";
+ mc->init = milkymist_init;
+ mc->is_default = 0;
}
-machine_init(milkymist_machine_init);
+DEFINE_MACHINE("milkymist", milkymist_machine_init)
diff --git a/qemu/hw/m68k/an5206.c b/qemu/hw/m68k/an5206.c
index f63ab2b94..142bab98c 100644
--- a/qemu/hw/m68k/an5206.c
+++ b/qemu/hw/m68k/an5206.c
@@ -6,12 +6,17 @@
* This code is licensed under the GPL
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "hw/m68k/mcf.h"
#include "hw/boards.h"
#include "hw/loader.h"
#include "elf.h"
#include "exec/address-spaces.h"
+#include "qemu/error-report.h"
#include "sysemu/qtest.h"
#define KERNEL_LOAD_ADDR 0x10000
@@ -39,7 +44,8 @@ static void an5206_init(MachineState *machine)
}
cpu = cpu_m68k_init(cpu_model);
if (!cpu) {
- hw_error("Unable to find m68k CPU definition\n");
+ error_report("Unable to find m68k CPU definition");
+ exit(1);
}
env = &cpu->env;
@@ -54,7 +60,7 @@ static void an5206_init(MachineState *machine)
memory_region_add_subregion(address_space_mem, 0, ram);
/* Internal SRAM. */
- memory_region_init_ram(sram, NULL, "an5206.sram", 512, &error_abort);
+ memory_region_init_ram(sram, NULL, "an5206.sram", 512, &error_fatal);
vmstate_register_ram_global(sram);
memory_region_add_subregion(address_space_mem, AN5206_RAMBAR_ADDR, sram);
@@ -70,7 +76,7 @@ static void an5206_init(MachineState *machine)
}
kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
- NULL, NULL, 1, ELF_MACHINE, 0);
+ NULL, NULL, 1, EM_68K, 0, 0);
entry = elf_entry;
if (kernel_size < 0) {
kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL,
@@ -89,15 +95,10 @@ static void an5206_init(MachineState *machine)
env->pc = entry;
}
-static QEMUMachine an5206_machine = {
- .name = "an5206",
- .desc = "Arnewsh 5206",
- .init = an5206_init,
-};
-
-static void an5206_machine_init(void)
+static void an5206_machine_init(MachineClass *mc)
{
- qemu_register_machine(&an5206_machine);
+ mc->desc = "Arnewsh 5206";
+ mc->init = an5206_init;
}
-machine_init(an5206_machine_init);
+DEFINE_MACHINE("an5206", an5206_machine_init)
diff --git a/qemu/hw/m68k/dummy_m68k.c b/qemu/hw/m68k/dummy_m68k.c
index 5b77d930e..0b11d2074 100644
--- a/qemu/hw/m68k/dummy_m68k.c
+++ b/qemu/hw/m68k/dummy_m68k.c
@@ -6,6 +6,9 @@
* This code is licensed under the GPL
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "hw/boards.h"
#include "hw/loader.h"
@@ -49,7 +52,7 @@ static void dummy_m68k_init(MachineState *machine)
/* Load kernel. */
if (kernel_filename) {
kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
- NULL, NULL, 1, ELF_MACHINE, 0);
+ NULL, NULL, 1, EM_68K, 0, 0);
entry = elf_entry;
if (kernel_size < 0) {
kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL,
@@ -72,15 +75,10 @@ static void dummy_m68k_init(MachineState *machine)
env->pc = entry;
}
-static QEMUMachine dummy_m68k_machine = {
- .name = "dummy",
- .desc = "Dummy board",
- .init = dummy_m68k_init,
-};
-
-static void dummy_m68k_machine_init(void)
+static void dummy_m68k_machine_init(MachineClass *mc)
{
- qemu_register_machine(&dummy_m68k_machine);
+ mc->desc = "Dummy board";
+ mc->init = dummy_m68k_init;
}
-machine_init(dummy_m68k_machine_init);
+DEFINE_MACHINE("dummy", dummy_m68k_machine_init)
diff --git a/qemu/hw/m68k/mcf5206.c b/qemu/hw/m68k/mcf5206.c
index 1727a4685..e14896e52 100644
--- a/qemu/hw/m68k/mcf5206.c
+++ b/qemu/hw/m68k/mcf5206.c
@@ -5,6 +5,9 @@
*
* This code is licensed under the GPL
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "hw/m68k/mcf.h"
#include "qemu/timer.h"
diff --git a/qemu/hw/m68k/mcf5208.c b/qemu/hw/m68k/mcf5208.c
index 326a42d27..24155574f 100644
--- a/qemu/hw/m68k/mcf5208.c
+++ b/qemu/hw/m68k/mcf5208.c
@@ -5,6 +5,10 @@
*
* This code is licensed under the GPL
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "hw/m68k/mcf.h"
#include "qemu/timer.h"
@@ -222,7 +226,7 @@ static void mcf5208evb_init(MachineState *machine)
memory_region_add_subregion(address_space_mem, 0x40000000, ram);
/* Internal SRAM. */
- memory_region_init_ram(sram, NULL, "mcf5208.sram", 16384, &error_abort);
+ memory_region_init_ram(sram, NULL, "mcf5208.sram", 16384, &error_fatal);
vmstate_register_ram_global(sram);
memory_region_add_subregion(address_space_mem, 0x80000000, sram);
@@ -275,7 +279,7 @@ static void mcf5208evb_init(MachineState *machine)
}
kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
- NULL, NULL, 1, ELF_MACHINE, 0);
+ NULL, NULL, 1, EM_68K, 0, 0);
entry = elf_entry;
if (kernel_size < 0) {
kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL,
@@ -294,16 +298,11 @@ static void mcf5208evb_init(MachineState *machine)
env->pc = entry;
}
-static QEMUMachine mcf5208evb_machine = {
- .name = "mcf5208evb",
- .desc = "MCF5206EVB",
- .init = mcf5208evb_init,
- .is_default = 1,
-};
-
-static void mcf5208evb_machine_init(void)
+static void mcf5208evb_machine_init(MachineClass *mc)
{
- qemu_register_machine(&mcf5208evb_machine);
+ mc->desc = "MCF5206EVB";
+ mc->init = mcf5208evb_init;
+ mc->is_default = 1;
}
-machine_init(mcf5208evb_machine_init);
+DEFINE_MACHINE("mcf5208evb", mcf5208evb_machine_init)
diff --git a/qemu/hw/m68k/mcf_intc.c b/qemu/hw/m68k/mcf_intc.c
index f13c7f3ae..cf581324e 100644
--- a/qemu/hw/m68k/mcf_intc.c
+++ b/qemu/hw/m68k/mcf_intc.c
@@ -5,6 +5,9 @@
*
* This code is licensed under the GPL
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "hw/m68k/mcf.h"
#include "exec/address-spaces.h"
diff --git a/qemu/hw/mem/Makefile.objs b/qemu/hw/mem/Makefile.objs
index b000fb42b..f12f8b97a 100644
--- a/qemu/hw/mem/Makefile.objs
+++ b/qemu/hw/mem/Makefile.objs
@@ -1 +1,2 @@
common-obj-$(CONFIG_MEM_HOTPLUG) += pc-dimm.o
+common-obj-$(CONFIG_NVDIMM) += nvdimm.o
diff --git a/qemu/hw/mem/nvdimm.c b/qemu/hw/mem/nvdimm.c
new file mode 100644
index 000000000..0a602f28b
--- /dev/null
+++ b/qemu/hw/mem/nvdimm.c
@@ -0,0 +1,47 @@
+/*
+ * Non-Volatile Dual In-line Memory Module Virtualization Implementation
+ *
+ * Copyright(C) 2015 Intel Corporation.
+ *
+ * Author:
+ * Xiao Guangrong <guangrong.xiao@linux.intel.com>
+ *
+ * Currently, it only supports PMEM Virtualization.
+ *
+ * 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 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 <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "hw/mem/nvdimm.h"
+
+static void nvdimm_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ /* nvdimm hotplug has not been supported yet. */
+ dc->hotpluggable = false;
+}
+
+static TypeInfo nvdimm_info = {
+ .name = TYPE_NVDIMM,
+ .parent = TYPE_PC_DIMM,
+ .class_init = nvdimm_class_init,
+};
+
+static void nvdimm_register_types(void)
+{
+ type_register_static(&nvdimm_info);
+}
+
+type_init(nvdimm_register_types)
diff --git a/qemu/hw/mem/pc-dimm.c b/qemu/hw/mem/pc-dimm.c
index bb04862de..9e7de5682 100644
--- a/qemu/hw/mem/pc-dimm.c
+++ b/qemu/hw/mem/pc-dimm.c
@@ -18,13 +18,16 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>
*/
+#include "qemu/osdep.h"
#include "hw/mem/pc-dimm.h"
+#include "qapi/error.h"
#include "qemu/config-file.h"
#include "qapi/visitor.h"
#include "qemu/range.h"
#include "sysemu/numa.h"
#include "sysemu/kvm.h"
#include "trace.h"
+#include "hw/virtio/vhost.h"
typedef struct pc_dimms_capacity {
uint64_t size;
@@ -95,6 +98,12 @@ void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms,
goto out;
}
+ if (!vhost_has_free_slot()) {
+ error_setg(&local_err, "a used vhost backend has no free"
+ " memory slots left");
+ goto out;
+ }
+
memory_region_add_subregion(&hpms->mr, addr - hpms->base, mr);
vmstate_register_ram(mr, dev);
numa_set_mem_node_id(addr, memory_region_size(mr), dimm->node);
@@ -172,7 +181,7 @@ int qmp_pc_dimm_device_list(Object *obj, void *opaque)
NULL);
di->memdev = object_get_canonical_path(OBJECT(dimm->hostmem));
- info->dimm = di;
+ info->u.dimm.data = di;
elem->value = info;
elem->next = NULL;
**prev = elem;
@@ -184,32 +193,6 @@ int qmp_pc_dimm_device_list(Object *obj, void *opaque)
return 0;
}
-ram_addr_t get_current_ram_size(void)
-{
- MemoryDeviceInfoList *info_list = NULL;
- MemoryDeviceInfoList **prev = &info_list;
- MemoryDeviceInfoList *info;
- ram_addr_t size = ram_size;
-
- qmp_pc_dimm_device_list(qdev_get_machine(), &prev);
- for (info = info_list; info; info = info->next) {
- MemoryDeviceInfo *value = info->value;
-
- if (value) {
- switch (value->kind) {
- case MEMORY_DEVICE_INFO_KIND_DIMM:
- size += value->dimm->size;
- break;
- default:
- break;
- }
- }
- }
- qapi_free_MemoryDeviceInfoList(info_list);
-
- return size;
-}
-
static int pc_dimm_slot2bitmap(Object *obj, void *opaque)
{
unsigned long *bitmap = opaque;
@@ -365,8 +348,8 @@ static Property pc_dimm_properties[] = {
DEFINE_PROP_END_OF_LIST(),
};
-static void pc_dimm_get_size(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void pc_dimm_get_size(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
int64_t value;
MemoryRegion *mr;
@@ -375,22 +358,29 @@ static void pc_dimm_get_size(Object *obj, Visitor *v, void *opaque,
mr = host_memory_backend_get_memory(dimm->hostmem, errp);
value = memory_region_size(mr);
- visit_type_int(v, &value, name, errp);
+ visit_type_int(v, name, &value, errp);
}
static void pc_dimm_check_memdev_is_busy(Object *obj, const char *name,
Object *val, Error **errp)
{
MemoryRegion *mr;
+ Error *local_err = NULL;
- mr = host_memory_backend_get_memory(MEMORY_BACKEND(val), errp);
+ mr = host_memory_backend_get_memory(MEMORY_BACKEND(val), &local_err);
+ if (local_err) {
+ goto out;
+ }
if (memory_region_is_mapped(mr)) {
char *path = object_get_canonical_path_component(val);
- error_setg(errp, "can't use already busy memdev: %s", path);
+ error_setg(&local_err, "can't use already busy memdev: %s", path);
g_free(path);
} else {
- qdev_prop_allow_set_link_before_realize(obj, name, val, errp);
+ qdev_prop_allow_set_link_before_realize(obj, name, val, &local_err);
}
+
+out:
+ error_propagate(errp, local_err);
}
static void pc_dimm_init(Object *obj)
@@ -414,10 +404,11 @@ static void pc_dimm_realize(DeviceState *dev, Error **errp)
error_setg(errp, "'" PC_DIMM_MEMDEV_PROP "' property is not set");
return;
}
- if ((nb_numa_nodes > 0) && (dimm->node >= nb_numa_nodes)) {
+ if (((nb_numa_nodes > 0) && (dimm->node >= nb_numa_nodes)) ||
+ (!nb_numa_nodes && dimm->node)) {
error_setg(errp, "'DIMM property " PC_DIMM_NODE_PROP " has value %"
PRIu32 "' which exceeds the number of numa nodes: %d",
- dimm->node, nb_numa_nodes);
+ dimm->node, nb_numa_nodes ? nb_numa_nodes : 1);
return;
}
}
diff --git a/qemu/hw/microblaze/boot.c b/qemu/hw/microblaze/boot.c
index 3e8820f36..9eebb1a52 100644
--- a/qemu/hw/microblaze/boot.c
+++ b/qemu/hw/microblaze/boot.c
@@ -24,6 +24,9 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "qemu/option.h"
#include "qemu/config-file.h"
#include "qemu/error-report.h"
@@ -32,6 +35,7 @@
#include "sysemu/sysemu.h"
#include "hw/loader.h"
#include "elf.h"
+#include "qemu/cutils.h"
#include "boot.h"
@@ -141,12 +145,12 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base,
/* Boots a kernel elf binary. */
kernel_size = load_elf(kernel_filename, NULL, NULL,
&entry, &low, &high,
- big_endian, ELF_MACHINE, 0);
+ big_endian, EM_MICROBLAZE, 0, 0);
base32 = entry;
if (base32 == 0xc0000000) {
kernel_size = load_elf(kernel_filename, translate_kernel_address,
NULL, &entry, NULL, NULL,
- big_endian, ELF_MACHINE, 0);
+ big_endian, EM_MICROBLAZE, 0, 0);
}
/* Always boot into physical ram. */
boot_info.bootstrap_pc = (uint32_t)entry;
diff --git a/qemu/hw/microblaze/petalogix_ml605_mmu.c b/qemu/hw/microblaze/petalogix_ml605_mmu.c
index ed84a37e6..07527b677 100644
--- a/qemu/hw/microblaze/petalogix_ml605_mmu.c
+++ b/qemu/hw/microblaze/petalogix_ml605_mmu.c
@@ -25,6 +25,10 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/sysbus.h"
#include "hw/hw.h"
#include "net/net.h"
@@ -35,7 +39,7 @@
#include "sysemu/block-backend.h"
#include "hw/char/serial.h"
#include "exec/address-spaces.h"
-#include "hw/ssi.h"
+#include "hw/ssi/ssi.h"
#include "boot.h"
@@ -81,6 +85,7 @@ petalogix_ml605_init(MachineState *machine)
/* init CPUs */
cpu = MICROBLAZE_CPU(object_new(TYPE_MICROBLAZE_CPU));
+ object_property_set_str(OBJECT(cpu), "8.10.a", "version", &error_abort);
/* Use FPU but don't use floating point conversion and square
* root instructions
*/
@@ -92,12 +97,12 @@ petalogix_ml605_init(MachineState *machine)
/* Attach emulated BRAM through the LMB. */
memory_region_init_ram(phys_lmb_bram, NULL, "petalogix_ml605.lmb_bram",
- LMB_BRAM_SIZE, &error_abort);
+ LMB_BRAM_SIZE, &error_fatal);
vmstate_register_ram_global(phys_lmb_bram);
memory_region_add_subregion(address_space_mem, 0x00000000, phys_lmb_bram);
memory_region_init_ram(phys_ram, NULL, "petalogix_ml605.ram", ram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(phys_ram);
memory_region_add_subregion(address_space_mem, MEMORY_BASEADDR, phys_ram);
@@ -206,16 +211,11 @@ petalogix_ml605_init(MachineState *machine)
}
-static QEMUMachine petalogix_ml605_machine = {
- .name = "petalogix-ml605",
- .desc = "PetaLogix linux refdesign for xilinx ml605 little endian",
- .init = petalogix_ml605_init,
- .is_default = 0,
-};
-
-static void petalogix_ml605_machine_init(void)
+static void petalogix_ml605_machine_init(MachineClass *mc)
{
- qemu_register_machine(&petalogix_ml605_machine);
+ mc->desc = "PetaLogix linux refdesign for xilinx ml605 little endian";
+ mc->init = petalogix_ml605_init;
+ mc->is_default = 0;
}
-machine_init(petalogix_ml605_machine_init);
+DEFINE_MACHINE("petalogix-ml605", petalogix_ml605_machine_init)
diff --git a/qemu/hw/microblaze/petalogix_s3adsp1800_mmu.c b/qemu/hw/microblaze/petalogix_s3adsp1800_mmu.c
index 0c2140c3f..f821e1cfe 100644
--- a/qemu/hw/microblaze/petalogix_s3adsp1800_mmu.c
+++ b/qemu/hw/microblaze/petalogix_s3adsp1800_mmu.c
@@ -23,6 +23,10 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/sysbus.h"
#include "hw/hw.h"
#include "net/net.h"
@@ -66,17 +70,18 @@ petalogix_s3adsp1800_init(MachineState *machine)
MemoryRegion *sysmem = get_system_memory();
cpu = MICROBLAZE_CPU(object_new(TYPE_MICROBLAZE_CPU));
+ object_property_set_str(OBJECT(cpu), "7.10.d", "version", &error_abort);
object_property_set_bool(OBJECT(cpu), true, "realized", &error_abort);
/* Attach emulated BRAM through the LMB. */
memory_region_init_ram(phys_lmb_bram, NULL,
"petalogix_s3adsp1800.lmb_bram", LMB_BRAM_SIZE,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(phys_lmb_bram);
memory_region_add_subregion(sysmem, 0x00000000, phys_lmb_bram);
memory_region_init_ram(phys_ram, NULL, "petalogix_s3adsp1800.ram",
- ram_size, &error_abort);
+ ram_size, &error_fatal);
vmstate_register_ram_global(phys_ram);
memory_region_add_subregion(sysmem, ddr_base, phys_ram);
@@ -124,16 +129,11 @@ petalogix_s3adsp1800_init(MachineState *machine)
NULL);
}
-static QEMUMachine petalogix_s3adsp1800_machine = {
- .name = "petalogix-s3adsp1800",
- .desc = "PetaLogix linux refdesign for xilinx Spartan 3ADSP1800",
- .init = petalogix_s3adsp1800_init,
- .is_default = 1,
-};
-
-static void petalogix_s3adsp1800_machine_init(void)
+static void petalogix_s3adsp1800_machine_init(MachineClass *mc)
{
- qemu_register_machine(&petalogix_s3adsp1800_machine);
+ mc->desc = "PetaLogix linux refdesign for xilinx Spartan 3ADSP1800";
+ mc->init = petalogix_s3adsp1800_init;
+ mc->is_default = 1;
}
-machine_init(petalogix_s3adsp1800_machine_init);
+DEFINE_MACHINE("petalogix-s3adsp1800", petalogix_s3adsp1800_machine_init)
diff --git a/qemu/hw/mips/Makefile.objs b/qemu/hw/mips/Makefile.objs
index 9633f3a57..9352a1c06 100644
--- a/qemu/hw/mips/Makefile.objs
+++ b/qemu/hw/mips/Makefile.objs
@@ -3,3 +3,4 @@ obj-y += addr.o cputimer.o mips_int.o
obj-$(CONFIG_JAZZ) += mips_jazz.o
obj-$(CONFIG_FULONG) += mips_fulong2e.o
obj-y += gt64xxx_pci.o
+obj-$(CONFIG_MIPS_CPS) += cps.o
diff --git a/qemu/hw/mips/addr.c b/qemu/hw/mips/addr.c
index ff3b95260..e4e86b4a7 100644
--- a/qemu/hw/mips/addr.c
+++ b/qemu/hw/mips/addr.c
@@ -20,6 +20,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/mips/cpudevs.h"
diff --git a/qemu/hw/mips/cps.c b/qemu/hw/mips/cps.c
new file mode 100644
index 000000000..1bafbbb27
--- /dev/null
+++ b/qemu/hw/mips/cps.c
@@ -0,0 +1,180 @@
+/*
+ * Coherent Processing System emulation.
+ *
+ * Copyright (c) 2016 Imagination Technologies
+ *
+ * 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 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/mips/cps.h"
+#include "hw/mips/mips.h"
+#include "hw/mips/cpudevs.h"
+#include "sysemu/kvm.h"
+
+qemu_irq get_cps_irq(MIPSCPSState *s, int pin_number)
+{
+ MIPSCPU *cpu = MIPS_CPU(first_cpu);
+ CPUMIPSState *env = &cpu->env;
+
+ assert(pin_number < s->num_irq);
+
+ /* TODO: return GIC pins once implemented */
+ return env->irq[pin_number];
+}
+
+static void mips_cps_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ MIPSCPSState *s = MIPS_CPS(obj);
+
+ /* Cover entire address space as there do not seem to be any
+ * constraints for the base address of CPC and GIC. */
+ memory_region_init(&s->container, obj, "mips-cps-container", UINT64_MAX);
+ sysbus_init_mmio(sbd, &s->container);
+}
+
+static void main_cpu_reset(void *opaque)
+{
+ MIPSCPU *cpu = opaque;
+ CPUState *cs = CPU(cpu);
+
+ cpu_reset(cs);
+
+ /* All VPs are halted on reset. Leave powering up to CPC. */
+ cs->halted = 1;
+}
+
+static bool cpu_mips_itu_supported(CPUMIPSState *env)
+{
+ bool is_mt = (env->CP0_Config5 & (1 << CP0C5_VP)) ||
+ (env->CP0_Config3 & (1 << CP0C3_MT));
+
+ return is_mt && !kvm_enabled();
+}
+
+static void mips_cps_realize(DeviceState *dev, Error **errp)
+{
+ MIPSCPSState *s = MIPS_CPS(dev);
+ CPUMIPSState *env;
+ MIPSCPU *cpu;
+ int i;
+ Error *err = NULL;
+ target_ulong gcr_base;
+ bool itu_present = false;
+
+ for (i = 0; i < s->num_vp; i++) {
+ cpu = cpu_mips_init(s->cpu_model);
+ if (cpu == NULL) {
+ error_setg(errp, "%s: CPU initialization failed\n", __func__);
+ return;
+ }
+ env = &cpu->env;
+
+ /* Init internal devices */
+ cpu_mips_irq_init_cpu(env);
+ cpu_mips_clock_init(env);
+ if (cpu_mips_itu_supported(env)) {
+ itu_present = true;
+ /* Attach ITC Tag to the VP */
+ env->itc_tag = mips_itu_get_tag_region(&s->itu);
+ }
+ qemu_register_reset(main_cpu_reset, cpu);
+ }
+
+ cpu = MIPS_CPU(first_cpu);
+ env = &cpu->env;
+
+ /* Inter-Thread Communication Unit */
+ if (itu_present) {
+ object_initialize(&s->itu, sizeof(s->itu), TYPE_MIPS_ITU);
+ qdev_set_parent_bus(DEVICE(&s->itu), sysbus_get_default());
+
+ object_property_set_int(OBJECT(&s->itu), 16, "num-fifo", &err);
+ object_property_set_int(OBJECT(&s->itu), 16, "num-semaphores", &err);
+ object_property_set_bool(OBJECT(&s->itu), true, "realized", &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ memory_region_add_subregion(&s->container, 0,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->itu), 0));
+ }
+
+ /* Cluster Power Controller */
+ object_initialize(&s->cpc, sizeof(s->cpc), TYPE_MIPS_CPC);
+ qdev_set_parent_bus(DEVICE(&s->cpc), sysbus_get_default());
+
+ object_property_set_int(OBJECT(&s->cpc), s->num_vp, "num-vp", &err);
+ object_property_set_int(OBJECT(&s->cpc), 1, "vp-start-running", &err);
+ object_property_set_bool(OBJECT(&s->cpc), true, "realized", &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ memory_region_add_subregion(&s->container, 0,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->cpc), 0));
+
+ /* Global Configuration Registers */
+ gcr_base = env->CP0_CMGCRBase << 4;
+
+ object_initialize(&s->gcr, sizeof(s->gcr), TYPE_MIPS_GCR);
+ qdev_set_parent_bus(DEVICE(&s->gcr), sysbus_get_default());
+
+ object_property_set_int(OBJECT(&s->gcr), s->num_vp, "num-vp", &err);
+ object_property_set_int(OBJECT(&s->gcr), 0x800, "gcr-rev", &err);
+ object_property_set_int(OBJECT(&s->gcr), gcr_base, "gcr-base", &err);
+ object_property_set_link(OBJECT(&s->gcr), OBJECT(&s->cpc.mr), "cpc", &err);
+ object_property_set_bool(OBJECT(&s->gcr), true, "realized", &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ memory_region_add_subregion(&s->container, gcr_base,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gcr), 0));
+}
+
+static Property mips_cps_properties[] = {
+ DEFINE_PROP_UINT32("num-vp", MIPSCPSState, num_vp, 1),
+ DEFINE_PROP_UINT32("num-irq", MIPSCPSState, num_irq, 8),
+ DEFINE_PROP_STRING("cpu-model", MIPSCPSState, cpu_model),
+ DEFINE_PROP_END_OF_LIST()
+};
+
+static void mips_cps_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = mips_cps_realize;
+ dc->props = mips_cps_properties;
+}
+
+static const TypeInfo mips_cps_info = {
+ .name = TYPE_MIPS_CPS,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(MIPSCPSState),
+ .instance_init = mips_cps_init,
+ .class_init = mips_cps_class_init,
+};
+
+static void mips_cps_register_types(void)
+{
+ type_register_static(&mips_cps_info);
+}
+
+type_init(mips_cps_register_types)
diff --git a/qemu/hw/mips/cputimer.c b/qemu/hw/mips/cputimer.c
index 577c9aeab..efb227d06 100644
--- a/qemu/hw/mips/cputimer.c
+++ b/qemu/hw/mips/cputimer.c
@@ -20,23 +20,32 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/mips/cpudevs.h"
#include "qemu/timer.h"
#include "sysemu/kvm.h"
-#define TIMER_FREQ 100 * 1000 * 1000
+#define TIMER_PERIOD 10 /* 10 ns period for 100 Mhz frequency */
/* XXX: do not use a global */
uint32_t cpu_mips_get_random (CPUMIPSState *env)
{
- static uint32_t lfsr = 1;
+ static uint32_t seed = 1;
static uint32_t prev_idx = 0;
uint32_t idx;
+ uint32_t nb_rand_tlb = env->tlb->nb_tlb - env->CP0_Wired;
+
+ if (nb_rand_tlb == 1) {
+ return env->tlb->nb_tlb - 1;
+ }
+
/* Don't return same value twice, so get another value */
do {
- lfsr = (lfsr >> 1) ^ (-(lfsr & 1u) & 0xd0000001u);
- idx = lfsr % (env->tlb->nb_tlb - env->CP0_Wired) + env->CP0_Wired;
+ /* Use a simple algorithm of Linear Congruential Generator
+ * from ISO/IEC 9899 standard. */
+ seed = 1103515245 * seed + 12345;
+ idx = (seed >> 16) % nb_rand_tlb + env->CP0_Wired;
} while (idx == prev_idx);
prev_idx = idx;
return idx;
@@ -49,9 +58,8 @@ static void cpu_mips_timer_update(CPUMIPSState *env)
uint32_t wait;
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- wait = env->CP0_Compare - env->CP0_Count -
- (uint32_t)muldiv64(now, TIMER_FREQ, get_ticks_per_sec());
- next = now + muldiv64(wait, get_ticks_per_sec(), TIMER_FREQ);
+ wait = env->CP0_Compare - env->CP0_Count - (uint32_t)(now / TIMER_PERIOD);
+ next = now + (uint64_t)wait * TIMER_PERIOD;
timer_mod(env->timer, next);
}
@@ -79,8 +87,7 @@ uint32_t cpu_mips_get_count (CPUMIPSState *env)
cpu_mips_timer_expire(env);
}
- return env->CP0_Count +
- (uint32_t)muldiv64(now, TIMER_FREQ, get_ticks_per_sec());
+ return env->CP0_Count + (uint32_t)(now / TIMER_PERIOD);
}
}
@@ -95,9 +102,8 @@ void cpu_mips_store_count (CPUMIPSState *env, uint32_t count)
env->CP0_Count = count;
else {
/* Store new count register */
- env->CP0_Count =
- count - (uint32_t)muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
- TIMER_FREQ, get_ticks_per_sec());
+ env->CP0_Count = count -
+ (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD);
/* Update timer timer */
cpu_mips_timer_update(env);
}
@@ -121,8 +127,8 @@ void cpu_mips_start_count(CPUMIPSState *env)
void cpu_mips_stop_count(CPUMIPSState *env)
{
/* Store the current value */
- env->CP0_Count += (uint32_t)muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
- TIMER_FREQ, get_ticks_per_sec());
+ env->CP0_Count += (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) /
+ TIMER_PERIOD);
}
static void mips_timer_cb (void *opaque)
diff --git a/qemu/hw/mips/gt64xxx_pci.c b/qemu/hw/mips/gt64xxx_pci.c
index 10fcca33f..3f4523df2 100644
--- a/qemu/hw/mips/gt64xxx_pci.c
+++ b/qemu/hw/mips/gt64xxx_pci.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/mips/mips.h"
#include "hw/pci/pci.h"
@@ -275,7 +276,8 @@ static void check_reserved_space (hwaddr *start,
static void gt64120_isd_mapping(GT64120State *s)
{
- hwaddr start = s->regs[GT_ISD] << 21;
+ /* Bits 14:0 of ISD map to bits 35:21 of the start address. */
+ hwaddr start = ((hwaddr)s->regs[GT_ISD] << 21) & 0xFFFE00000ull;
hwaddr length = 0x1000;
if (s->ISD_length) {
@@ -1192,7 +1194,7 @@ static int gt64120_init(SysBusDevice *dev)
return 0;
}
-static int gt64120_pci_init(PCIDevice *d)
+static void gt64120_pci_realize(PCIDevice *d, Error **errp)
{
/* FIXME: Malta specific hw assumptions ahead */
pci_set_word(d->config + PCI_COMMAND, 0);
@@ -1206,8 +1208,6 @@ static int gt64120_pci_init(PCIDevice *d)
pci_set_long(d->config + PCI_BASE_ADDRESS_4, 0x14000000);
pci_set_long(d->config + PCI_BASE_ADDRESS_5, 0x14000001);
pci_set_byte(d->config + 0x3d, 0x01);
-
- return 0;
}
static void gt64120_pci_class_init(ObjectClass *klass, void *data)
@@ -1215,7 +1215,7 @@ static void gt64120_pci_class_init(ObjectClass *klass, void *data)
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
- k->init = gt64120_pci_init;
+ k->realize = gt64120_pci_realize;
k->vendor_id = PCI_VENDOR_ID_MARVELL;
k->device_id = PCI_DEVICE_ID_MARVELL_GT6412X;
k->revision = 0x10;
diff --git a/qemu/hw/mips/mips_fulong2e.c b/qemu/hw/mips/mips_fulong2e.c
index dea941ad8..bdb716e72 100644
--- a/qemu/hw/mips/mips_fulong2e.c
+++ b/qemu/hw/mips/mips_fulong2e.c
@@ -18,6 +18,8 @@
* http://www.loongsondeveloper.com/doc/Loongson2EUserGuide.pdf
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/char/serial.h"
@@ -116,7 +118,7 @@ static int64_t load_kernel (CPUMIPSState *env)
if (load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, NULL,
(uint64_t *)&kernel_entry, (uint64_t *)&kernel_low,
- (uint64_t *)&kernel_high, 0, ELF_MACHINE, 1) < 0) {
+ (uint64_t *)&kernel_high, 0, EM_MIPS, 1, 0) < 0) {
fprintf(stderr, "qemu: could not load kernel '%s'\n",
loaderparams.kernel_filename);
exit(1);
@@ -251,15 +253,6 @@ static void network_init (PCIBus *pci_bus)
}
}
-static void cpu_request_exit(void *opaque, int irq, int level)
-{
- CPUState *cpu = current_cpu;
-
- if (cpu && level) {
- cpu_exit(cpu);
- }
-}
-
static void mips_fulong2e_init(MachineState *machine)
{
ram_addr_t ram_size = machine->ram_size;
@@ -274,7 +267,6 @@ static void mips_fulong2e_init(MachineState *machine)
long bios_size;
int64_t kernel_entry;
qemu_irq *i8259;
- qemu_irq *cpu_exit_irq;
PCIBus *pci_bus;
ISABus *isa_bus;
I2CBus *smbus;
@@ -304,7 +296,7 @@ static void mips_fulong2e_init(MachineState *machine)
/* allocate RAM */
memory_region_allocate_system_memory(ram, NULL, "fulong2e.ram", ram_size);
memory_region_init_ram(bios, NULL, "fulong2e.bios", bios_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(bios);
memory_region_set_readonly(bios, true);
@@ -375,8 +367,7 @@ static void mips_fulong2e_init(MachineState *machine)
/* init other devices */
pit = pit_init(isa_bus, 0x40, 0, NULL);
- cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1);
- DMA_init(0, cpu_exit_irq);
+ DMA_init(isa_bus, 0);
/* Super I/O */
isa_create_simple(isa_bus, "i8042");
@@ -392,15 +383,10 @@ static void mips_fulong2e_init(MachineState *machine)
network_init(pci_bus);
}
-static QEMUMachine mips_fulong2e_machine = {
- .name = "fulong2e",
- .desc = "Fulong 2e mini pc",
- .init = mips_fulong2e_init,
-};
-
-static void mips_fulong2e_machine_init(void)
+static void mips_fulong2e_machine_init(MachineClass *mc)
{
- qemu_register_machine(&mips_fulong2e_machine);
+ mc->desc = "Fulong 2e mini pc";
+ mc->init = mips_fulong2e_init;
}
-machine_init(mips_fulong2e_machine_init);
+DEFINE_MACHINE("fulong2e", mips_fulong2e_machine_init)
diff --git a/qemu/hw/mips/mips_int.c b/qemu/hw/mips/mips_int.c
index d740046ba..59081f9d1 100644
--- a/qemu/hw/mips/mips_int.c
+++ b/qemu/hw/mips/mips_int.c
@@ -20,6 +20,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/mips/cpudevs.h"
#include "cpu.h"
diff --git a/qemu/hw/mips/mips_jazz.c b/qemu/hw/mips/mips_jazz.c
index 9d60633ef..ac7c64125 100644
--- a/qemu/hw/mips/mips_jazz.c
+++ b/qemu/hw/mips/mips_jazz.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/mips/mips.h"
#include "hw/mips/cpudevs.h"
@@ -44,6 +45,7 @@
#include "exec/address-spaces.h"
#include "sysemu/qtest.h"
#include "qemu/error-report.h"
+#include "qemu/help_option.h"
enum jazz_model_e
{
@@ -104,15 +106,6 @@ static const MemoryRegionOps dma_dummy_ops = {
#define MAGNUM_BIOS_SIZE_MAX 0x7e000
#define MAGNUM_BIOS_SIZE (BIOS_SIZE < MAGNUM_BIOS_SIZE_MAX ? BIOS_SIZE : MAGNUM_BIOS_SIZE_MAX)
-static void cpu_request_exit(void *opaque, int irq, int level)
-{
- CPUState *cpu = current_cpu;
-
- if (cpu && level) {
- cpu_exit(cpu);
- }
-}
-
static CPUUnassignedAccess real_do_unassigned_access;
static void mips_jazz_do_unassigned_access(CPUState *cpu, hwaddr addr,
bool is_write, bool is_exec,
@@ -150,7 +143,6 @@ static void mips_jazz_init(MachineState *machine,
ISADevice *pit;
DriveInfo *fds[MAX_FD];
qemu_irq esp_reset, dma_enable;
- qemu_irq *cpu_exit_irq;
MemoryRegion *ram = g_new(MemoryRegion, 1);
MemoryRegion *bios = g_new(MemoryRegion, 1);
MemoryRegion *bios2 = g_new(MemoryRegion, 1);
@@ -184,7 +176,7 @@ static void mips_jazz_init(MachineState *machine,
memory_region_add_subregion(address_space, 0, ram);
memory_region_init_ram(bios, NULL, "mips_jazz.bios", MAGNUM_BIOS_SIZE,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(bios);
memory_region_set_readonly(bios, true);
memory_region_init_alias(bios2, NULL, "mips_jazz.bios", bios,
@@ -229,13 +221,12 @@ static void mips_jazz_init(MachineState *machine,
memory_region_init(isa_mem, NULL, "isa-mem", 0x01000000);
memory_region_add_subregion(address_space, 0x90000000, isa_io);
memory_region_add_subregion(address_space, 0x91000000, isa_mem);
- isa_bus = isa_bus_new(NULL, isa_mem, isa_io);
+ isa_bus = isa_bus_new(NULL, isa_mem, isa_io, &error_abort);
/* ISA devices */
i8259 = i8259_init(isa_bus, env->irq[4]);
isa_bus_irqs(isa_bus, i8259);
- cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1);
- DMA_init(0, cpu_exit_irq);
+ DMA_init(isa_bus, 0);
pit = pit_init(isa_bus, 0x40, 0, NULL);
pcspk_init(isa_bus, pit);
@@ -252,7 +243,7 @@ static void mips_jazz_init(MachineState *machine,
/* Simple ROM, so user doesn't have to provide one */
MemoryRegion *rom_mr = g_new(MemoryRegion, 1);
memory_region_init_ram(rom_mr, NULL, "g364fb.rom", 0x80000,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(rom_mr);
memory_region_set_readonly(rom_mr, true);
uint8_t *rom = memory_region_get_ram_ptr(rom_mr);
@@ -307,7 +298,8 @@ static void mips_jazz_init(MachineState *machine,
for (n = 0; n < MAX_FD; n++) {
fds[n] = drive_get(IF_FLOPPY, 0, n);
}
- fdctrl_init_sysbus(qdev_get_gpio_in(rc4030, 1), 0, 0x80003000, fds);
+ /* FIXME: we should enable DMA with a custom IsaDma device */
+ fdctrl_init_sysbus(qdev_get_gpio_in(rc4030, 1), -1, 0x80003000, fds);
/* Real time clock */
rtc_init(isa_bus, 1980, NULL);
@@ -360,24 +352,40 @@ void mips_pica61_init(MachineState *machine)
mips_jazz_init(machine, JAZZ_PICA61);
}
-static QEMUMachine mips_magnum_machine = {
- .name = "magnum",
- .desc = "MIPS Magnum",
- .init = mips_magnum_init,
- .block_default_type = IF_SCSI,
+static void mips_magnum_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "MIPS Magnum";
+ mc->init = mips_magnum_init;
+ mc->block_default_type = IF_SCSI;
+}
+
+static const TypeInfo mips_magnum_type = {
+ .name = MACHINE_TYPE_NAME("magnum"),
+ .parent = TYPE_MACHINE,
+ .class_init = mips_magnum_class_init,
};
-static QEMUMachine mips_pica61_machine = {
- .name = "pica61",
- .desc = "Acer Pica 61",
- .init = mips_pica61_init,
- .block_default_type = IF_SCSI,
+static void mips_pica61_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Acer Pica 61";
+ mc->init = mips_pica61_init;
+ mc->block_default_type = IF_SCSI;
+}
+
+static const TypeInfo mips_pica61_type = {
+ .name = MACHINE_TYPE_NAME("pica61"),
+ .parent = TYPE_MACHINE,
+ .class_init = mips_pica61_class_init,
};
static void mips_jazz_machine_init(void)
{
- qemu_register_machine(&mips_magnum_machine);
- qemu_register_machine(&mips_pica61_machine);
+ type_register_static(&mips_magnum_type);
+ type_register_static(&mips_pica61_type);
}
-machine_init(mips_jazz_machine_init);
+type_init(mips_jazz_machine_init)
diff --git a/qemu/hw/mips/mips_malta.c b/qemu/hw/mips/mips_malta.c
index 3082e7534..fa769e5c0 100644
--- a/qemu/hw/mips/mips_malta.c
+++ b/qemu/hw/mips/mips_malta.c
@@ -22,6 +22,9 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/char/serial.h"
@@ -54,6 +57,7 @@
#include "hw/empty_slot.h"
#include "sysemu/kvm.h"
#include "exec/semihost.h"
+#include "hw/mips/cps.h"
//#define DEBUG_BOARD_INIT
@@ -92,6 +96,7 @@ typedef struct {
typedef struct {
SysBusDevice parent_obj;
+ MIPSCPSState *cps;
qemu_irq *i8259;
} MaltaState;
@@ -605,8 +610,8 @@ static void network_init(PCIBus *pci_bus)
a3 - RAM size in bytes
*/
-static void write_bootloader (CPUMIPSState *env, uint8_t *base,
- int64_t run_addr, int64_t kernel_entry)
+static void write_bootloader(uint8_t *base, int64_t run_addr,
+ int64_t kernel_entry)
{
uint32_t *p;
@@ -795,7 +800,7 @@ static int64_t load_kernel (void)
if (load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, NULL,
(uint64_t *)&kernel_entry, NULL, (uint64_t *)&kernel_high,
- big_endian, ELF_MACHINE, 1) < 0) {
+ big_endian, EM_MIPS, 1, 0) < 0) {
fprintf(stderr, "qemu: could not load kernel '%s'\n",
loaderparams.kernel_filename);
exit(1);
@@ -901,16 +906,77 @@ static void main_cpu_reset(void *opaque)
if (kvm_enabled()) {
/* Start running from the bootloader we wrote to end of RAM */
- env->active_tc.PC = 0x40000000 + loaderparams.ram_size;
+ env->active_tc.PC = 0x40000000 + loaderparams.ram_low_size;
}
}
-static void cpu_request_exit(void *opaque, int irq, int level)
+static void create_cpu_without_cps(const char *cpu_model,
+ qemu_irq *cbus_irq, qemu_irq *i8259_irq)
{
- CPUState *cpu = current_cpu;
+ CPUMIPSState *env;
+ MIPSCPU *cpu;
+ int i;
- if (cpu && level) {
- cpu_exit(cpu);
+ for (i = 0; i < smp_cpus; i++) {
+ cpu = cpu_mips_init(cpu_model);
+ if (cpu == NULL) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ env = &cpu->env;
+
+ /* Init internal devices */
+ cpu_mips_irq_init_cpu(env);
+ cpu_mips_clock_init(env);
+ qemu_register_reset(main_cpu_reset, cpu);
+ }
+
+ cpu = MIPS_CPU(first_cpu);
+ env = &cpu->env;
+ *i8259_irq = env->irq[2];
+ *cbus_irq = env->irq[4];
+}
+
+static void create_cps(MaltaState *s, const char *cpu_model,
+ qemu_irq *cbus_irq, qemu_irq *i8259_irq)
+{
+ Error *err = NULL;
+ s->cps = g_new0(MIPSCPSState, 1);
+
+ object_initialize(s->cps, sizeof(MIPSCPSState), TYPE_MIPS_CPS);
+ qdev_set_parent_bus(DEVICE(s->cps), sysbus_get_default());
+
+ object_property_set_str(OBJECT(s->cps), cpu_model, "cpu-model", &err);
+ object_property_set_int(OBJECT(s->cps), smp_cpus, "num-vp", &err);
+ object_property_set_bool(OBJECT(s->cps), true, "realized", &err);
+ if (err != NULL) {
+ error_report("%s", error_get_pretty(err));
+ exit(1);
+ }
+
+ sysbus_mmio_map_overlap(SYS_BUS_DEVICE(s->cps), 0, 0, 1);
+
+ /* FIXME: When GIC is present then we should use GIC's IRQ 3.
+ Until then CPS exposes CPU's IRQs thus use the default IRQ 2. */
+ *i8259_irq = get_cps_irq(s->cps, 2);
+ *cbus_irq = NULL;
+}
+
+static void create_cpu(MaltaState *s, const char *cpu_model,
+ qemu_irq *cbus_irq, qemu_irq *i8259_irq)
+{
+ if (cpu_model == NULL) {
+#ifdef TARGET_MIPS64
+ cpu_model = "20Kc";
+#else
+ cpu_model = "24Kf";
+#endif
+ }
+
+ if ((smp_cpus > 1) && cpu_supports_cps_smp(cpu_model)) {
+ create_cps(s, cpu_model, cbus_irq, i8259_irq);
+ } else {
+ create_cpu_without_cps(cpu_model, cbus_irq, i8259_irq);
}
}
@@ -919,7 +985,6 @@ void mips_malta_init(MachineState *machine)
{
ram_addr_t ram_size = machine->ram_size;
ram_addr_t ram_low_size;
- const char *cpu_model = machine->cpu_model;
const char *kernel_filename = machine->kernel_filename;
const char *kernel_cmdline = machine->kernel_cmdline;
const char *initrd_filename = machine->initrd_filename;
@@ -936,10 +1001,8 @@ void mips_malta_init(MachineState *machine)
int64_t kernel_entry, bootloader_run_addr;
PCIBus *pci_bus;
ISABus *isa_bus;
- MIPSCPU *cpu;
- CPUMIPSState *env;
qemu_irq *isa_irq;
- qemu_irq *cpu_exit_irq;
+ qemu_irq cbus_irq, i8259_irq;
int piix4_devfn;
I2CBus *smbus;
int i;
@@ -969,30 +1032,8 @@ void mips_malta_init(MachineState *machine)
}
}
- /* init CPUs */
- if (cpu_model == NULL) {
-#ifdef TARGET_MIPS64
- cpu_model = "20Kc";
-#else
- cpu_model = "24Kf";
-#endif
- }
-
- for (i = 0; i < smp_cpus; i++) {
- cpu = cpu_mips_init(cpu_model);
- if (cpu == NULL) {
- fprintf(stderr, "Unable to find CPU definition\n");
- exit(1);
- }
- env = &cpu->env;
-
- /* Init internal devices */
- cpu_mips_irq_init_cpu(env);
- cpu_mips_clock_init(env);
- qemu_register_reset(main_cpu_reset, cpu);
- }
- cpu = MIPS_CPU(first_cpu);
- env = &cpu->env;
+ /* create CPU */
+ create_cpu(s, machine->cpu_model, &cbus_irq, &i8259_irq);
/* allocate RAM */
if (ram_size > (2048u << 20)) {
@@ -1033,7 +1074,7 @@ void mips_malta_init(MachineState *machine)
#endif
/* FPGA */
/* The CBUS UART is attached to the MIPS CPU INT2 pin, ie interrupt 4 */
- malta_fpga_init(system_memory, FPGA_ADDRESS, env->irq[4], serial_hds[2]);
+ malta_fpga_init(system_memory, FPGA_ADDRESS, cbus_irq, serial_hds[2]);
/* Load firmware in flash / BIOS. */
dinfo = drive_get(IF_PFLASH, 0, fl_idx);
@@ -1070,11 +1111,11 @@ void mips_malta_init(MachineState *machine)
loaderparams.initrd_filename = initrd_filename;
kernel_entry = load_kernel();
- write_bootloader(env, memory_region_get_ram_ptr(bios),
+ write_bootloader(memory_region_get_ram_ptr(bios),
bootloader_run_addr, kernel_entry);
if (kvm_enabled()) {
/* Write the bootloader code @ the end of RAM, 1MB reserved */
- write_bootloader(env, memory_region_get_ram_ptr(ram_low_preio) +
+ write_bootloader(memory_region_get_ram_ptr(ram_low_preio) +
ram_low_size,
bootloader_run_addr, kernel_entry);
}
@@ -1130,7 +1171,7 @@ void mips_malta_init(MachineState *machine)
* regions are not executable.
*/
memory_region_init_ram(bios_copy, NULL, "bios.1fc", BIOS_SIZE,
- &error_abort);
+ &error_fatal);
if (!rom_copy(memory_region_get_ram_ptr(bios_copy),
FLASH_ADDRESS, BIOS_SIZE)) {
memcpy(memory_region_get_ram_ptr(bios_copy),
@@ -1142,10 +1183,6 @@ void mips_malta_init(MachineState *machine)
/* Board ID = 0x420 (Malta Board with CoreLV) */
stl_p(memory_region_get_ram_ptr(bios_copy) + 0x10, 0x00000420);
- /* Init internal devices */
- cpu_mips_irq_init_cpu(env);
- cpu_mips_clock_init(env);
-
/*
* We have a circular dependency problem: pci_bus depends on isa_irq,
* isa_irq is provided by i8259, i8259 depends on ISA, ISA depends
@@ -1165,7 +1202,7 @@ void mips_malta_init(MachineState *machine)
/* Interrupt controller */
/* The 8259 is attached to the MIPS CPU INT0 pin, ie interrupt 2 */
- s->i8259 = i8259_init(isa_bus, env->irq[2]);
+ s->i8259 = i8259_init(isa_bus, i8259_irq);
isa_bus_irqs(isa_bus, s->i8259);
pci_piix4_ide_init(pci_bus, hd, piix4_devfn + 1);
@@ -1175,8 +1212,7 @@ void mips_malta_init(MachineState *machine)
smbus_eeprom_init(smbus, 8, smbus_eeprom_buf, smbus_eeprom_size);
g_free(smbus_eeprom_buf);
pit = pit_init(isa_bus, 0x40, 0, NULL);
- cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1);
- DMA_init(0, cpu_exit_irq);
+ DMA_init(isa_bus, 0);
/* Super I/O */
isa_create_simple(isa_bus, "i8042");
@@ -1216,23 +1252,19 @@ static const TypeInfo mips_malta_device = {
.class_init = mips_malta_class_init,
};
-static QEMUMachine mips_malta_machine = {
- .name = "malta",
- .desc = "MIPS Malta Core LV",
- .init = mips_malta_init,
- .max_cpus = 16,
- .is_default = 1,
-};
-
-static void mips_malta_register_types(void)
+static void mips_malta_machine_init(MachineClass *mc)
{
- type_register_static(&mips_malta_device);
+ mc->desc = "MIPS Malta Core LV";
+ mc->init = mips_malta_init;
+ mc->max_cpus = 16;
+ mc->is_default = 1;
}
-static void mips_malta_machine_init(void)
+DEFINE_MACHINE("malta", mips_malta_machine_init)
+
+static void mips_malta_register_types(void)
{
- qemu_register_machine(&mips_malta_machine);
+ type_register_static(&mips_malta_device);
}
type_init(mips_malta_register_types)
-machine_init(mips_malta_machine_init);
diff --git a/qemu/hw/mips/mips_mipssim.c b/qemu/hw/mips/mips_mipssim.c
index 61f74a631..a2c2a1646 100644
--- a/qemu/hw/mips/mips_mipssim.c
+++ b/qemu/hw/mips/mips_mipssim.c
@@ -24,6 +24,10 @@
* 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-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "hw/mips/mips.h"
#include "hw/mips/cpudevs.h"
@@ -69,7 +73,7 @@ static int64_t load_kernel(void)
kernel_size = load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys,
NULL, (uint64_t *)&entry, NULL,
(uint64_t *)&kernel_high, big_endian,
- ELF_MACHINE, 1);
+ EM_MIPS, 1, 0);
if (kernel_size >= 0) {
if ((entry & ~0x7fffffffULL) == 0x80000000)
entry = (int32_t)entry;
@@ -174,7 +178,7 @@ mips_mipssim_init(MachineState *machine)
memory_region_allocate_system_memory(ram, NULL, "mips_mipssim.ram",
ram_size);
memory_region_init_ram(bios, NULL, "mips_mipssim.bios", BIOS_SIZE,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(bios);
memory_region_set_readonly(bios, true);
@@ -231,15 +235,10 @@ mips_mipssim_init(MachineState *machine)
mipsnet_init(0x4200, env->irq[2], &nd_table[0]);
}
-static QEMUMachine mips_mipssim_machine = {
- .name = "mipssim",
- .desc = "MIPS MIPSsim platform",
- .init = mips_mipssim_init,
-};
-
-static void mips_mipssim_machine_init(void)
+static void mips_mipssim_machine_init(MachineClass *mc)
{
- qemu_register_machine(&mips_mipssim_machine);
+ mc->desc = "MIPS MIPSsim platform";
+ mc->init = mips_mipssim_init;
}
-machine_init(mips_mipssim_machine_init);
+DEFINE_MACHINE("mipssim", mips_mipssim_machine_init)
diff --git a/qemu/hw/mips/mips_r4k.c b/qemu/hw/mips/mips_r4k.c
index f4dcacd86..21aca981c 100644
--- a/qemu/hw/mips/mips_r4k.c
+++ b/qemu/hw/mips/mips_r4k.c
@@ -7,6 +7,10 @@
* All peripherial devices are attached to this "bus" with
* the standard PC ISA addresses.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "hw/mips/mips.h"
#include "hw/mips/cpudevs.h"
@@ -87,7 +91,7 @@ static int64_t load_kernel(void)
kernel_size = load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys,
NULL, (uint64_t *)&entry, NULL,
(uint64_t *)&kernel_high, big_endian,
- ELF_MACHINE, 1);
+ EM_MIPS, 1, 0);
if (kernel_size >= 0) {
if ((entry & ~0x7fffffffULL) == 0x80000000)
entry = (int32_t)entry;
@@ -233,7 +237,7 @@ void mips_r4k_init(MachineState *machine)
if ((bios_size > 0) && (bios_size <= BIOS_SIZE)) {
bios = g_new(MemoryRegion, 1);
memory_region_init_ram(bios, NULL, "mips_r4k.bios", BIOS_SIZE,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(bios);
memory_region_set_readonly(bios, true);
memory_region_add_subregion(get_system_memory(), 0x1fc00000, bios);
@@ -252,9 +256,7 @@ void mips_r4k_init(MachineState *machine)
fprintf(stderr, "qemu: Warning, could not load MIPS bios '%s'\n",
bios_name);
}
- if (filename) {
- g_free(filename);
- }
+ g_free(filename);
if (kernel_filename) {
loaderparams.ram_size = ram_size;
@@ -274,7 +276,7 @@ void mips_r4k_init(MachineState *machine)
memory_region_init(isa_mem, NULL, "isa-mem", 0x01000000);
memory_region_add_subregion(get_system_memory(), 0x14000000, isa_io);
memory_region_add_subregion(get_system_memory(), 0x10000000, isa_mem);
- isa_bus = isa_bus_new(NULL, isa_mem, get_system_io());
+ isa_bus = isa_bus_new(NULL, isa_mem, get_system_io(), &error_abort);
/* The PIC is attached to the MIPS CPU INT0 pin */
i8259 = i8259_init(isa_bus, env->irq[2]);
@@ -300,15 +302,10 @@ void mips_r4k_init(MachineState *machine)
isa_create_simple(isa_bus, "i8042");
}
-static QEMUMachine mips_machine = {
- .name = "mips",
- .desc = "mips r4k platform",
- .init = mips_r4k_init,
-};
-
-static void mips_machine_init(void)
+static void mips_machine_init(MachineClass *mc)
{
- qemu_register_machine(&mips_machine);
+ mc->desc = "mips r4k platform";
+ mc->init = mips_r4k_init;
}
-machine_init(mips_machine_init);
+DEFINE_MACHINE("mips", mips_machine_init)
diff --git a/qemu/hw/misc/Makefile.objs b/qemu/hw/misc/Makefile.objs
index 4aa76ffec..93f952880 100644
--- a/qemu/hw/misc/Makefile.objs
+++ b/qemu/hw/misc/Makefile.objs
@@ -26,6 +26,9 @@ obj-$(CONFIG_NSERIES) += cbus.o
obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o
obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o
obj-$(CONFIG_IMX) += imx_ccm.o
+obj-$(CONFIG_IMX) += imx31_ccm.o
+obj-$(CONFIG_IMX) += imx25_ccm.o
+obj-$(CONFIG_IMX) += imx6_ccm.o
obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o
obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o
obj-$(CONFIG_MAINSTONE) += mst_fpga.o
@@ -34,9 +37,16 @@ obj-$(CONFIG_OMAP) += omap_gpmc.o
obj-$(CONFIG_OMAP) += omap_l4.o
obj-$(CONFIG_OMAP) += omap_sdrc.o
obj-$(CONFIG_OMAP) += omap_tap.o
+obj-$(CONFIG_RASPI) += bcm2835_mbox.o
+obj-$(CONFIG_RASPI) += bcm2835_property.o
obj-$(CONFIG_SLAVIO) += slavio_misc.o
obj-$(CONFIG_ZYNQ) += zynq_slcr.o
+obj-$(CONFIG_ZYNQ) += zynq-xadc.o
obj-$(CONFIG_STM32F2XX_SYSCFG) += stm32f2xx_syscfg.o
+obj-$(CONFIG_MIPS_CPS) += mips_cmgcr.o
+obj-$(CONFIG_MIPS_CPS) += mips_cpc.o
+obj-$(CONFIG_MIPS_ITU) += mips_itu.o
obj-$(CONFIG_PVPANIC) += pvpanic.o
obj-$(CONFIG_EDU) += edu.o
+obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o
diff --git a/qemu/hw/misc/a9scu.c b/qemu/hw/misc/a9scu.c
index 443494590..3e8ad8cd7 100644
--- a/qemu/hw/misc/a9scu.c
+++ b/qemu/hw/misc/a9scu.c
@@ -8,6 +8,7 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
#include "hw/misc/a9scu.h"
static uint64_t a9_scu_read(void *opaque, hwaddr offset,
diff --git a/qemu/hw/misc/applesmc.c b/qemu/hw/misc/applesmc.c
index 6bd61e782..77fab5b9d 100644
--- a/qemu/hw/misc/applesmc.c
+++ b/qemu/hw/misc/applesmc.c
@@ -30,6 +30,7 @@
*
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/isa/isa.h"
#include "ui/console.h"
diff --git a/qemu/hw/misc/arm11scu.c b/qemu/hw/misc/arm11scu.c
index a79167544..5e54b494b 100644
--- a/qemu/hw/misc/arm11scu.c
+++ b/qemu/hw/misc/arm11scu.c
@@ -8,6 +8,7 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
#include "hw/misc/arm11scu.h"
static uint64_t mpcore_scu_read(void *opaque, hwaddr offset,
diff --git a/qemu/hw/misc/arm_integrator_debug.c b/qemu/hw/misc/arm_integrator_debug.c
index 99b720fbb..902605fef 100644
--- a/qemu/hw/misc/arm_integrator_debug.c
+++ b/qemu/hw/misc/arm_integrator_debug.c
@@ -14,6 +14,7 @@
* See the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "exec/address-spaces.h"
@@ -79,7 +80,7 @@ static void intdbg_control_init(Object *obj)
SysBusDevice *sd = SYS_BUS_DEVICE(obj);
IntegratorDebugState *s = INTEGRATOR_DEBUG(obj);
- memory_region_init_io(&s->iomem, NULL, &intdbg_control_ops,
+ memory_region_init_io(&s->iomem, obj, &intdbg_control_ops,
NULL, "dbg-leds", 0x1000000);
sysbus_init_mmio(sd, &s->iomem);
}
diff --git a/qemu/hw/misc/arm_l2x0.c b/qemu/hw/misc/arm_l2x0.c
index 9e220c9a5..7e179f1a4 100644
--- a/qemu/hw/misc/arm_l2x0.c
+++ b/qemu/hw/misc/arm_l2x0.c
@@ -18,6 +18,7 @@
*
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
/* L2C-310 r3p2 */
diff --git a/qemu/hw/misc/arm_sysctl.c b/qemu/hw/misc/arm_sysctl.c
index 3fad6f86d..34d90d523 100644
--- a/qemu/hw/misc/arm_sysctl.c
+++ b/qemu/hw/misc/arm_sysctl.c
@@ -7,6 +7,7 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "qemu/timer.h"
#include "qemu/bitops.h"
@@ -170,7 +171,8 @@ static uint64_t arm_sysctl_read(void *opaque, hwaddr offset,
case 0x58: /* BOOTCS */
return 0;
case 0x5c: /* 24MHz */
- return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 24000000, get_ticks_per_sec());
+ return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 24000000,
+ NANOSECONDS_PER_SECOND);
case 0x60: /* MISC */
return 0;
case 0x84: /* PROCID0 */
diff --git a/qemu/hw/misc/bcm2835_mbox.c b/qemu/hw/misc/bcm2835_mbox.c
new file mode 100644
index 000000000..263280fd4
--- /dev/null
+++ b/qemu/hw/misc/bcm2835_mbox.c
@@ -0,0 +1,335 @@
+/*
+ * Raspberry Pi emulation (c) 2012 Gregory Estrade
+ * This code is licensed under the GNU GPLv2 and later.
+ *
+ * This file models the system mailboxes, which are used for
+ * communication with low-bandwidth GPU peripherals. Refs:
+ * https://github.com/raspberrypi/firmware/wiki/Mailboxes
+ * https://github.com/raspberrypi/firmware/wiki/Accessing-mailboxes
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/misc/bcm2835_mbox.h"
+
+#define MAIL0_PEEK 0x90
+#define MAIL0_SENDER 0x94
+#define MAIL1_STATUS 0xb8
+
+/* Mailbox status register */
+#define MAIL0_STATUS 0x98
+#define ARM_MS_FULL 0x80000000
+#define ARM_MS_EMPTY 0x40000000
+#define ARM_MS_LEVEL 0x400000FF /* Max. value depends on mailbox depth */
+
+/* MAILBOX config/status register */
+#define MAIL0_CONFIG 0x9c
+/* ANY write to this register clears the error bits! */
+#define ARM_MC_IHAVEDATAIRQEN 0x00000001 /* mbox irq enable: has data */
+#define ARM_MC_IHAVESPACEIRQEN 0x00000002 /* mbox irq enable: has space */
+#define ARM_MC_OPPISEMPTYIRQEN 0x00000004 /* mbox irq enable: Opp is empty */
+#define ARM_MC_MAIL_CLEAR 0x00000008 /* mbox clear write 1, then 0 */
+#define ARM_MC_IHAVEDATAIRQPEND 0x00000010 /* mbox irq pending: has space */
+#define ARM_MC_IHAVESPACEIRQPEND 0x00000020 /* mbox irq pending: Opp is empty */
+#define ARM_MC_OPPISEMPTYIRQPEND 0x00000040 /* mbox irq pending */
+/* Bit 7 is unused */
+#define ARM_MC_ERRNOOWN 0x00000100 /* error : none owner read from mailbox */
+#define ARM_MC_ERROVERFLW 0x00000200 /* error : write to fill mailbox */
+#define ARM_MC_ERRUNDRFLW 0x00000400 /* error : read from empty mailbox */
+
+static void mbox_update_status(BCM2835Mbox *mb)
+{
+ mb->status &= ~(ARM_MS_EMPTY | ARM_MS_FULL);
+ if (mb->count == 0) {
+ mb->status |= ARM_MS_EMPTY;
+ } else if (mb->count == MBOX_SIZE) {
+ mb->status |= ARM_MS_FULL;
+ }
+}
+
+static void mbox_reset(BCM2835Mbox *mb)
+{
+ int n;
+
+ mb->count = 0;
+ mb->config = 0;
+ for (n = 0; n < MBOX_SIZE; n++) {
+ mb->reg[n] = MBOX_INVALID_DATA;
+ }
+ mbox_update_status(mb);
+}
+
+static uint32_t mbox_pull(BCM2835Mbox *mb, int index)
+{
+ int n;
+ uint32_t val;
+
+ assert(mb->count > 0);
+ assert(index < mb->count);
+
+ val = mb->reg[index];
+ for (n = index + 1; n < mb->count; n++) {
+ mb->reg[n - 1] = mb->reg[n];
+ }
+ mb->count--;
+ mb->reg[mb->count] = MBOX_INVALID_DATA;
+
+ mbox_update_status(mb);
+
+ return val;
+}
+
+static void mbox_push(BCM2835Mbox *mb, uint32_t val)
+{
+ assert(mb->count < MBOX_SIZE);
+ mb->reg[mb->count++] = val;
+ mbox_update_status(mb);
+}
+
+static void bcm2835_mbox_update(BCM2835MboxState *s)
+{
+ uint32_t value;
+ bool set;
+ int n;
+
+ s->mbox_irq_disabled = true;
+
+ /* Get pending responses and put them in the vc->arm mbox,
+ * as long as it's not full
+ */
+ for (n = 0; n < MBOX_CHAN_COUNT; n++) {
+ while (s->available[n] && !(s->mbox[0].status & ARM_MS_FULL)) {
+ value = ldl_le_phys(&s->mbox_as, n << MBOX_AS_CHAN_SHIFT);
+ assert(value != MBOX_INVALID_DATA); /* Pending interrupt but no data */
+ mbox_push(&s->mbox[0], value);
+ }
+ }
+
+ /* TODO (?): Try to push pending requests from the arm->vc mbox */
+
+ /* Re-enable calls from the IRQ routine */
+ s->mbox_irq_disabled = false;
+
+ /* Update ARM IRQ status */
+ set = false;
+ s->mbox[0].config &= ~ARM_MC_IHAVEDATAIRQPEND;
+ if (!(s->mbox[0].status & ARM_MS_EMPTY)) {
+ s->mbox[0].config |= ARM_MC_IHAVEDATAIRQPEND;
+ if (s->mbox[0].config & ARM_MC_IHAVEDATAIRQEN) {
+ set = true;
+ }
+ }
+ qemu_set_irq(s->arm_irq, set);
+}
+
+static void bcm2835_mbox_set_irq(void *opaque, int irq, int level)
+{
+ BCM2835MboxState *s = opaque;
+
+ s->available[irq] = level;
+
+ /* avoid recursively calling bcm2835_mbox_update when the interrupt
+ * status changes due to the ldl_phys call within that function
+ */
+ if (!s->mbox_irq_disabled) {
+ bcm2835_mbox_update(s);
+ }
+}
+
+static uint64_t bcm2835_mbox_read(void *opaque, hwaddr offset, unsigned size)
+{
+ BCM2835MboxState *s = opaque;
+ uint32_t res = 0;
+
+ offset &= 0xff;
+
+ switch (offset) {
+ case 0x80 ... 0x8c: /* MAIL0_READ */
+ if (s->mbox[0].status & ARM_MS_EMPTY) {
+ res = MBOX_INVALID_DATA;
+ } else {
+ res = mbox_pull(&s->mbox[0], 0);
+ }
+ break;
+
+ case MAIL0_PEEK:
+ res = s->mbox[0].reg[0];
+ break;
+
+ case MAIL0_SENDER:
+ break;
+
+ case MAIL0_STATUS:
+ res = s->mbox[0].status;
+ break;
+
+ case MAIL0_CONFIG:
+ res = s->mbox[0].config;
+ break;
+
+ case MAIL1_STATUS:
+ res = s->mbox[1].status;
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return 0;
+ }
+
+ bcm2835_mbox_update(s);
+
+ return res;
+}
+
+static void bcm2835_mbox_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ BCM2835MboxState *s = opaque;
+ hwaddr childaddr;
+ uint8_t ch;
+
+ offset &= 0xff;
+
+ switch (offset) {
+ case MAIL0_SENDER:
+ break;
+
+ case MAIL0_CONFIG:
+ s->mbox[0].config &= ~ARM_MC_IHAVEDATAIRQEN;
+ s->mbox[0].config |= value & ARM_MC_IHAVEDATAIRQEN;
+ break;
+
+ case 0xa0 ... 0xac: /* MAIL1_WRITE */
+ if (s->mbox[1].status & ARM_MS_FULL) {
+ /* Mailbox full */
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: mailbox full\n", __func__);
+ } else {
+ ch = value & 0xf;
+ if (ch < MBOX_CHAN_COUNT) {
+ childaddr = ch << MBOX_AS_CHAN_SHIFT;
+ if (ldl_le_phys(&s->mbox_as, childaddr + MBOX_AS_PENDING)) {
+ /* Child busy, push delayed. Push it in the arm->vc mbox */
+ mbox_push(&s->mbox[1], value);
+ } else {
+ /* Push it directly to the child device */
+ stl_le_phys(&s->mbox_as, childaddr, value);
+ }
+ } else {
+ /* Invalid channel number */
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid channel %u\n",
+ __func__, ch);
+ }
+ }
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return;
+ }
+
+ bcm2835_mbox_update(s);
+}
+
+static const MemoryRegionOps bcm2835_mbox_ops = {
+ .read = bcm2835_mbox_read,
+ .write = bcm2835_mbox_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+/* vmstate of a single mailbox */
+static const VMStateDescription vmstate_bcm2835_mbox_box = {
+ .name = TYPE_BCM2835_MBOX "_box",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(reg, BCM2835Mbox, MBOX_SIZE),
+ VMSTATE_UINT32(count, BCM2835Mbox),
+ VMSTATE_UINT32(status, BCM2835Mbox),
+ VMSTATE_UINT32(config, BCM2835Mbox),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+/* vmstate of the entire device */
+static const VMStateDescription vmstate_bcm2835_mbox = {
+ .name = TYPE_BCM2835_MBOX,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_BOOL_ARRAY(available, BCM2835MboxState, MBOX_CHAN_COUNT),
+ VMSTATE_STRUCT_ARRAY(mbox, BCM2835MboxState, 2, 1,
+ vmstate_bcm2835_mbox_box, BCM2835Mbox),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void bcm2835_mbox_init(Object *obj)
+{
+ BCM2835MboxState *s = BCM2835_MBOX(obj);
+
+ memory_region_init_io(&s->iomem, obj, &bcm2835_mbox_ops, s,
+ TYPE_BCM2835_MBOX, 0x400);
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
+ sysbus_init_irq(SYS_BUS_DEVICE(s), &s->arm_irq);
+ qdev_init_gpio_in(DEVICE(s), bcm2835_mbox_set_irq, MBOX_CHAN_COUNT);
+}
+
+static void bcm2835_mbox_reset(DeviceState *dev)
+{
+ BCM2835MboxState *s = BCM2835_MBOX(dev);
+ int n;
+
+ mbox_reset(&s->mbox[0]);
+ mbox_reset(&s->mbox[1]);
+ s->mbox_irq_disabled = false;
+ for (n = 0; n < MBOX_CHAN_COUNT; n++) {
+ s->available[n] = false;
+ }
+}
+
+static void bcm2835_mbox_realize(DeviceState *dev, Error **errp)
+{
+ BCM2835MboxState *s = BCM2835_MBOX(dev);
+ Object *obj;
+ Error *err = NULL;
+
+ obj = object_property_get_link(OBJECT(dev), "mbox-mr", &err);
+ if (obj == NULL) {
+ error_setg(errp, "%s: required mbox-mr link not found: %s",
+ __func__, error_get_pretty(err));
+ return;
+ }
+
+ s->mbox_mr = MEMORY_REGION(obj);
+ address_space_init(&s->mbox_as, s->mbox_mr, NULL);
+ bcm2835_mbox_reset(dev);
+}
+
+static void bcm2835_mbox_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = bcm2835_mbox_realize;
+ dc->reset = bcm2835_mbox_reset;
+ dc->vmsd = &vmstate_bcm2835_mbox;
+}
+
+static TypeInfo bcm2835_mbox_info = {
+ .name = TYPE_BCM2835_MBOX,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BCM2835MboxState),
+ .class_init = bcm2835_mbox_class_init,
+ .instance_init = bcm2835_mbox_init,
+};
+
+static void bcm2835_mbox_register_types(void)
+{
+ type_register_static(&bcm2835_mbox_info);
+}
+
+type_init(bcm2835_mbox_register_types)
diff --git a/qemu/hw/misc/bcm2835_property.c b/qemu/hw/misc/bcm2835_property.c
new file mode 100644
index 000000000..530411f84
--- /dev/null
+++ b/qemu/hw/misc/bcm2835_property.c
@@ -0,0 +1,424 @@
+/*
+ * Raspberry Pi emulation (c) 2012 Gregory Estrade
+ * This code is licensed under the GNU GPLv2 and later.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/misc/bcm2835_property.h"
+#include "hw/misc/bcm2835_mbox_defs.h"
+#include "sysemu/dma.h"
+
+/* https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */
+
+static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
+{
+ uint32_t tag;
+ uint32_t bufsize;
+ uint32_t tot_len;
+ size_t resplen;
+ uint32_t tmp;
+ int n;
+ uint32_t offset, length, color;
+ uint32_t xres, yres, xoffset, yoffset, bpp, pixo, alpha;
+ uint32_t *newxres = NULL, *newyres = NULL, *newxoffset = NULL,
+ *newyoffset = NULL, *newbpp = NULL, *newpixo = NULL, *newalpha = NULL;
+
+ value &= ~0xf;
+
+ s->addr = value;
+
+ tot_len = ldl_le_phys(&s->dma_as, value);
+
+ /* @(addr + 4) : Buffer response code */
+ value = s->addr + 8;
+ while (value + 8 <= s->addr + tot_len) {
+ tag = ldl_le_phys(&s->dma_as, value);
+ bufsize = ldl_le_phys(&s->dma_as, value + 4);
+ /* @(value + 8) : Request/response indicator */
+ resplen = 0;
+ switch (tag) {
+ case 0x00000000: /* End tag */
+ break;
+ case 0x00000001: /* Get firmware revision */
+ stl_le_phys(&s->dma_as, value + 12, 346337);
+ resplen = 4;
+ break;
+ case 0x00010001: /* Get board model */
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_property: %x get board model NYI\n", tag);
+ resplen = 4;
+ break;
+ case 0x00010002: /* Get board revision */
+ stl_le_phys(&s->dma_as, value + 12, s->board_rev);
+ resplen = 4;
+ break;
+ case 0x00010003: /* Get board MAC address */
+ resplen = sizeof(s->macaddr.a);
+ dma_memory_write(&s->dma_as, value + 12, s->macaddr.a, resplen);
+ break;
+ case 0x00010004: /* Get board serial */
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_property: %x get board serial NYI\n", tag);
+ resplen = 8;
+ break;
+ case 0x00010005: /* Get ARM memory */
+ /* base */
+ stl_le_phys(&s->dma_as, value + 12, 0);
+ /* size */
+ stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_base);
+ resplen = 8;
+ break;
+ case 0x00010006: /* Get VC memory */
+ /* base */
+ stl_le_phys(&s->dma_as, value + 12, s->fbdev->vcram_base);
+ /* size */
+ stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_size);
+ resplen = 8;
+ break;
+ case 0x00028001: /* Set power state */
+ /* Assume that whatever device they asked for exists,
+ * and we'll just claim we set it to the desired state
+ */
+ tmp = ldl_le_phys(&s->dma_as, value + 16);
+ stl_le_phys(&s->dma_as, value + 16, (tmp & 1));
+ resplen = 8;
+ break;
+
+ /* Clocks */
+
+ case 0x00030001: /* Get clock state */
+ stl_le_phys(&s->dma_as, value + 16, 0x1);
+ resplen = 8;
+ break;
+
+ case 0x00038001: /* Set clock state */
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_property: %x set clock state NYI\n", tag);
+ resplen = 8;
+ break;
+
+ case 0x00030002: /* Get clock rate */
+ case 0x00030004: /* Get max clock rate */
+ case 0x00030007: /* Get min clock rate */
+ switch (ldl_le_phys(&s->dma_as, value + 12)) {
+ case 1: /* EMMC */
+ stl_le_phys(&s->dma_as, value + 16, 50000000);
+ break;
+ case 2: /* UART */
+ stl_le_phys(&s->dma_as, value + 16, 3000000);
+ break;
+ default:
+ stl_le_phys(&s->dma_as, value + 16, 700000000);
+ break;
+ }
+ resplen = 8;
+ break;
+
+ case 0x00038002: /* Set clock rate */
+ case 0x00038004: /* Set max clock rate */
+ case 0x00038007: /* Set min clock rate */
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_property: %x set clock rates NYI\n", tag);
+ resplen = 8;
+ break;
+
+ /* Temperature */
+
+ case 0x00030006: /* Get temperature */
+ stl_le_phys(&s->dma_as, value + 16, 25000);
+ resplen = 8;
+ break;
+
+ case 0x0003000A: /* Get max temperature */
+ stl_le_phys(&s->dma_as, value + 16, 99000);
+ resplen = 8;
+ break;
+
+ /* Frame buffer */
+
+ case 0x00040001: /* Allocate buffer */
+ stl_le_phys(&s->dma_as, value + 12, s->fbdev->base);
+ stl_le_phys(&s->dma_as, value + 16, s->fbdev->size);
+ resplen = 8;
+ break;
+ case 0x00048001: /* Release buffer */
+ resplen = 0;
+ break;
+ case 0x00040002: /* Blank screen */
+ resplen = 4;
+ break;
+ case 0x00040003: /* Get display width/height */
+ case 0x00040004:
+ stl_le_phys(&s->dma_as, value + 12, s->fbdev->xres);
+ stl_le_phys(&s->dma_as, value + 16, s->fbdev->yres);
+ resplen = 8;
+ break;
+ case 0x00044003: /* Test display width/height */
+ case 0x00044004:
+ resplen = 8;
+ break;
+ case 0x00048003: /* Set display width/height */
+ case 0x00048004:
+ xres = ldl_le_phys(&s->dma_as, value + 12);
+ newxres = &xres;
+ yres = ldl_le_phys(&s->dma_as, value + 16);
+ newyres = &yres;
+ resplen = 8;
+ break;
+ case 0x00040005: /* Get depth */
+ stl_le_phys(&s->dma_as, value + 12, s->fbdev->bpp);
+ resplen = 4;
+ break;
+ case 0x00044005: /* Test depth */
+ resplen = 4;
+ break;
+ case 0x00048005: /* Set depth */
+ bpp = ldl_le_phys(&s->dma_as, value + 12);
+ newbpp = &bpp;
+ resplen = 4;
+ break;
+ case 0x00040006: /* Get pixel order */
+ stl_le_phys(&s->dma_as, value + 12, s->fbdev->pixo);
+ resplen = 4;
+ break;
+ case 0x00044006: /* Test pixel order */
+ resplen = 4;
+ break;
+ case 0x00048006: /* Set pixel order */
+ pixo = ldl_le_phys(&s->dma_as, value + 12);
+ newpixo = &pixo;
+ resplen = 4;
+ break;
+ case 0x00040007: /* Get alpha */
+ stl_le_phys(&s->dma_as, value + 12, s->fbdev->alpha);
+ resplen = 4;
+ break;
+ case 0x00044007: /* Test pixel alpha */
+ resplen = 4;
+ break;
+ case 0x00048007: /* Set alpha */
+ alpha = ldl_le_phys(&s->dma_as, value + 12);
+ newalpha = &alpha;
+ resplen = 4;
+ break;
+ case 0x00040008: /* Get pitch */
+ stl_le_phys(&s->dma_as, value + 12, s->fbdev->pitch);
+ resplen = 4;
+ break;
+ case 0x00040009: /* Get virtual offset */
+ stl_le_phys(&s->dma_as, value + 12, s->fbdev->xoffset);
+ stl_le_phys(&s->dma_as, value + 16, s->fbdev->yoffset);
+ resplen = 8;
+ break;
+ case 0x00044009: /* Test virtual offset */
+ resplen = 8;
+ break;
+ case 0x00048009: /* Set virtual offset */
+ xoffset = ldl_le_phys(&s->dma_as, value + 12);
+ newxoffset = &xoffset;
+ yoffset = ldl_le_phys(&s->dma_as, value + 16);
+ newyoffset = &yoffset;
+ resplen = 8;
+ break;
+ case 0x0004000a: /* Get/Test/Set overscan */
+ case 0x0004400a:
+ case 0x0004800a:
+ stl_le_phys(&s->dma_as, value + 12, 0);
+ stl_le_phys(&s->dma_as, value + 16, 0);
+ stl_le_phys(&s->dma_as, value + 20, 0);
+ stl_le_phys(&s->dma_as, value + 24, 0);
+ resplen = 16;
+ break;
+ case 0x0004800b: /* Set palette */
+ offset = ldl_le_phys(&s->dma_as, value + 12);
+ length = ldl_le_phys(&s->dma_as, value + 16);
+ n = 0;
+ while (n < length - offset) {
+ color = ldl_le_phys(&s->dma_as, value + 20 + (n << 2));
+ stl_le_phys(&s->dma_as,
+ s->fbdev->vcram_base + ((offset + n) << 2), color);
+ n++;
+ }
+ stl_le_phys(&s->dma_as, value + 12, 0);
+ resplen = 4;
+ break;
+
+ case 0x00060001: /* Get DMA channels */
+ /* channels 2-5 */
+ stl_le_phys(&s->dma_as, value + 12, 0x003C);
+ resplen = 4;
+ break;
+
+ case 0x00050001: /* Get command line */
+ resplen = 0;
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "bcm2835_property: unhandled tag %08x\n", tag);
+ break;
+ }
+
+ if (tag == 0) {
+ break;
+ }
+
+ stl_le_phys(&s->dma_as, value + 8, (1 << 31) | resplen);
+ value += bufsize + 12;
+ }
+
+ /* Reconfigure framebuffer if required */
+ if (newxres || newyres || newxoffset || newyoffset || newbpp || newpixo
+ || newalpha) {
+ bcm2835_fb_reconfigure(s->fbdev, newxres, newyres, newxoffset,
+ newyoffset, newbpp, newpixo, newalpha);
+ }
+
+ /* Buffer response code */
+ stl_le_phys(&s->dma_as, s->addr + 4, (1 << 31));
+}
+
+static uint64_t bcm2835_property_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ BCM2835PropertyState *s = opaque;
+ uint32_t res = 0;
+
+ switch (offset) {
+ case MBOX_AS_DATA:
+ res = MBOX_CHAN_PROPERTY | s->addr;
+ s->pending = false;
+ qemu_set_irq(s->mbox_irq, 0);
+ break;
+
+ case MBOX_AS_PENDING:
+ res = s->pending;
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return 0;
+ }
+
+ return res;
+}
+
+static void bcm2835_property_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ BCM2835PropertyState *s = opaque;
+
+ switch (offset) {
+ case MBOX_AS_DATA:
+ /* bcm2835_mbox should check our pending status before pushing */
+ assert(!s->pending);
+ s->pending = true;
+ bcm2835_property_mbox_push(s, value);
+ qemu_set_irq(s->mbox_irq, 1);
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return;
+ }
+}
+
+static const MemoryRegionOps bcm2835_property_ops = {
+ .read = bcm2835_property_read,
+ .write = bcm2835_property_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+static const VMStateDescription vmstate_bcm2835_property = {
+ .name = TYPE_BCM2835_PROPERTY,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_MACADDR(macaddr, BCM2835PropertyState),
+ VMSTATE_UINT32(addr, BCM2835PropertyState),
+ VMSTATE_BOOL(pending, BCM2835PropertyState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void bcm2835_property_init(Object *obj)
+{
+ BCM2835PropertyState *s = BCM2835_PROPERTY(obj);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_property_ops, s,
+ TYPE_BCM2835_PROPERTY, 0x10);
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
+ sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq);
+}
+
+static void bcm2835_property_reset(DeviceState *dev)
+{
+ BCM2835PropertyState *s = BCM2835_PROPERTY(dev);
+
+ s->pending = false;
+}
+
+static void bcm2835_property_realize(DeviceState *dev, Error **errp)
+{
+ BCM2835PropertyState *s = BCM2835_PROPERTY(dev);
+ Object *obj;
+ Error *err = NULL;
+
+ obj = object_property_get_link(OBJECT(dev), "fb", &err);
+ if (obj == NULL) {
+ error_setg(errp, "%s: required fb link not found: %s",
+ __func__, error_get_pretty(err));
+ return;
+ }
+
+ s->fbdev = BCM2835_FB(obj);
+
+ obj = object_property_get_link(OBJECT(dev), "dma-mr", &err);
+ if (obj == NULL) {
+ error_setg(errp, "%s: required dma-mr link not found: %s",
+ __func__, error_get_pretty(err));
+ return;
+ }
+
+ s->dma_mr = MEMORY_REGION(obj);
+ address_space_init(&s->dma_as, s->dma_mr, NULL);
+
+ /* TODO: connect to MAC address of USB NIC device, once we emulate it */
+ qemu_macaddr_default_if_unset(&s->macaddr);
+
+ bcm2835_property_reset(dev);
+}
+
+static Property bcm2835_property_props[] = {
+ DEFINE_PROP_UINT32("board-rev", BCM2835PropertyState, board_rev, 0),
+ DEFINE_PROP_END_OF_LIST()
+};
+
+static void bcm2835_property_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->props = bcm2835_property_props;
+ dc->realize = bcm2835_property_realize;
+ dc->vmsd = &vmstate_bcm2835_property;
+}
+
+static TypeInfo bcm2835_property_info = {
+ .name = TYPE_BCM2835_PROPERTY,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BCM2835PropertyState),
+ .class_init = bcm2835_property_class_init,
+ .instance_init = bcm2835_property_init,
+};
+
+static void bcm2835_property_register_types(void)
+{
+ type_register_static(&bcm2835_property_info);
+}
+
+type_init(bcm2835_property_register_types)
diff --git a/qemu/hw/misc/cbus.c b/qemu/hw/misc/cbus.c
index 495d5078f..0c207e310 100644
--- a/qemu/hw/misc/cbus.c
+++ b/qemu/hw/misc/cbus.c
@@ -20,7 +20,8 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
-#include "qemu-common.h"
+#include "qemu/osdep.h"
+#include "hw/hw.h"
#include "hw/irq.h"
#include "hw/devices.h"
#include "sysemu/sysemu.h"
diff --git a/qemu/hw/misc/debugexit.c b/qemu/hw/misc/debugexit.c
index 69a1b004c..84fa1a5b9 100644
--- a/qemu/hw/misc/debugexit.c
+++ b/qemu/hw/misc/debugexit.c
@@ -7,6 +7,7 @@
* (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/isa/isa.h"
diff --git a/qemu/hw/misc/eccmemctl.c b/qemu/hw/misc/eccmemctl.c
index 8bad6f682..a0071f3ea 100644
--- a/qemu/hw/misc/eccmemctl.c
+++ b/qemu/hw/misc/eccmemctl.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "trace.h"
diff --git a/qemu/hw/misc/edu.c b/qemu/hw/misc/edu.c
index fe50b42af..888ba49a0 100644
--- a/qemu/hw/misc/edu.c
+++ b/qemu/hw/misc/edu.c
@@ -22,6 +22,7 @@
* DEALINGS IN THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/pci/pci.h"
#include "qemu/timer.h"
#include "qemu/main-loop.h" /* iothread mutex */
@@ -327,7 +328,7 @@ static void *edu_fact_thread(void *opaque)
return NULL;
}
-static int pci_edu_init(PCIDevice *pdev)
+static void pci_edu_realize(PCIDevice *pdev, Error **errp)
{
EduState *edu = DO_UPCAST(EduState, pdev, pdev);
uint8_t *pci_conf = pdev->config;
@@ -344,8 +345,6 @@ static int pci_edu_init(PCIDevice *pdev)
memory_region_init_io(&edu->mmio, OBJECT(edu), &edu_mmio_ops, edu,
"edu-mmio", 1 << 20);
pci_register_bar(pdev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &edu->mmio);
-
- return 0;
}
static void pci_edu_uninit(PCIDevice *pdev)
@@ -364,12 +363,12 @@ static void pci_edu_uninit(PCIDevice *pdev)
timer_del(&edu->dma_timer);
}
-static void edu_obj_uint64(Object *obj, struct Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void edu_obj_uint64(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
uint64_t *val = opaque;
- visit_type_uint64(v, val, name, errp);
+ visit_type_uint64(v, name, val, errp);
}
static void edu_instance_init(Object *obj)
@@ -385,7 +384,7 @@ static void edu_class_init(ObjectClass *class, void *data)
{
PCIDeviceClass *k = PCI_DEVICE_CLASS(class);
- k->init = pci_edu_init;
+ k->realize = pci_edu_realize;
k->exit = pci_edu_uninit;
k->vendor_id = PCI_VENDOR_ID_QEMU;
k->device_id = 0x11e8;
diff --git a/qemu/hw/misc/exynos4210_pmu.c b/qemu/hw/misc/exynos4210_pmu.c
index 2b118c725..889abadfe 100644
--- a/qemu/hw/misc/exynos4210_pmu.c
+++ b/qemu/hw/misc/exynos4210_pmu.c
@@ -24,6 +24,7 @@
* uses PMU INFORM5 register as a holding pen.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#ifndef DEBUG_PMU
diff --git a/qemu/hw/misc/hyperv_testdev.c b/qemu/hw/misc/hyperv_testdev.c
new file mode 100644
index 000000000..1883fd7f2
--- /dev/null
+++ b/qemu/hw/misc/hyperv_testdev.c
@@ -0,0 +1,168 @@
+/*
+ * QEMU KVM Hyper-V test device to support Hyper-V kvm-unit-tests
+ *
+ * Copyright (C) 2015 Andrey Smetanin <asmetanin@virtuozzo.com>
+ *
+ * Authors:
+ * Andrey Smetanin <asmetanin@virtuozzo.com>
+ *
+ * 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 "hw/hw.h"
+#include "hw/qdev.h"
+#include "hw/isa/isa.h"
+#include "sysemu/kvm.h"
+#include "linux/kvm.h"
+#include "target-i386/hyperv.h"
+#include "kvm_i386.h"
+
+#define HV_TEST_DEV_MAX_SINT_ROUTES 64
+
+struct HypervTestDev {
+ ISADevice parent_obj;
+ MemoryRegion sint_control;
+ HvSintRoute *sint_route[HV_TEST_DEV_MAX_SINT_ROUTES];
+};
+typedef struct HypervTestDev HypervTestDev;
+
+#define TYPE_HYPERV_TEST_DEV "hyperv-testdev"
+#define HYPERV_TEST_DEV(obj) \
+ OBJECT_CHECK(HypervTestDev, (obj), TYPE_HYPERV_TEST_DEV)
+
+enum {
+ HV_TEST_DEV_SINT_ROUTE_CREATE = 1,
+ HV_TEST_DEV_SINT_ROUTE_DESTROY,
+ HV_TEST_DEV_SINT_ROUTE_SET_SINT
+};
+
+static int alloc_sint_route_index(HypervTestDev *dev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dev->sint_route); i++) {
+ if (dev->sint_route[i] == NULL) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static void free_sint_route_index(HypervTestDev *dev, int i)
+{
+ assert(i >= 0 && i < ARRAY_SIZE(dev->sint_route));
+ dev->sint_route[i] = NULL;
+}
+
+static int find_sint_route_index(HypervTestDev *dev, uint32_t vcpu_id,
+ uint32_t sint)
+{
+ HvSintRoute *sint_route;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dev->sint_route); i++) {
+ sint_route = dev->sint_route[i];
+ if (sint_route && sint_route->vcpu_id == vcpu_id &&
+ sint_route->sint == sint) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static void hv_synic_test_dev_control(HypervTestDev *dev, uint32_t ctl,
+ uint32_t vcpu_id, uint32_t sint)
+{
+ int i;
+ HvSintRoute *sint_route;
+
+ switch (ctl) {
+ case HV_TEST_DEV_SINT_ROUTE_CREATE:
+ i = alloc_sint_route_index(dev);
+ assert(i >= 0);
+ sint_route = kvm_hv_sint_route_create(vcpu_id, sint, NULL);
+ assert(sint_route);
+ dev->sint_route[i] = sint_route;
+ break;
+ case HV_TEST_DEV_SINT_ROUTE_DESTROY:
+ i = find_sint_route_index(dev, vcpu_id, sint);
+ assert(i >= 0);
+ sint_route = dev->sint_route[i];
+ kvm_hv_sint_route_destroy(sint_route);
+ free_sint_route_index(dev, i);
+ break;
+ case HV_TEST_DEV_SINT_ROUTE_SET_SINT:
+ i = find_sint_route_index(dev, vcpu_id, sint);
+ assert(i >= 0);
+ sint_route = dev->sint_route[i];
+ kvm_hv_sint_route_set_sint(sint_route);
+ break;
+ default:
+ break;
+ }
+}
+
+static void hv_test_dev_control(void *opaque, hwaddr addr, uint64_t data,
+ uint32_t len)
+{
+ HypervTestDev *dev = HYPERV_TEST_DEV(opaque);
+ uint8_t ctl;
+
+ ctl = (data >> 16ULL) & 0xFF;
+ switch (ctl) {
+ case HV_TEST_DEV_SINT_ROUTE_CREATE:
+ case HV_TEST_DEV_SINT_ROUTE_DESTROY:
+ case HV_TEST_DEV_SINT_ROUTE_SET_SINT: {
+ uint8_t sint = data & 0xFF;
+ uint8_t vcpu_id = (data >> 8ULL) & 0xFF;
+ hv_synic_test_dev_control(dev, ctl, vcpu_id, sint);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static const MemoryRegionOps synic_test_sint_ops = {
+ .write = hv_test_dev_control,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void hv_test_dev_realizefn(DeviceState *d, Error **errp)
+{
+ ISADevice *isa = ISA_DEVICE(d);
+ HypervTestDev *dev = HYPERV_TEST_DEV(d);
+ MemoryRegion *io = isa_address_space_io(isa);
+
+ memset(dev->sint_route, 0, sizeof(dev->sint_route));
+ memory_region_init_io(&dev->sint_control, OBJECT(dev),
+ &synic_test_sint_ops, dev,
+ "hyperv-testdev-ctl", 4);
+ memory_region_add_subregion(io, 0x3000, &dev->sint_control);
+}
+
+static void hv_test_dev_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ dc->realize = hv_test_dev_realizefn;
+}
+
+static const TypeInfo hv_test_dev_info = {
+ .name = TYPE_HYPERV_TEST_DEV,
+ .parent = TYPE_ISA_DEVICE,
+ .instance_size = sizeof(HypervTestDev),
+ .class_init = hv_test_dev_class_init,
+};
+
+static void hv_test_dev_register_types(void)
+{
+ type_register_static(&hv_test_dev_info);
+}
+type_init(hv_test_dev_register_types);
diff --git a/qemu/hw/misc/imx25_ccm.c b/qemu/hw/misc/imx25_ccm.c
new file mode 100644
index 000000000..225604d82
--- /dev/null
+++ b/qemu/hw/misc/imx25_ccm.c
@@ -0,0 +1,317 @@
+/*
+ * IMX25 Clock Control Module
+ *
+ * Copyright (C) 2012 NICTA
+ * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * To get the timer frequencies right, we need to emulate at least part of
+ * the CCM.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/misc/imx25_ccm.h"
+
+#ifndef DEBUG_IMX25_CCM
+#define DEBUG_IMX25_CCM 0
+#endif
+
+#define DPRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX25_CCM) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX25_CCM, \
+ __func__, ##args); \
+ } \
+ } while (0)
+
+static char const *imx25_ccm_reg_name(uint32_t reg)
+{
+ static char unknown[20];
+
+ switch (reg) {
+ case IMX25_CCM_MPCTL_REG:
+ return "mpctl";
+ case IMX25_CCM_UPCTL_REG:
+ return "upctl";
+ case IMX25_CCM_CCTL_REG:
+ return "cctl";
+ case IMX25_CCM_CGCR0_REG:
+ return "cgcr0";
+ case IMX25_CCM_CGCR1_REG:
+ return "cgcr1";
+ case IMX25_CCM_CGCR2_REG:
+ return "cgcr2";
+ case IMX25_CCM_PCDR0_REG:
+ return "pcdr0";
+ case IMX25_CCM_PCDR1_REG:
+ return "pcdr1";
+ case IMX25_CCM_PCDR2_REG:
+ return "pcdr2";
+ case IMX25_CCM_PCDR3_REG:
+ return "pcdr3";
+ case IMX25_CCM_RCSR_REG:
+ return "rcsr";
+ case IMX25_CCM_CRDR_REG:
+ return "crdr";
+ case IMX25_CCM_DCVR0_REG:
+ return "dcvr0";
+ case IMX25_CCM_DCVR1_REG:
+ return "dcvr1";
+ case IMX25_CCM_DCVR2_REG:
+ return "dcvr2";
+ case IMX25_CCM_DCVR3_REG:
+ return "dcvr3";
+ case IMX25_CCM_LTR0_REG:
+ return "ltr0";
+ case IMX25_CCM_LTR1_REG:
+ return "ltr1";
+ case IMX25_CCM_LTR2_REG:
+ return "ltr2";
+ case IMX25_CCM_LTR3_REG:
+ return "ltr3";
+ case IMX25_CCM_LTBR0_REG:
+ return "ltbr0";
+ case IMX25_CCM_LTBR1_REG:
+ return "ltbr1";
+ case IMX25_CCM_PMCR0_REG:
+ return "pmcr0";
+ case IMX25_CCM_PMCR1_REG:
+ return "pmcr1";
+ case IMX25_CCM_PMCR2_REG:
+ return "pmcr2";
+ case IMX25_CCM_MCR_REG:
+ return "mcr";
+ case IMX25_CCM_LPIMR0_REG:
+ return "lpimr0";
+ case IMX25_CCM_LPIMR1_REG:
+ return "lpimr1";
+ default:
+ sprintf(unknown, "[%d ?]", reg);
+ return unknown;
+ }
+}
+#define CKIH_FREQ 24000000 /* 24MHz crystal input */
+
+static const VMStateDescription vmstate_imx25_ccm = {
+ .name = TYPE_IMX25_CCM,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(reg, IMX25CCMState, IMX25_CCM_MAX_REG),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static uint32_t imx25_ccm_get_mpll_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+ IMX25CCMState *s = IMX25_CCM(dev);
+
+ if (EXTRACT(s->reg[IMX25_CCM_CCTL_REG], MPLL_BYPASS)) {
+ freq = CKIH_FREQ;
+ } else {
+ freq = imx_ccm_calc_pll(s->reg[IMX25_CCM_MPCTL_REG], CKIH_FREQ);
+ }
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx25_ccm_get_mcu_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+ IMX25CCMState *s = IMX25_CCM(dev);
+
+ freq = imx25_ccm_get_mpll_clk(dev);
+
+ if (EXTRACT(s->reg[IMX25_CCM_CCTL_REG], ARM_SRC)) {
+ freq = (freq * 3 / 4);
+ }
+
+ freq = freq / (1 + EXTRACT(s->reg[IMX25_CCM_CCTL_REG], ARM_CLK_DIV));
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx25_ccm_get_ahb_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+ IMX25CCMState *s = IMX25_CCM(dev);
+
+ freq = imx25_ccm_get_mcu_clk(dev)
+ / (1 + EXTRACT(s->reg[IMX25_CCM_CCTL_REG], AHB_CLK_DIV));
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx25_ccm_get_ipg_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+
+ freq = imx25_ccm_get_ahb_clk(dev) / 2;
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx25_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
+{
+ uint32_t freq = 0;
+ DPRINTF("Clock = %d)\n", clock);
+
+ switch (clock) {
+ case CLK_NONE:
+ break;
+ case CLK_IPG:
+ case CLK_IPG_HIGH:
+ freq = imx25_ccm_get_ipg_clk(dev);
+ break;
+ case CLK_32k:
+ freq = CKIL_FREQ;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n",
+ TYPE_IMX25_CCM, __func__, clock);
+ break;
+ }
+
+ DPRINTF("Clock = %d) = %d\n", clock, freq);
+
+ return freq;
+}
+
+static void imx25_ccm_reset(DeviceState *dev)
+{
+ IMX25CCMState *s = IMX25_CCM(dev);
+
+ DPRINTF("\n");
+
+ memset(s->reg, 0, IMX25_CCM_MAX_REG * sizeof(uint32_t));
+ s->reg[IMX25_CCM_MPCTL_REG] = 0x800b2c01;
+ s->reg[IMX25_CCM_UPCTL_REG] = 0x84042800;
+ /*
+ * The value below gives:
+ * CPU = 133 MHz, AHB = 66,5 MHz, IPG = 33 MHz.
+ */
+ s->reg[IMX25_CCM_CCTL_REG] = 0xd0030000;
+ s->reg[IMX25_CCM_CGCR0_REG] = 0x028A0100;
+ s->reg[IMX25_CCM_CGCR1_REG] = 0x04008100;
+ s->reg[IMX25_CCM_CGCR2_REG] = 0x00000438;
+ s->reg[IMX25_CCM_PCDR0_REG] = 0x01010101;
+ s->reg[IMX25_CCM_PCDR1_REG] = 0x01010101;
+ s->reg[IMX25_CCM_PCDR2_REG] = 0x01010101;
+ s->reg[IMX25_CCM_PCDR3_REG] = 0x01010101;
+ s->reg[IMX25_CCM_PMCR0_REG] = 0x00A00000;
+ s->reg[IMX25_CCM_PMCR1_REG] = 0x0000A030;
+ s->reg[IMX25_CCM_PMCR2_REG] = 0x0000A030;
+ s->reg[IMX25_CCM_MCR_REG] = 0x43000000;
+
+ /*
+ * default boot will change the reset values to allow:
+ * CPU = 399 MHz, AHB = 133 MHz, IPG = 66,5 MHz.
+ * For some reason, this doesn't work. With the value below, linux
+ * detects a 88 MHz IPG CLK instead of 66,5 MHz.
+ s->reg[IMX25_CCM_CCTL_REG] = 0x20032000;
+ */
+}
+
+static uint64_t imx25_ccm_read(void *opaque, hwaddr offset, unsigned size)
+{
+ uint32_t value = 0;
+ IMX25CCMState *s = (IMX25CCMState *)opaque;
+
+ if (offset < 0x70) {
+ value = s->reg[offset >> 2];
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX25_CCM, __func__, offset);
+ }
+
+ DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx25_ccm_reg_name(offset >> 2),
+ value);
+
+ return value;
+}
+
+static void imx25_ccm_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ IMX25CCMState *s = (IMX25CCMState *)opaque;
+
+ DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx25_ccm_reg_name(offset >> 2),
+ (uint32_t)value);
+
+ if (offset < 0x70) {
+ /*
+ * We will do a better implementation later. In particular some bits
+ * cannot be written to.
+ */
+ s->reg[offset >> 2] = value;
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX25_CCM, __func__, offset);
+ }
+}
+
+static const struct MemoryRegionOps imx25_ccm_ops = {
+ .read = imx25_ccm_read,
+ .write = imx25_ccm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ /*
+ * Our device would not work correctly if the guest was doing
+ * unaligned access. This might not be a limitation on the real
+ * device but in practice there is no reason for a guest to access
+ * this device unaligned.
+ */
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false,
+ },
+};
+
+static void imx25_ccm_init(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+ SysBusDevice *sd = SYS_BUS_DEVICE(obj);
+ IMX25CCMState *s = IMX25_CCM(obj);
+
+ memory_region_init_io(&s->iomem, OBJECT(dev), &imx25_ccm_ops, s,
+ TYPE_IMX25_CCM, 0x1000);
+ sysbus_init_mmio(sd, &s->iomem);
+}
+
+static void imx25_ccm_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ IMXCCMClass *ccm = IMX_CCM_CLASS(klass);
+
+ dc->reset = imx25_ccm_reset;
+ dc->vmsd = &vmstate_imx25_ccm;
+ dc->desc = "i.MX25 Clock Control Module";
+
+ ccm->get_clock_frequency = imx25_ccm_get_clock_frequency;
+}
+
+static const TypeInfo imx25_ccm_info = {
+ .name = TYPE_IMX25_CCM,
+ .parent = TYPE_IMX_CCM,
+ .instance_size = sizeof(IMX25CCMState),
+ .instance_init = imx25_ccm_init,
+ .class_init = imx25_ccm_class_init,
+};
+
+static void imx25_ccm_register_types(void)
+{
+ type_register_static(&imx25_ccm_info);
+}
+
+type_init(imx25_ccm_register_types)
diff --git a/qemu/hw/misc/imx31_ccm.c b/qemu/hw/misc/imx31_ccm.c
new file mode 100644
index 000000000..80c164716
--- /dev/null
+++ b/qemu/hw/misc/imx31_ccm.c
@@ -0,0 +1,344 @@
+/*
+ * IMX31 Clock Control Module
+ *
+ * Copyright (C) 2012 NICTA
+ * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * To get the timer frequencies right, we need to emulate at least part of
+ * the i.MX31 CCM.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/misc/imx31_ccm.h"
+
+#define CKIH_FREQ 26000000 /* 26MHz crystal input */
+
+#ifndef DEBUG_IMX31_CCM
+#define DEBUG_IMX31_CCM 0
+#endif
+
+#define DPRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX31_CCM) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX31_CCM, \
+ __func__, ##args); \
+ } \
+ } while (0)
+
+static char const *imx31_ccm_reg_name(uint32_t reg)
+{
+ static char unknown[20];
+
+ switch (reg) {
+ case IMX31_CCM_CCMR_REG:
+ return "CCMR";
+ case IMX31_CCM_PDR0_REG:
+ return "PDR0";
+ case IMX31_CCM_PDR1_REG:
+ return "PDR1";
+ case IMX31_CCM_RCSR_REG:
+ return "RCSR";
+ case IMX31_CCM_MPCTL_REG:
+ return "MPCTL";
+ case IMX31_CCM_UPCTL_REG:
+ return "UPCTL";
+ case IMX31_CCM_SPCTL_REG:
+ return "SPCTL";
+ case IMX31_CCM_COSR_REG:
+ return "COSR";
+ case IMX31_CCM_CGR0_REG:
+ return "CGR0";
+ case IMX31_CCM_CGR1_REG:
+ return "CGR1";
+ case IMX31_CCM_CGR2_REG:
+ return "CGR2";
+ case IMX31_CCM_WIMR_REG:
+ return "WIMR";
+ case IMX31_CCM_LDC_REG:
+ return "LDC";
+ case IMX31_CCM_DCVR0_REG:
+ return "DCVR0";
+ case IMX31_CCM_DCVR1_REG:
+ return "DCVR1";
+ case IMX31_CCM_DCVR2_REG:
+ return "DCVR2";
+ case IMX31_CCM_DCVR3_REG:
+ return "DCVR3";
+ case IMX31_CCM_LTR0_REG:
+ return "LTR0";
+ case IMX31_CCM_LTR1_REG:
+ return "LTR1";
+ case IMX31_CCM_LTR2_REG:
+ return "LTR2";
+ case IMX31_CCM_LTR3_REG:
+ return "LTR3";
+ case IMX31_CCM_LTBR0_REG:
+ return "LTBR0";
+ case IMX31_CCM_LTBR1_REG:
+ return "LTBR1";
+ case IMX31_CCM_PMCR0_REG:
+ return "PMCR0";
+ case IMX31_CCM_PMCR1_REG:
+ return "PMCR1";
+ case IMX31_CCM_PDR2_REG:
+ return "PDR2";
+ default:
+ sprintf(unknown, "[%d ?]", reg);
+ return unknown;
+ }
+}
+
+static const VMStateDescription vmstate_imx31_ccm = {
+ .name = TYPE_IMX31_CCM,
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(reg, IMX31CCMState, IMX31_CCM_MAX_REG),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static uint32_t imx31_ccm_get_pll_ref_clk(IMXCCMState *dev)
+{
+ uint32_t freq = 0;
+ IMX31CCMState *s = IMX31_CCM(dev);
+
+ if ((s->reg[IMX31_CCM_CCMR_REG] & CCMR_PRCS) == 2) {
+ if (s->reg[IMX31_CCM_CCMR_REG] & CCMR_FPME) {
+ freq = CKIL_FREQ;
+ if (s->reg[IMX31_CCM_CCMR_REG] & CCMR_FPMF) {
+ freq *= 1024;
+ }
+ }
+ } else {
+ freq = CKIH_FREQ;
+ }
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx31_ccm_get_mpll_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+ IMX31CCMState *s = IMX31_CCM(dev);
+
+ freq = imx_ccm_calc_pll(s->reg[IMX31_CCM_MPCTL_REG],
+ imx31_ccm_get_pll_ref_clk(dev));
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx31_ccm_get_mcu_main_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+ IMX31CCMState *s = IMX31_CCM(dev);
+
+ if ((s->reg[IMX31_CCM_CCMR_REG] & CCMR_MDS) ||
+ !(s->reg[IMX31_CCM_CCMR_REG] & CCMR_MPE)) {
+ freq = imx31_ccm_get_pll_ref_clk(dev);
+ } else {
+ freq = imx31_ccm_get_mpll_clk(dev);
+ }
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx31_ccm_get_hclk_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+ IMX31CCMState *s = IMX31_CCM(dev);
+
+ freq = imx31_ccm_get_mcu_main_clk(dev)
+ / (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], MAX));
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx31_ccm_get_ipg_clk(IMXCCMState *dev)
+{
+ uint32_t freq;
+ IMX31CCMState *s = IMX31_CCM(dev);
+
+ freq = imx31_ccm_get_hclk_clk(dev)
+ / (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], IPG));
+
+ DPRINTF("freq = %d\n", freq);
+
+ return freq;
+}
+
+static uint32_t imx31_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
+{
+ uint32_t freq = 0;
+
+ switch (clock) {
+ case CLK_NONE:
+ break;
+ case CLK_IPG:
+ case CLK_IPG_HIGH:
+ freq = imx31_ccm_get_ipg_clk(dev);
+ break;
+ case CLK_32k:
+ freq = CKIL_FREQ;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n",
+ TYPE_IMX31_CCM, __func__, clock);
+ break;
+ }
+
+ DPRINTF("Clock = %d) = %d\n", clock, freq);
+
+ return freq;
+}
+
+static void imx31_ccm_reset(DeviceState *dev)
+{
+ IMX31CCMState *s = IMX31_CCM(dev);
+
+ DPRINTF("()\n");
+
+ memset(s->reg, 0, sizeof(uint32_t) * IMX31_CCM_MAX_REG);
+
+ s->reg[IMX31_CCM_CCMR_REG] = 0x074b0b7d;
+ s->reg[IMX31_CCM_PDR0_REG] = 0xff870b48;
+ s->reg[IMX31_CCM_PDR1_REG] = 0x49fcfe7f;
+ s->reg[IMX31_CCM_RCSR_REG] = 0x007f0000;
+ s->reg[IMX31_CCM_MPCTL_REG] = 0x04001800;
+ s->reg[IMX31_CCM_UPCTL_REG] = 0x04051c03;
+ s->reg[IMX31_CCM_SPCTL_REG] = 0x04043001;
+ s->reg[IMX31_CCM_COSR_REG] = 0x00000280;
+ s->reg[IMX31_CCM_CGR0_REG] = 0xffffffff;
+ s->reg[IMX31_CCM_CGR1_REG] = 0xffffffff;
+ s->reg[IMX31_CCM_CGR2_REG] = 0xffffffff;
+ s->reg[IMX31_CCM_WIMR_REG] = 0xffffffff;
+ s->reg[IMX31_CCM_LTR1_REG] = 0x00004040;
+ s->reg[IMX31_CCM_PMCR0_REG] = 0x80209828;
+ s->reg[IMX31_CCM_PMCR1_REG] = 0x00aa0000;
+ s->reg[IMX31_CCM_PDR2_REG] = 0x00000285;
+}
+
+static uint64_t imx31_ccm_read(void *opaque, hwaddr offset, unsigned size)
+{
+ uint32_t value = 0;
+ IMX31CCMState *s = (IMX31CCMState *)opaque;
+
+ if ((offset >> 2) < IMX31_CCM_MAX_REG) {
+ value = s->reg[offset >> 2];
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX31_CCM, __func__, offset);
+ }
+
+ DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx31_ccm_reg_name(offset >> 2),
+ value);
+
+ return (uint64_t)value;
+}
+
+static void imx31_ccm_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ IMX31CCMState *s = (IMX31CCMState *)opaque;
+
+ DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx31_ccm_reg_name(offset >> 2),
+ (uint32_t)value);
+
+ switch (offset >> 2) {
+ case IMX31_CCM_CCMR_REG:
+ s->reg[IMX31_CCM_CCMR_REG] = CCMR_FPMF | (value & 0x3b6fdfff);
+ break;
+ case IMX31_CCM_PDR0_REG:
+ s->reg[IMX31_CCM_PDR0_REG] = value & 0xff9f3fff;
+ break;
+ case IMX31_CCM_PDR1_REG:
+ s->reg[IMX31_CCM_PDR1_REG] = value;
+ break;
+ case IMX31_CCM_MPCTL_REG:
+ s->reg[IMX31_CCM_MPCTL_REG] = value & 0xbfff3fff;
+ break;
+ case IMX31_CCM_SPCTL_REG:
+ s->reg[IMX31_CCM_SPCTL_REG] = value & 0xbfff3fff;
+ break;
+ case IMX31_CCM_CGR0_REG:
+ s->reg[IMX31_CCM_CGR0_REG] = value;
+ break;
+ case IMX31_CCM_CGR1_REG:
+ s->reg[IMX31_CCM_CGR1_REG] = value;
+ break;
+ case IMX31_CCM_CGR2_REG:
+ s->reg[IMX31_CCM_CGR2_REG] = value;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX31_CCM, __func__, offset);
+ break;
+ }
+}
+
+static const struct MemoryRegionOps imx31_ccm_ops = {
+ .read = imx31_ccm_read,
+ .write = imx31_ccm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ /*
+ * Our device would not work correctly if the guest was doing
+ * unaligned access. This might not be a limitation on the real
+ * device but in practice there is no reason for a guest to access
+ * this device unaligned.
+ */
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false,
+ },
+
+};
+
+static void imx31_ccm_init(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+ SysBusDevice *sd = SYS_BUS_DEVICE(obj);
+ IMX31CCMState *s = IMX31_CCM(obj);
+
+ memory_region_init_io(&s->iomem, OBJECT(dev), &imx31_ccm_ops, s,
+ TYPE_IMX31_CCM, 0x1000);
+ sysbus_init_mmio(sd, &s->iomem);
+}
+
+static void imx31_ccm_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ IMXCCMClass *ccm = IMX_CCM_CLASS(klass);
+
+ dc->reset = imx31_ccm_reset;
+ dc->vmsd = &vmstate_imx31_ccm;
+ dc->desc = "i.MX31 Clock Control Module";
+
+ ccm->get_clock_frequency = imx31_ccm_get_clock_frequency;
+}
+
+static const TypeInfo imx31_ccm_info = {
+ .name = TYPE_IMX31_CCM,
+ .parent = TYPE_IMX_CCM,
+ .instance_size = sizeof(IMX31CCMState),
+ .instance_init = imx31_ccm_init,
+ .class_init = imx31_ccm_class_init,
+};
+
+static void imx31_ccm_register_types(void)
+{
+ type_register_static(&imx31_ccm_info);
+}
+
+type_init(imx31_ccm_register_types)
diff --git a/qemu/hw/misc/imx6_ccm.c b/qemu/hw/misc/imx6_ccm.c
new file mode 100644
index 000000000..4e1d49da6
--- /dev/null
+++ b/qemu/hw/misc/imx6_ccm.c
@@ -0,0 +1,774 @@
+/*
+ * IMX6 Clock Control Module
+ *
+ * Copyright (c) 2015 Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * To get the timer frequencies right, we need to emulate at least part of
+ * the CCM.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/misc/imx6_ccm.h"
+
+#ifndef DEBUG_IMX6_CCM
+#define DEBUG_IMX6_CCM 0
+#endif
+
+#define DPRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX6_CCM) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX6_CCM, \
+ __func__, ##args); \
+ } \
+ } while (0)
+
+static char const *imx6_ccm_reg_name(uint32_t reg)
+{
+ static char unknown[20];
+
+ switch (reg) {
+ case CCM_CCR:
+ return "CCR";
+ case CCM_CCDR:
+ return "CCDR";
+ case CCM_CSR:
+ return "CSR";
+ case CCM_CCSR:
+ return "CCSR";
+ case CCM_CACRR:
+ return "CACRR";
+ case CCM_CBCDR:
+ return "CBCDR";
+ case CCM_CBCMR:
+ return "CBCMR";
+ case CCM_CSCMR1:
+ return "CSCMR1";
+ case CCM_CSCMR2:
+ return "CSCMR2";
+ case CCM_CSCDR1:
+ return "CSCDR1";
+ case CCM_CS1CDR:
+ return "CS1CDR";
+ case CCM_CS2CDR:
+ return "CS2CDR";
+ case CCM_CDCDR:
+ return "CDCDR";
+ case CCM_CHSCCDR:
+ return "CHSCCDR";
+ case CCM_CSCDR2:
+ return "CSCDR2";
+ case CCM_CSCDR3:
+ return "CSCDR3";
+ case CCM_CDHIPR:
+ return "CDHIPR";
+ case CCM_CTOR:
+ return "CTOR";
+ case CCM_CLPCR:
+ return "CLPCR";
+ case CCM_CISR:
+ return "CISR";
+ case CCM_CIMR:
+ return "CIMR";
+ case CCM_CCOSR:
+ return "CCOSR";
+ case CCM_CGPR:
+ return "CGPR";
+ case CCM_CCGR0:
+ return "CCGR0";
+ case CCM_CCGR1:
+ return "CCGR1";
+ case CCM_CCGR2:
+ return "CCGR2";
+ case CCM_CCGR3:
+ return "CCGR3";
+ case CCM_CCGR4:
+ return "CCGR4";
+ case CCM_CCGR5:
+ return "CCGR5";
+ case CCM_CCGR6:
+ return "CCGR6";
+ case CCM_CMEOR:
+ return "CMEOR";
+ default:
+ sprintf(unknown, "%d ?", reg);
+ return unknown;
+ }
+}
+
+static char const *imx6_analog_reg_name(uint32_t reg)
+{
+ static char unknown[20];
+
+ switch (reg) {
+ case CCM_ANALOG_PLL_ARM:
+ return "PLL_ARM";
+ case CCM_ANALOG_PLL_ARM_SET:
+ return "PLL_ARM_SET";
+ case CCM_ANALOG_PLL_ARM_CLR:
+ return "PLL_ARM_CLR";
+ case CCM_ANALOG_PLL_ARM_TOG:
+ return "PLL_ARM_TOG";
+ case CCM_ANALOG_PLL_USB1:
+ return "PLL_USB1";
+ case CCM_ANALOG_PLL_USB1_SET:
+ return "PLL_USB1_SET";
+ case CCM_ANALOG_PLL_USB1_CLR:
+ return "PLL_USB1_CLR";
+ case CCM_ANALOG_PLL_USB1_TOG:
+ return "PLL_USB1_TOG";
+ case CCM_ANALOG_PLL_USB2:
+ return "PLL_USB2";
+ case CCM_ANALOG_PLL_USB2_SET:
+ return "PLL_USB2_SET";
+ case CCM_ANALOG_PLL_USB2_CLR:
+ return "PLL_USB2_CLR";
+ case CCM_ANALOG_PLL_USB2_TOG:
+ return "PLL_USB2_TOG";
+ case CCM_ANALOG_PLL_SYS:
+ return "PLL_SYS";
+ case CCM_ANALOG_PLL_SYS_SET:
+ return "PLL_SYS_SET";
+ case CCM_ANALOG_PLL_SYS_CLR:
+ return "PLL_SYS_CLR";
+ case CCM_ANALOG_PLL_SYS_TOG:
+ return "PLL_SYS_TOG";
+ case CCM_ANALOG_PLL_SYS_SS:
+ return "PLL_SYS_SS";
+ case CCM_ANALOG_PLL_SYS_NUM:
+ return "PLL_SYS_NUM";
+ case CCM_ANALOG_PLL_SYS_DENOM:
+ return "PLL_SYS_DENOM";
+ case CCM_ANALOG_PLL_AUDIO:
+ return "PLL_AUDIO";
+ case CCM_ANALOG_PLL_AUDIO_SET:
+ return "PLL_AUDIO_SET";
+ case CCM_ANALOG_PLL_AUDIO_CLR:
+ return "PLL_AUDIO_CLR";
+ case CCM_ANALOG_PLL_AUDIO_TOG:
+ return "PLL_AUDIO_TOG";
+ case CCM_ANALOG_PLL_AUDIO_NUM:
+ return "PLL_AUDIO_NUM";
+ case CCM_ANALOG_PLL_AUDIO_DENOM:
+ return "PLL_AUDIO_DENOM";
+ case CCM_ANALOG_PLL_VIDEO:
+ return "PLL_VIDEO";
+ case CCM_ANALOG_PLL_VIDEO_SET:
+ return "PLL_VIDEO_SET";
+ case CCM_ANALOG_PLL_VIDEO_CLR:
+ return "PLL_VIDEO_CLR";
+ case CCM_ANALOG_PLL_VIDEO_TOG:
+ return "PLL_VIDEO_TOG";
+ case CCM_ANALOG_PLL_VIDEO_NUM:
+ return "PLL_VIDEO_NUM";
+ case CCM_ANALOG_PLL_VIDEO_DENOM:
+ return "PLL_VIDEO_DENOM";
+ case CCM_ANALOG_PLL_MLB:
+ return "PLL_MLB";
+ case CCM_ANALOG_PLL_MLB_SET:
+ return "PLL_MLB_SET";
+ case CCM_ANALOG_PLL_MLB_CLR:
+ return "PLL_MLB_CLR";
+ case CCM_ANALOG_PLL_MLB_TOG:
+ return "PLL_MLB_TOG";
+ case CCM_ANALOG_PLL_ENET:
+ return "PLL_ENET";
+ case CCM_ANALOG_PLL_ENET_SET:
+ return "PLL_ENET_SET";
+ case CCM_ANALOG_PLL_ENET_CLR:
+ return "PLL_ENET_CLR";
+ case CCM_ANALOG_PLL_ENET_TOG:
+ return "PLL_ENET_TOG";
+ case CCM_ANALOG_PFD_480:
+ return "PFD_480";
+ case CCM_ANALOG_PFD_480_SET:
+ return "PFD_480_SET";
+ case CCM_ANALOG_PFD_480_CLR:
+ return "PFD_480_CLR";
+ case CCM_ANALOG_PFD_480_TOG:
+ return "PFD_480_TOG";
+ case CCM_ANALOG_PFD_528:
+ return "PFD_528";
+ case CCM_ANALOG_PFD_528_SET:
+ return "PFD_528_SET";
+ case CCM_ANALOG_PFD_528_CLR:
+ return "PFD_528_CLR";
+ case CCM_ANALOG_PFD_528_TOG:
+ return "PFD_528_TOG";
+ case CCM_ANALOG_MISC0:
+ return "MISC0";
+ case CCM_ANALOG_MISC0_SET:
+ return "MISC0_SET";
+ case CCM_ANALOG_MISC0_CLR:
+ return "MISC0_CLR";
+ case CCM_ANALOG_MISC0_TOG:
+ return "MISC0_TOG";
+ case CCM_ANALOG_MISC2:
+ return "MISC2";
+ case CCM_ANALOG_MISC2_SET:
+ return "MISC2_SET";
+ case CCM_ANALOG_MISC2_CLR:
+ return "MISC2_CLR";
+ case CCM_ANALOG_MISC2_TOG:
+ return "MISC2_TOG";
+ case PMU_REG_1P1:
+ return "PMU_REG_1P1";
+ case PMU_REG_3P0:
+ return "PMU_REG_3P0";
+ case PMU_REG_2P5:
+ return "PMU_REG_2P5";
+ case PMU_REG_CORE:
+ return "PMU_REG_CORE";
+ case PMU_MISC1:
+ return "PMU_MISC1";
+ case PMU_MISC1_SET:
+ return "PMU_MISC1_SET";
+ case PMU_MISC1_CLR:
+ return "PMU_MISC1_CLR";
+ case PMU_MISC1_TOG:
+ return "PMU_MISC1_TOG";
+ case USB_ANALOG_DIGPROG:
+ return "USB_ANALOG_DIGPROG";
+ default:
+ sprintf(unknown, "%d ?", reg);
+ return unknown;
+ }
+}
+
+#define CKIH_FREQ 24000000 /* 24MHz crystal input */
+
+static const VMStateDescription vmstate_imx6_ccm = {
+ .name = TYPE_IMX6_CCM,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(ccm, IMX6CCMState, CCM_MAX),
+ VMSTATE_UINT32_ARRAY(analog, IMX6CCMState, CCM_ANALOG_MAX),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static uint64_t imx6_analog_get_pll2_clk(IMX6CCMState *dev)
+{
+ uint64_t freq = 24000000;
+
+ if (EXTRACT(dev->analog[CCM_ANALOG_PLL_SYS], DIV_SELECT)) {
+ freq *= 22;
+ } else {
+ freq *= 20;
+ }
+
+ DPRINTF("freq = %d\n", (uint32_t)freq);
+
+ return freq;
+}
+
+static uint64_t imx6_analog_get_pll2_pfd0_clk(IMX6CCMState *dev)
+{
+ uint64_t freq = 0;
+
+ freq = imx6_analog_get_pll2_clk(dev) * 18
+ / EXTRACT(dev->analog[CCM_ANALOG_PFD_528], PFD0_FRAC);
+
+ DPRINTF("freq = %d\n", (uint32_t)freq);
+
+ return freq;
+}
+
+static uint64_t imx6_analog_get_pll2_pfd2_clk(IMX6CCMState *dev)
+{
+ uint64_t freq = 0;
+
+ freq = imx6_analog_get_pll2_clk(dev) * 18
+ / EXTRACT(dev->analog[CCM_ANALOG_PFD_528], PFD2_FRAC);
+
+ DPRINTF("freq = %d\n", (uint32_t)freq);
+
+ return freq;
+}
+
+static uint64_t imx6_analog_get_periph_clk(IMX6CCMState *dev)
+{
+ uint64_t freq = 0;
+
+ switch (EXTRACT(dev->ccm[CCM_CBCMR], PRE_PERIPH_CLK_SEL)) {
+ case 0:
+ freq = imx6_analog_get_pll2_clk(dev);
+ break;
+ case 1:
+ freq = imx6_analog_get_pll2_pfd2_clk(dev);
+ break;
+ case 2:
+ freq = imx6_analog_get_pll2_pfd0_clk(dev);
+ break;
+ case 3:
+ freq = imx6_analog_get_pll2_pfd2_clk(dev) / 2;
+ break;
+ default:
+ /* We should never get there */
+ g_assert_not_reached();
+ break;
+ }
+
+ DPRINTF("freq = %d\n", (uint32_t)freq);
+
+ return freq;
+}
+
+static uint64_t imx6_ccm_get_ahb_clk(IMX6CCMState *dev)
+{
+ uint64_t freq = 0;
+
+ freq = imx6_analog_get_periph_clk(dev)
+ / (1 + EXTRACT(dev->ccm[CCM_CBCDR], AHB_PODF));
+
+ DPRINTF("freq = %d\n", (uint32_t)freq);
+
+ return freq;
+}
+
+static uint64_t imx6_ccm_get_ipg_clk(IMX6CCMState *dev)
+{
+ uint64_t freq = 0;
+
+ freq = imx6_ccm_get_ahb_clk(dev)
+ / (1 + EXTRACT(dev->ccm[CCM_CBCDR], IPG_PODF));;
+
+ DPRINTF("freq = %d\n", (uint32_t)freq);
+
+ return freq;
+}
+
+static uint64_t imx6_ccm_get_per_clk(IMX6CCMState *dev)
+{
+ uint64_t freq = 0;
+
+ freq = imx6_ccm_get_ipg_clk(dev)
+ / (1 + EXTRACT(dev->ccm[CCM_CSCMR1], PERCLK_PODF));
+
+ DPRINTF("freq = %d\n", (uint32_t)freq);
+
+ return freq;
+}
+
+static uint32_t imx6_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
+{
+ uint32_t freq = 0;
+ IMX6CCMState *s = IMX6_CCM(dev);
+
+ switch (clock) {
+ case CLK_NONE:
+ break;
+ case CLK_IPG:
+ freq = imx6_ccm_get_ipg_clk(s);
+ break;
+ case CLK_IPG_HIGH:
+ freq = imx6_ccm_get_per_clk(s);
+ break;
+ case CLK_32k:
+ freq = CKIL_FREQ;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n",
+ TYPE_IMX6_CCM, __func__, clock);
+ break;
+ }
+
+ DPRINTF("Clock = %d) = %d\n", clock, freq);
+
+ return freq;
+}
+
+static void imx6_ccm_reset(DeviceState *dev)
+{
+ IMX6CCMState *s = IMX6_CCM(dev);
+
+ DPRINTF("\n");
+
+ s->ccm[CCM_CCR] = 0x040116FF;
+ s->ccm[CCM_CCDR] = 0x00000000;
+ s->ccm[CCM_CSR] = 0x00000010;
+ s->ccm[CCM_CCSR] = 0x00000100;
+ s->ccm[CCM_CACRR] = 0x00000000;
+ s->ccm[CCM_CBCDR] = 0x00018D40;
+ s->ccm[CCM_CBCMR] = 0x00022324;
+ s->ccm[CCM_CSCMR1] = 0x00F00000;
+ s->ccm[CCM_CSCMR2] = 0x02B92F06;
+ s->ccm[CCM_CSCDR1] = 0x00490B00;
+ s->ccm[CCM_CS1CDR] = 0x0EC102C1;
+ s->ccm[CCM_CS2CDR] = 0x000736C1;
+ s->ccm[CCM_CDCDR] = 0x33F71F92;
+ s->ccm[CCM_CHSCCDR] = 0x0002A150;
+ s->ccm[CCM_CSCDR2] = 0x0002A150;
+ s->ccm[CCM_CSCDR3] = 0x00014841;
+ s->ccm[CCM_CDHIPR] = 0x00000000;
+ s->ccm[CCM_CTOR] = 0x00000000;
+ s->ccm[CCM_CLPCR] = 0x00000079;
+ s->ccm[CCM_CISR] = 0x00000000;
+ s->ccm[CCM_CIMR] = 0xFFFFFFFF;
+ s->ccm[CCM_CCOSR] = 0x000A0001;
+ s->ccm[CCM_CGPR] = 0x0000FE62;
+ s->ccm[CCM_CCGR0] = 0xFFFFFFFF;
+ s->ccm[CCM_CCGR1] = 0xFFFFFFFF;
+ s->ccm[CCM_CCGR2] = 0xFC3FFFFF;
+ s->ccm[CCM_CCGR3] = 0xFFFFFFFF;
+ s->ccm[CCM_CCGR4] = 0xFFFFFFFF;
+ s->ccm[CCM_CCGR5] = 0xFFFFFFFF;
+ s->ccm[CCM_CCGR6] = 0xFFFFFFFF;
+ s->ccm[CCM_CMEOR] = 0xFFFFFFFF;
+
+ s->analog[CCM_ANALOG_PLL_ARM] = 0x00013042;
+ s->analog[CCM_ANALOG_PLL_USB1] = 0x00012000;
+ s->analog[CCM_ANALOG_PLL_USB2] = 0x00012000;
+ s->analog[CCM_ANALOG_PLL_SYS] = 0x00013001;
+ s->analog[CCM_ANALOG_PLL_SYS_SS] = 0x00000000;
+ s->analog[CCM_ANALOG_PLL_SYS_NUM] = 0x00000000;
+ s->analog[CCM_ANALOG_PLL_SYS_DENOM] = 0x00000012;
+ s->analog[CCM_ANALOG_PLL_AUDIO] = 0x00011006;
+ s->analog[CCM_ANALOG_PLL_AUDIO_NUM] = 0x05F5E100;
+ s->analog[CCM_ANALOG_PLL_AUDIO_DENOM] = 0x2964619C;
+ s->analog[CCM_ANALOG_PLL_VIDEO] = 0x0001100C;
+ s->analog[CCM_ANALOG_PLL_VIDEO_NUM] = 0x05F5E100;
+ s->analog[CCM_ANALOG_PLL_VIDEO_DENOM] = 0x10A24447;
+ s->analog[CCM_ANALOG_PLL_MLB] = 0x00010000;
+ s->analog[CCM_ANALOG_PLL_ENET] = 0x00011001;
+ s->analog[CCM_ANALOG_PFD_480] = 0x1311100C;
+ s->analog[CCM_ANALOG_PFD_528] = 0x1018101B;
+
+ s->analog[PMU_REG_1P1] = 0x00001073;
+ s->analog[PMU_REG_3P0] = 0x00000F74;
+ s->analog[PMU_REG_2P5] = 0x00005071;
+ s->analog[PMU_REG_CORE] = 0x00402010;
+ s->analog[PMU_MISC0] = 0x04000000;
+ s->analog[PMU_MISC1] = 0x00000000;
+ s->analog[PMU_MISC2] = 0x00272727;
+
+ s->analog[USB_ANALOG_USB1_VBUS_DETECT] = 0x00000004;
+ s->analog[USB_ANALOG_USB1_CHRG_DETECT] = 0x00000000;
+ s->analog[USB_ANALOG_USB1_VBUS_DETECT_STAT] = 0x00000000;
+ s->analog[USB_ANALOG_USB1_CHRG_DETECT_STAT] = 0x00000000;
+ s->analog[USB_ANALOG_USB1_MISC] = 0x00000002;
+ s->analog[USB_ANALOG_USB2_VBUS_DETECT] = 0x00000004;
+ s->analog[USB_ANALOG_USB2_CHRG_DETECT] = 0x00000000;
+ s->analog[USB_ANALOG_USB2_MISC] = 0x00000002;
+ s->analog[USB_ANALOG_DIGPROG] = 0x00000000;
+
+ /* all PLLs need to be locked */
+ s->analog[CCM_ANALOG_PLL_ARM] |= CCM_ANALOG_PLL_LOCK;
+ s->analog[CCM_ANALOG_PLL_USB1] |= CCM_ANALOG_PLL_LOCK;
+ s->analog[CCM_ANALOG_PLL_USB2] |= CCM_ANALOG_PLL_LOCK;
+ s->analog[CCM_ANALOG_PLL_SYS] |= CCM_ANALOG_PLL_LOCK;
+ s->analog[CCM_ANALOG_PLL_AUDIO] |= CCM_ANALOG_PLL_LOCK;
+ s->analog[CCM_ANALOG_PLL_VIDEO] |= CCM_ANALOG_PLL_LOCK;
+ s->analog[CCM_ANALOG_PLL_MLB] |= CCM_ANALOG_PLL_LOCK;
+ s->analog[CCM_ANALOG_PLL_ENET] |= CCM_ANALOG_PLL_LOCK;
+}
+
+static uint64_t imx6_ccm_read(void *opaque, hwaddr offset, unsigned size)
+{
+ uint32_t value = 0;
+ uint32_t index = offset >> 2;
+ IMX6CCMState *s = (IMX6CCMState *)opaque;
+
+ value = s->ccm[index];
+
+ DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_ccm_reg_name(index), value);
+
+ return (uint64_t)value;
+}
+
+static void imx6_ccm_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ uint32_t index = offset >> 2;
+ IMX6CCMState *s = (IMX6CCMState *)opaque;
+
+ DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_ccm_reg_name(index),
+ (uint32_t)value);
+
+ /*
+ * We will do a better implementation later. In particular some bits
+ * cannot be written to.
+ */
+ s->ccm[index] = (uint32_t)value;
+}
+
+static uint64_t imx6_analog_read(void *opaque, hwaddr offset, unsigned size)
+{
+ uint32_t value;
+ uint32_t index = offset >> 2;
+ IMX6CCMState *s = (IMX6CCMState *)opaque;
+
+ switch (index) {
+ case CCM_ANALOG_PLL_ARM_SET:
+ case CCM_ANALOG_PLL_USB1_SET:
+ case CCM_ANALOG_PLL_USB2_SET:
+ case CCM_ANALOG_PLL_SYS_SET:
+ case CCM_ANALOG_PLL_AUDIO_SET:
+ case CCM_ANALOG_PLL_VIDEO_SET:
+ case CCM_ANALOG_PLL_MLB_SET:
+ case CCM_ANALOG_PLL_ENET_SET:
+ case CCM_ANALOG_PFD_480_SET:
+ case CCM_ANALOG_PFD_528_SET:
+ case CCM_ANALOG_MISC0_SET:
+ case PMU_MISC1_SET:
+ case CCM_ANALOG_MISC2_SET:
+ case USB_ANALOG_USB1_VBUS_DETECT_SET:
+ case USB_ANALOG_USB1_CHRG_DETECT_SET:
+ case USB_ANALOG_USB1_MISC_SET:
+ case USB_ANALOG_USB2_VBUS_DETECT_SET:
+ case USB_ANALOG_USB2_CHRG_DETECT_SET:
+ case USB_ANALOG_USB2_MISC_SET:
+ /*
+ * All REG_NAME_SET register access are in fact targeting the
+ * the REG_NAME register.
+ */
+ value = s->analog[index - 1];
+ break;
+ case CCM_ANALOG_PLL_ARM_CLR:
+ case CCM_ANALOG_PLL_USB1_CLR:
+ case CCM_ANALOG_PLL_USB2_CLR:
+ case CCM_ANALOG_PLL_SYS_CLR:
+ case CCM_ANALOG_PLL_AUDIO_CLR:
+ case CCM_ANALOG_PLL_VIDEO_CLR:
+ case CCM_ANALOG_PLL_MLB_CLR:
+ case CCM_ANALOG_PLL_ENET_CLR:
+ case CCM_ANALOG_PFD_480_CLR:
+ case CCM_ANALOG_PFD_528_CLR:
+ case CCM_ANALOG_MISC0_CLR:
+ case PMU_MISC1_CLR:
+ case CCM_ANALOG_MISC2_CLR:
+ case USB_ANALOG_USB1_VBUS_DETECT_CLR:
+ case USB_ANALOG_USB1_CHRG_DETECT_CLR:
+ case USB_ANALOG_USB1_MISC_CLR:
+ case USB_ANALOG_USB2_VBUS_DETECT_CLR:
+ case USB_ANALOG_USB2_CHRG_DETECT_CLR:
+ case USB_ANALOG_USB2_MISC_CLR:
+ /*
+ * All REG_NAME_CLR register access are in fact targeting the
+ * the REG_NAME register.
+ */
+ value = s->analog[index - 2];
+ break;
+ case CCM_ANALOG_PLL_ARM_TOG:
+ case CCM_ANALOG_PLL_USB1_TOG:
+ case CCM_ANALOG_PLL_USB2_TOG:
+ case CCM_ANALOG_PLL_SYS_TOG:
+ case CCM_ANALOG_PLL_AUDIO_TOG:
+ case CCM_ANALOG_PLL_VIDEO_TOG:
+ case CCM_ANALOG_PLL_MLB_TOG:
+ case CCM_ANALOG_PLL_ENET_TOG:
+ case CCM_ANALOG_PFD_480_TOG:
+ case CCM_ANALOG_PFD_528_TOG:
+ case CCM_ANALOG_MISC0_TOG:
+ case PMU_MISC1_TOG:
+ case CCM_ANALOG_MISC2_TOG:
+ case USB_ANALOG_USB1_VBUS_DETECT_TOG:
+ case USB_ANALOG_USB1_CHRG_DETECT_TOG:
+ case USB_ANALOG_USB1_MISC_TOG:
+ case USB_ANALOG_USB2_VBUS_DETECT_TOG:
+ case USB_ANALOG_USB2_CHRG_DETECT_TOG:
+ case USB_ANALOG_USB2_MISC_TOG:
+ /*
+ * All REG_NAME_TOG register access are in fact targeting the
+ * the REG_NAME register.
+ */
+ value = s->analog[index - 3];
+ break;
+ default:
+ value = s->analog[index];
+ break;
+ }
+
+ DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_analog_reg_name(index), value);
+
+ return (uint64_t)value;
+}
+
+static void imx6_analog_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ uint32_t index = offset >> 2;
+ IMX6CCMState *s = (IMX6CCMState *)opaque;
+
+ DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_analog_reg_name(index),
+ (uint32_t)value);
+
+ switch (index) {
+ case CCM_ANALOG_PLL_ARM_SET:
+ case CCM_ANALOG_PLL_USB1_SET:
+ case CCM_ANALOG_PLL_USB2_SET:
+ case CCM_ANALOG_PLL_SYS_SET:
+ case CCM_ANALOG_PLL_AUDIO_SET:
+ case CCM_ANALOG_PLL_VIDEO_SET:
+ case CCM_ANALOG_PLL_MLB_SET:
+ case CCM_ANALOG_PLL_ENET_SET:
+ case CCM_ANALOG_PFD_480_SET:
+ case CCM_ANALOG_PFD_528_SET:
+ case CCM_ANALOG_MISC0_SET:
+ case PMU_MISC1_SET:
+ case CCM_ANALOG_MISC2_SET:
+ case USB_ANALOG_USB1_VBUS_DETECT_SET:
+ case USB_ANALOG_USB1_CHRG_DETECT_SET:
+ case USB_ANALOG_USB1_MISC_SET:
+ case USB_ANALOG_USB2_VBUS_DETECT_SET:
+ case USB_ANALOG_USB2_CHRG_DETECT_SET:
+ case USB_ANALOG_USB2_MISC_SET:
+ /*
+ * All REG_NAME_SET register access are in fact targeting the
+ * the REG_NAME register. So we change the value of the
+ * REG_NAME register, setting bits passed in the value.
+ */
+ s->analog[index - 1] |= value;
+ break;
+ case CCM_ANALOG_PLL_ARM_CLR:
+ case CCM_ANALOG_PLL_USB1_CLR:
+ case CCM_ANALOG_PLL_USB2_CLR:
+ case CCM_ANALOG_PLL_SYS_CLR:
+ case CCM_ANALOG_PLL_AUDIO_CLR:
+ case CCM_ANALOG_PLL_VIDEO_CLR:
+ case CCM_ANALOG_PLL_MLB_CLR:
+ case CCM_ANALOG_PLL_ENET_CLR:
+ case CCM_ANALOG_PFD_480_CLR:
+ case CCM_ANALOG_PFD_528_CLR:
+ case CCM_ANALOG_MISC0_CLR:
+ case PMU_MISC1_CLR:
+ case CCM_ANALOG_MISC2_CLR:
+ case USB_ANALOG_USB1_VBUS_DETECT_CLR:
+ case USB_ANALOG_USB1_CHRG_DETECT_CLR:
+ case USB_ANALOG_USB1_MISC_CLR:
+ case USB_ANALOG_USB2_VBUS_DETECT_CLR:
+ case USB_ANALOG_USB2_CHRG_DETECT_CLR:
+ case USB_ANALOG_USB2_MISC_CLR:
+ /*
+ * All REG_NAME_CLR register access are in fact targeting the
+ * the REG_NAME register. So we change the value of the
+ * REG_NAME register, unsetting bits passed in the value.
+ */
+ s->analog[index - 2] &= ~value;
+ break;
+ case CCM_ANALOG_PLL_ARM_TOG:
+ case CCM_ANALOG_PLL_USB1_TOG:
+ case CCM_ANALOG_PLL_USB2_TOG:
+ case CCM_ANALOG_PLL_SYS_TOG:
+ case CCM_ANALOG_PLL_AUDIO_TOG:
+ case CCM_ANALOG_PLL_VIDEO_TOG:
+ case CCM_ANALOG_PLL_MLB_TOG:
+ case CCM_ANALOG_PLL_ENET_TOG:
+ case CCM_ANALOG_PFD_480_TOG:
+ case CCM_ANALOG_PFD_528_TOG:
+ case CCM_ANALOG_MISC0_TOG:
+ case PMU_MISC1_TOG:
+ case CCM_ANALOG_MISC2_TOG:
+ case USB_ANALOG_USB1_VBUS_DETECT_TOG:
+ case USB_ANALOG_USB1_CHRG_DETECT_TOG:
+ case USB_ANALOG_USB1_MISC_TOG:
+ case USB_ANALOG_USB2_VBUS_DETECT_TOG:
+ case USB_ANALOG_USB2_CHRG_DETECT_TOG:
+ case USB_ANALOG_USB2_MISC_TOG:
+ /*
+ * All REG_NAME_TOG register access are in fact targeting the
+ * the REG_NAME register. So we change the value of the
+ * REG_NAME register, toggling bits passed in the value.
+ */
+ s->analog[index - 3] ^= value;
+ break;
+ default:
+ /*
+ * We will do a better implementation later. In particular some bits
+ * cannot be written to.
+ */
+ s->analog[index] = value;
+ break;
+ }
+}
+
+static const struct MemoryRegionOps imx6_ccm_ops = {
+ .read = imx6_ccm_read,
+ .write = imx6_ccm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ /*
+ * Our device would not work correctly if the guest was doing
+ * unaligned access. This might not be a limitation on the real
+ * device but in practice there is no reason for a guest to access
+ * this device unaligned.
+ */
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false,
+ },
+};
+
+static const struct MemoryRegionOps imx6_analog_ops = {
+ .read = imx6_analog_read,
+ .write = imx6_analog_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ /*
+ * Our device would not work correctly if the guest was doing
+ * unaligned access. This might not be a limitation on the real
+ * device but in practice there is no reason for a guest to access
+ * this device unaligned.
+ */
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false,
+ },
+};
+
+static void imx6_ccm_init(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+ SysBusDevice *sd = SYS_BUS_DEVICE(obj);
+ IMX6CCMState *s = IMX6_CCM(obj);
+
+ /* initialize a container for the all memory range */
+ memory_region_init(&s->container, OBJECT(dev), TYPE_IMX6_CCM, 0x5000);
+
+ /* We initialize an IO memory region for the CCM part */
+ memory_region_init_io(&s->ioccm, OBJECT(dev), &imx6_ccm_ops, s,
+ TYPE_IMX6_CCM ".ccm", CCM_MAX * sizeof(uint32_t));
+
+ /* Add the CCM as a subregion at offset 0 */
+ memory_region_add_subregion(&s->container, 0, &s->ioccm);
+
+ /* We initialize an IO memory region for the ANALOG part */
+ memory_region_init_io(&s->ioanalog, OBJECT(dev), &imx6_analog_ops, s,
+ TYPE_IMX6_CCM ".analog",
+ CCM_ANALOG_MAX * sizeof(uint32_t));
+
+ /* Add the ANALOG as a subregion at offset 0x4000 */
+ memory_region_add_subregion(&s->container, 0x4000, &s->ioanalog);
+
+ sysbus_init_mmio(sd, &s->container);
+}
+
+static void imx6_ccm_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ IMXCCMClass *ccm = IMX_CCM_CLASS(klass);
+
+ dc->reset = imx6_ccm_reset;
+ dc->vmsd = &vmstate_imx6_ccm;
+ dc->desc = "i.MX6 Clock Control Module";
+
+ ccm->get_clock_frequency = imx6_ccm_get_clock_frequency;
+}
+
+static const TypeInfo imx6_ccm_info = {
+ .name = TYPE_IMX6_CCM,
+ .parent = TYPE_IMX_CCM,
+ .instance_size = sizeof(IMX6CCMState),
+ .instance_init = imx6_ccm_init,
+ .class_init = imx6_ccm_class_init,
+};
+
+static void imx6_ccm_register_types(void)
+{
+ type_register_static(&imx6_ccm_info);
+}
+
+type_init(imx6_ccm_register_types)
diff --git a/qemu/hw/misc/imx_ccm.c b/qemu/hw/misc/imx_ccm.c
index 092028863..986d890ca 100644
--- a/qemu/hw/misc/imx_ccm.c
+++ b/qemu/hw/misc/imx_ccm.c
@@ -2,138 +2,51 @@
* IMX31 Clock Control Module
*
* Copyright (C) 2012 NICTA
+ * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
- * To get the timer frequencies right, we need to emulate at least part of
- * the CCM.
+ * This is an abstract base class used to get a common interface to
+ * retrieve the CCM frequencies from the various i.MX SOC.
*/
-#include "hw/hw.h"
-#include "hw/sysbus.h"
-#include "sysemu/sysemu.h"
-#include "hw/arm/imx.h"
+#include "qemu/osdep.h"
+#include "hw/misc/imx_ccm.h"
-#define CKIH_FREQ 26000000 /* 26MHz crystal input */
-#define CKIL_FREQ 32768 /* nominal 32khz clock */
-
-
-//#define DEBUG_CCM 1
-#ifdef DEBUG_CCM
-#define DPRINTF(fmt, args...) \
-do { printf("imx_ccm: " fmt , ##args); } while (0)
-#else
-#define DPRINTF(fmt, args...) do {} while (0)
+#ifndef DEBUG_IMX_CCM
+#define DEBUG_IMX_CCM 0
#endif
-static int imx_ccm_post_load(void *opaque, int version_id);
-
-#define TYPE_IMX_CCM "imx_ccm"
-#define IMX_CCM(obj) OBJECT_CHECK(IMXCCMState, (obj), TYPE_IMX_CCM)
-
-typedef struct IMXCCMState {
- SysBusDevice parent_obj;
-
- MemoryRegion iomem;
-
- uint32_t ccmr;
- uint32_t pdr0;
- uint32_t pdr1;
- uint32_t mpctl;
- uint32_t spctl;
- uint32_t cgr[3];
- uint32_t pmcr0;
- uint32_t pmcr1;
-
- /* Frequencies precalculated on register changes */
- uint32_t pll_refclk_freq;
- uint32_t mcu_clk_freq;
- uint32_t hsp_clk_freq;
- uint32_t ipg_clk_freq;
-} IMXCCMState;
-
-static const VMStateDescription vmstate_imx_ccm = {
- .name = "imx-ccm",
- .version_id = 1,
- .minimum_version_id = 1,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32(ccmr, IMXCCMState),
- VMSTATE_UINT32(pdr0, IMXCCMState),
- VMSTATE_UINT32(pdr1, IMXCCMState),
- VMSTATE_UINT32(mpctl, IMXCCMState),
- VMSTATE_UINT32(spctl, IMXCCMState),
- VMSTATE_UINT32_ARRAY(cgr, IMXCCMState, 3),
- VMSTATE_UINT32(pmcr0, IMXCCMState),
- VMSTATE_UINT32(pmcr1, IMXCCMState),
- VMSTATE_UINT32(pll_refclk_freq, IMXCCMState),
- VMSTATE_END_OF_LIST()
- },
- .post_load = imx_ccm_post_load,
-};
-
-/* CCMR */
-#define CCMR_FPME (1<<0)
-#define CCMR_MPE (1<<3)
-#define CCMR_MDS (1<<7)
-#define CCMR_FPMF (1<<26)
-#define CCMR_PRCS (3<<1)
-
-/* PDR0 */
-#define PDR0_MCU_PODF_SHIFT (0)
-#define PDR0_MCU_PODF_MASK (0x7)
-#define PDR0_MAX_PODF_SHIFT (3)
-#define PDR0_MAX_PODF_MASK (0x7)
-#define PDR0_IPG_PODF_SHIFT (6)
-#define PDR0_IPG_PODF_MASK (0x3)
-#define PDR0_NFC_PODF_SHIFT (8)
-#define PDR0_NFC_PODF_MASK (0x7)
-#define PDR0_HSP_PODF_SHIFT (11)
-#define PDR0_HSP_PODF_MASK (0x7)
-#define PDR0_PER_PODF_SHIFT (16)
-#define PDR0_PER_PODF_MASK (0x1f)
-#define PDR0_CSI_PODF_SHIFT (23)
-#define PDR0_CSI_PODF_MASK (0x1ff)
-
-#define EXTRACT(value, name) (((value) >> PDR0_##name##_PODF_SHIFT) \
- & PDR0_##name##_PODF_MASK)
-#define INSERT(value, name) (((value) & PDR0_##name##_PODF_MASK) << \
- PDR0_##name##_PODF_SHIFT)
-/* PLL control registers */
-#define PD(v) (((v) >> 26) & 0xf)
-#define MFD(v) (((v) >> 16) & 0x3ff)
-#define MFI(v) (((v) >> 10) & 0xf);
-#define MFN(v) ((v) & 0x3ff)
+#define DPRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX_CCM) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_CCM, \
+ __func__, ##args); \
+ } \
+ } while (0)
-#define PLL_PD(x) (((x) & 0xf) << 26)
-#define PLL_MFD(x) (((x) & 0x3ff) << 16)
-#define PLL_MFI(x) (((x) & 0xf) << 10)
-#define PLL_MFN(x) (((x) & 0x3ff) << 0)
-uint32_t imx_clock_frequency(DeviceState *dev, IMXClk clock)
+uint32_t imx_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
{
- IMXCCMState *s = IMX_CCM(dev);
+ uint32_t freq = 0;
+ IMXCCMClass *klass = IMX_GET_CLASS(dev);
- switch (clock) {
- case NOCLK:
- return 0;
- case MCU:
- return s->mcu_clk_freq;
- case HSP:
- return s->hsp_clk_freq;
- case IPG:
- return s->ipg_clk_freq;
- case CLK_32k:
- return CKIL_FREQ;
+ if (klass->get_clock_frequency) {
+ freq = klass->get_clock_frequency(dev, clock);
}
- return 0;
+
+ DPRINTF("(clock = %d) = %d\n", clock, freq);
+
+ return freq;
}
/*
* Calculate PLL output frequency
*/
-static uint32_t calc_pll(uint32_t pllreg, uint32_t base_freq)
+uint32_t imx_ccm_calc_pll(uint32_t pllreg, uint32_t base_freq)
{
+ int32_t freq;
int32_t mfn = MFN(pllreg); /* Numerator */
uint32_t mfi = MFI(pllreg); /* Integer part */
uint32_t mfd = 1 + MFD(pllreg); /* Denominator */
@@ -142,180 +55,26 @@ static uint32_t calc_pll(uint32_t pllreg, uint32_t base_freq)
if (mfi < 5) {
mfi = 5;
}
+
/* mfn is 10-bit signed twos-complement */
mfn <<= 32 - 10;
mfn >>= 32 - 10;
- return ((2 * (base_freq >> 10) * (mfi * mfd + mfn)) /
+ freq = ((2 * (base_freq >> 10) * (mfi * mfd + mfn)) /
(mfd * pd)) << 10;
-}
-
-static void update_clocks(IMXCCMState *s)
-{
- /*
- * If we ever emulate more clocks, this should switch to a data-driven
- * approach
- */
-
- if ((s->ccmr & CCMR_PRCS) == 2) {
- s->pll_refclk_freq = CKIL_FREQ * 1024;
- } else {
- s->pll_refclk_freq = CKIH_FREQ;
- }
-
- /* ipg_clk_arm aka MCU clock */
- if ((s->ccmr & CCMR_MDS) || !(s->ccmr & CCMR_MPE)) {
- s->mcu_clk_freq = s->pll_refclk_freq;
- } else {
- s->mcu_clk_freq = calc_pll(s->mpctl, s->pll_refclk_freq);
- }
-
- /* High-speed clock */
- s->hsp_clk_freq = s->mcu_clk_freq / (1 + EXTRACT(s->pdr0, HSP));
- s->ipg_clk_freq = s->hsp_clk_freq / (1 + EXTRACT(s->pdr0, IPG));
- DPRINTF("Clocks: mcu %uMHz, HSP %uMHz, IPG %uHz\n",
- s->mcu_clk_freq / 1000000,
- s->hsp_clk_freq / 1000000,
- s->ipg_clk_freq);
-}
-
-static void imx_ccm_reset(DeviceState *dev)
-{
- IMXCCMState *s = IMX_CCM(dev);
-
- s->ccmr = 0x074b0b7b;
- s->pdr0 = 0xff870b48;
- s->pdr1 = 0x49fcfe7f;
- s->mpctl = PLL_PD(1) | PLL_MFD(0) | PLL_MFI(6) | PLL_MFN(0);
- s->cgr[0] = s->cgr[1] = s->cgr[2] = 0xffffffff;
- s->spctl = PLL_PD(1) | PLL_MFD(4) | PLL_MFI(0xc) | PLL_MFN(1);
- s->pmcr0 = 0x80209828;
-
- update_clocks(s);
-}
-
-static uint64_t imx_ccm_read(void *opaque, hwaddr offset,
- unsigned size)
-{
- IMXCCMState *s = (IMXCCMState *)opaque;
-
- DPRINTF("read(offset=%x)", offset >> 2);
- switch (offset >> 2) {
- case 0: /* CCMR */
- DPRINTF(" ccmr = 0x%x\n", s->ccmr);
- return s->ccmr;
- case 1:
- DPRINTF(" pdr0 = 0x%x\n", s->pdr0);
- return s->pdr0;
- case 2:
- DPRINTF(" pdr1 = 0x%x\n", s->pdr1);
- return s->pdr1;
- case 4:
- DPRINTF(" mpctl = 0x%x\n", s->mpctl);
- return s->mpctl;
- case 6:
- DPRINTF(" spctl = 0x%x\n", s->spctl);
- return s->spctl;
- case 8:
- DPRINTF(" cgr0 = 0x%x\n", s->cgr[0]);
- return s->cgr[0];
- case 9:
- DPRINTF(" cgr1 = 0x%x\n", s->cgr[1]);
- return s->cgr[1];
- case 10:
- DPRINTF(" cgr2 = 0x%x\n", s->cgr[2]);
- return s->cgr[2];
- case 18: /* LTR1 */
- return 0x00004040;
- case 23:
- DPRINTF(" pcmr0 = 0x%x\n", s->pmcr0);
- return s->pmcr0;
- }
- DPRINTF(" return 0\n");
- return 0;
-}
-
-static void imx_ccm_write(void *opaque, hwaddr offset,
- uint64_t value, unsigned size)
-{
- IMXCCMState *s = (IMXCCMState *)opaque;
-
- DPRINTF("write(offset=%x, value = %x)\n",
- offset >> 2, (unsigned int)value);
- switch (offset >> 2) {
- case 0:
- s->ccmr = CCMR_FPMF | (value & 0x3b6fdfff);
- break;
- case 1:
- s->pdr0 = value & 0xff9f3fff;
- break;
- case 2:
- s->pdr1 = value;
- break;
- case 4:
- s->mpctl = value & 0xbfff3fff;
- break;
- case 6:
- s->spctl = value & 0xbfff3fff;
- break;
- case 8:
- s->cgr[0] = value;
- return;
- case 9:
- s->cgr[1] = value;
- return;
- case 10:
- s->cgr[2] = value;
- return;
-
- default:
- return;
- }
- update_clocks(s);
-}
-
-static const struct MemoryRegionOps imx_ccm_ops = {
- .read = imx_ccm_read,
- .write = imx_ccm_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static int imx_ccm_init(SysBusDevice *dev)
-{
- IMXCCMState *s = IMX_CCM(dev);
-
- memory_region_init_io(&s->iomem, OBJECT(dev), &imx_ccm_ops, s,
- "imx_ccm", 0x1000);
- sysbus_init_mmio(dev, &s->iomem);
-
- return 0;
-}
-
-static int imx_ccm_post_load(void *opaque, int version_id)
-{
- IMXCCMState *s = (IMXCCMState *)opaque;
-
- update_clocks(s);
- return 0;
-}
-
-static void imx_ccm_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
+ DPRINTF("(pllreg = 0x%08x, base_freq = %d) = %d\n", pllreg, base_freq,
+ freq);
- sbc->init = imx_ccm_init;
- dc->reset = imx_ccm_reset;
- dc->vmsd = &vmstate_imx_ccm;
- dc->desc = "i.MX Clock Control Module";
+ return freq;
}
static const TypeInfo imx_ccm_info = {
- .name = TYPE_IMX_CCM,
- .parent = TYPE_SYS_BUS_DEVICE,
+ .name = TYPE_IMX_CCM,
+ .parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IMXCCMState),
- .class_init = imx_ccm_class_init,
+ .class_size = sizeof(IMXCCMClass),
+ .abstract = true,
};
static void imx_ccm_register_types(void)
diff --git a/qemu/hw/misc/ivshmem.c b/qemu/hw/misc/ivshmem.c
index cc76989a3..e40f23bfc 100644
--- a/qemu/hw/misc/ivshmem.c
+++ b/qemu/hw/misc/ivshmem.c
@@ -16,39 +16,57 @@
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/cutils.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/pci/pci.h"
+#include "hw/pci/msi.h"
#include "hw/pci/msix.h"
#include "sysemu/kvm.h"
#include "migration/migration.h"
#include "qemu/error-report.h"
#include "qemu/event_notifier.h"
-#include "qemu/fifo8.h"
+#include "qom/object_interfaces.h"
#include "sysemu/char.h"
+#include "sysemu/hostmem.h"
+#include "sysemu/qtest.h"
+#include "qapi/visitor.h"
+#include "exec/ram_addr.h"
+
+#include "hw/misc/ivshmem.h"
#include <sys/mman.h>
-#include <sys/types.h>
-#include <limits.h>
#define PCI_VENDOR_ID_IVSHMEM PCI_VENDOR_ID_REDHAT_QUMRANET
#define PCI_DEVICE_ID_IVSHMEM 0x1110
+#define IVSHMEM_MAX_PEERS UINT16_MAX
#define IVSHMEM_IOEVENTFD 0
#define IVSHMEM_MSI 1
-#define IVSHMEM_PEER 0
-#define IVSHMEM_MASTER 1
-
#define IVSHMEM_REG_BAR_SIZE 0x100
-//#define DEBUG_IVSHMEM
-#ifdef DEBUG_IVSHMEM
-#define IVSHMEM_DPRINTF(fmt, ...) \
- do {printf("IVSHMEM: " fmt, ## __VA_ARGS__); } while (0)
-#else
-#define IVSHMEM_DPRINTF(fmt, ...)
-#endif
+#define IVSHMEM_DEBUG 0
+#define IVSHMEM_DPRINTF(fmt, ...) \
+ do { \
+ if (IVSHMEM_DEBUG) { \
+ printf("IVSHMEM: " fmt, ## __VA_ARGS__); \
+ } \
+ } while (0)
+
+#define TYPE_IVSHMEM_COMMON "ivshmem-common"
+#define IVSHMEM_COMMON(obj) \
+ OBJECT_CHECK(IVShmemState, (obj), TYPE_IVSHMEM_COMMON)
+
+#define TYPE_IVSHMEM_PLAIN "ivshmem-plain"
+#define IVSHMEM_PLAIN(obj) \
+ OBJECT_CHECK(IVShmemState, (obj), TYPE_IVSHMEM_PLAIN)
+
+#define TYPE_IVSHMEM_DOORBELL "ivshmem-doorbell"
+#define IVSHMEM_DOORBELL(obj) \
+ OBJECT_CHECK(IVShmemState, (obj), TYPE_IVSHMEM_DOORBELL)
#define TYPE_IVSHMEM "ivshmem"
#define IVSHMEM(obj) \
@@ -59,51 +77,50 @@ typedef struct Peer {
EventNotifier *eventfds;
} Peer;
-typedef struct EventfdEntry {
+typedef struct MSIVector {
PCIDevice *pdev;
- int vector;
-} EventfdEntry;
+ int virq;
+} MSIVector;
typedef struct IVShmemState {
/*< private >*/
PCIDevice parent_obj;
/*< public >*/
+ uint32_t features;
+
+ /* exactly one of these two may be set */
+ HostMemoryBackend *hostmem; /* with interrupts */
+ CharDriverState *server_chr; /* without interrupts */
+
+ /* registers */
uint32_t intrmask;
uint32_t intrstatus;
- uint32_t doorbell;
-
- CharDriverState **eventfd_chr;
- CharDriverState *server_chr;
- Fifo8 incoming_fifo;
- MemoryRegion ivshmem_mmio;
+ int vm_id;
- /* We might need to register the BAR before we actually have the memory.
- * So prepare a container MemoryRegion for the BAR immediately and
- * add a subregion when we have the memory.
- */
- MemoryRegion bar;
- MemoryRegion ivshmem;
- uint64_t ivshmem_size; /* size of shared memory region */
- uint32_t ivshmem_attr;
- uint32_t ivshmem_64bit;
- int shm_fd; /* shared memory file descriptor */
+ /* BARs */
+ MemoryRegion ivshmem_mmio; /* BAR 0 (registers) */
+ MemoryRegion *ivshmem_bar2; /* BAR 2 (shared memory) */
+ MemoryRegion server_bar2; /* used with server_chr */
+ /* interrupt support */
Peer *peers;
- int nb_peers; /* how many guests we have space for */
- int max_peer; /* maximum numbered peer */
-
- int vm_id;
+ int nb_peers; /* space in @peers[] */
uint32_t vectors;
- uint32_t features;
- EventfdEntry *eventfd_table;
+ MSIVector *msi_vectors;
+ uint64_t msg_buf; /* buffer for receiving server messages */
+ int msg_buffered_bytes; /* #bytes in @msg_buf */
+ /* migration stuff */
+ OnOffAuto master;
Error *migration_blocker;
- char * shmobj;
- char * sizearg;
- char * role;
- int role_val; /* scalar to avoid multiple string comparisons */
+ /* legacy cruft */
+ char *role;
+ char *shmobj;
+ char *sizearg;
+ size_t legacy_size;
+ uint32_t not_legacy_32bit;
} IVShmemState;
/* registers for the Inter-VM shared memory device */
@@ -119,16 +136,34 @@ static inline uint32_t ivshmem_has_feature(IVShmemState *ivs,
return (ivs->features & (1 << feature));
}
-static inline bool is_power_of_two(uint64_t x) {
- return (x & (x - 1)) == 0;
+static inline bool ivshmem_is_master(IVShmemState *s)
+{
+ assert(s->master != ON_OFF_AUTO_AUTO);
+ return s->master == ON_OFF_AUTO_ON;
}
-/* accessing registers - based on rtl8139 */
-static void ivshmem_update_irq(IVShmemState *s, int val)
+static void ivshmem_update_irq(IVShmemState *s)
{
PCIDevice *d = PCI_DEVICE(s);
- int isr;
- isr = (s->intrstatus & s->intrmask) & 0xffffffff;
+ uint32_t isr = s->intrstatus & s->intrmask;
+
+ /*
+ * Do nothing unless the device actually uses INTx. Here's how
+ * the device variants signal interrupts, what they put in PCI
+ * config space:
+ * Device variant Interrupt Interrupt Pin MSI-X cap.
+ * ivshmem-plain none 0 no
+ * ivshmem-doorbell MSI-X 1 yes(1)
+ * ivshmem,msi=off INTx 1 no
+ * ivshmem,msi=on MSI-X 1(2) yes(1)
+ * (1) if guest enabled MSI-X
+ * (2) the device lies
+ * Leads to the condition for doing nothing:
+ */
+ if (ivshmem_has_feature(s, IVSHMEM_MSI)
+ || !d->config[PCI_INTERRUPT_PIN]) {
+ return;
+ }
/* don't print ISR resets */
if (isr) {
@@ -136,7 +171,7 @@ static void ivshmem_update_irq(IVShmemState *s, int val)
isr ? 1 : 0, s->intrstatus, s->intrmask);
}
- pci_set_irq(d, (isr != 0));
+ pci_set_irq(d, isr != 0);
}
static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val)
@@ -144,8 +179,7 @@ static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val)
IVSHMEM_DPRINTF("IntrMask write(w) val = 0x%04x\n", val);
s->intrmask = val;
-
- ivshmem_update_irq(s, val);
+ ivshmem_update_irq(s);
}
static uint32_t ivshmem_IntrMask_read(IVShmemState *s)
@@ -153,7 +187,6 @@ static uint32_t ivshmem_IntrMask_read(IVShmemState *s)
uint32_t ret = s->intrmask;
IVSHMEM_DPRINTF("intrmask read(w) val = 0x%04x\n", ret);
-
return ret;
}
@@ -162,8 +195,7 @@ static void ivshmem_IntrStatus_write(IVShmemState *s, uint32_t val)
IVSHMEM_DPRINTF("IntrStatus write(w) val = 0x%04x\n", val);
s->intrstatus = val;
-
- ivshmem_update_irq(s, val);
+ ivshmem_update_irq(s);
}
static uint32_t ivshmem_IntrStatus_read(IVShmemState *s)
@@ -172,9 +204,7 @@ static uint32_t ivshmem_IntrStatus_read(IVShmemState *s)
/* reading ISR clears all interrupts */
s->intrstatus = 0;
-
- ivshmem_update_irq(s, 0);
-
+ ivshmem_update_irq(s);
return ret;
}
@@ -201,7 +231,7 @@ static void ivshmem_io_write(void *opaque, hwaddr addr,
case DOORBELL:
/* check that dest VM ID is reasonable */
- if (dest > s->max_peer) {
+ if (dest >= s->nb_peers) {
IVSHMEM_DPRINTF("Invalid destination VM ID (%d)\n", dest);
break;
}
@@ -210,10 +240,13 @@ static void ivshmem_io_write(void *opaque, hwaddr addr,
if (vector < s->peers[dest].nb_eventfds) {
IVSHMEM_DPRINTF("Notifying VM %d on vector %d\n", dest, vector);
event_notifier_set(&s->peers[dest].eventfds[vector]);
+ } else {
+ IVSHMEM_DPRINTF("Invalid destination vector %d on VM %d\n",
+ vector, dest);
}
break;
default:
- IVSHMEM_DPRINTF("Invalid VM Doorbell VM %d\n", dest);
+ IVSHMEM_DPRINTF("Unhandled write " TARGET_FMT_plx "\n", addr);
}
}
@@ -235,12 +268,7 @@ static uint64_t ivshmem_io_read(void *opaque, hwaddr addr,
break;
case IVPOSITION:
- /* return my VM ID if the memory is mapped */
- if (s->shm_fd > 0) {
- ret = s->vm_id;
- } else {
- ret = -1;
- }
+ ret = s->vm_id;
break;
default:
@@ -261,105 +289,95 @@ static const MemoryRegionOps ivshmem_mmio_ops = {
},
};
-static void ivshmem_receive(void *opaque, const uint8_t *buf, int size)
+static void ivshmem_vector_notify(void *opaque)
{
- IVShmemState *s = opaque;
-
- ivshmem_IntrStatus_write(s, *buf);
+ MSIVector *entry = opaque;
+ PCIDevice *pdev = entry->pdev;
+ IVShmemState *s = IVSHMEM_COMMON(pdev);
+ int vector = entry - s->msi_vectors;
+ EventNotifier *n = &s->peers[s->vm_id].eventfds[vector];
- IVSHMEM_DPRINTF("ivshmem_receive 0x%02x\n", *buf);
-}
+ if (!event_notifier_test_and_clear(n)) {
+ return;
+ }
-static int ivshmem_can_receive(void * opaque)
-{
- return 8;
+ IVSHMEM_DPRINTF("interrupt on vector %p %d\n", pdev, vector);
+ if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
+ if (msix_enabled(pdev)) {
+ msix_notify(pdev, vector);
+ }
+ } else {
+ ivshmem_IntrStatus_write(s, 1);
+ }
}
-static void ivshmem_event(void *opaque, int event)
+static int ivshmem_vector_unmask(PCIDevice *dev, unsigned vector,
+ MSIMessage msg)
{
- IVSHMEM_DPRINTF("ivshmem_event %d\n", event);
-}
+ IVShmemState *s = IVSHMEM_COMMON(dev);
+ EventNotifier *n = &s->peers[s->vm_id].eventfds[vector];
+ MSIVector *v = &s->msi_vectors[vector];
+ int ret;
-static void fake_irqfd(void *opaque, const uint8_t *buf, int size) {
+ IVSHMEM_DPRINTF("vector unmask %p %d\n", dev, vector);
- EventfdEntry *entry = opaque;
- PCIDevice *pdev = entry->pdev;
+ ret = kvm_irqchip_update_msi_route(kvm_state, v->virq, msg, dev);
+ if (ret < 0) {
+ return ret;
+ }
- IVSHMEM_DPRINTF("interrupt on vector %p %d\n", pdev, entry->vector);
- msix_notify(pdev, entry->vector);
+ return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, v->virq);
}
-static CharDriverState* create_eventfd_chr_device(void * opaque, EventNotifier *n,
- int vector)
+static void ivshmem_vector_mask(PCIDevice *dev, unsigned vector)
{
- /* create a event character device based on the passed eventfd */
- IVShmemState *s = opaque;
- CharDriverState * chr;
- int eventfd = event_notifier_get_fd(n);
-
- chr = qemu_chr_open_eventfd(eventfd);
-
- if (chr == NULL) {
- error_report("creating eventfd for eventfd %d failed", eventfd);
- exit(1);
- }
- qemu_chr_fe_claim_no_fail(chr);
+ IVShmemState *s = IVSHMEM_COMMON(dev);
+ EventNotifier *n = &s->peers[s->vm_id].eventfds[vector];
+ int ret;
- /* if MSI is supported we need multiple interrupts */
- if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
- s->eventfd_table[vector].pdev = PCI_DEVICE(s);
- s->eventfd_table[vector].vector = vector;
+ IVSHMEM_DPRINTF("vector mask %p %d\n", dev, vector);
- qemu_chr_add_handlers(chr, ivshmem_can_receive, fake_irqfd,
- ivshmem_event, &s->eventfd_table[vector]);
- } else {
- qemu_chr_add_handlers(chr, ivshmem_can_receive, ivshmem_receive,
- ivshmem_event, s);
+ ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, n,
+ s->msi_vectors[vector].virq);
+ if (ret != 0) {
+ error_report("remove_irqfd_notifier_gsi failed");
}
+}
- return chr;
+static void ivshmem_vector_poll(PCIDevice *dev,
+ unsigned int vector_start,
+ unsigned int vector_end)
+{
+ IVShmemState *s = IVSHMEM_COMMON(dev);
+ unsigned int vector;
-}
+ IVSHMEM_DPRINTF("vector poll %p %d-%d\n", dev, vector_start, vector_end);
-static int check_shm_size(IVShmemState *s, int fd) {
- /* check that the guest isn't going to try and map more memory than the
- * the object has allocated return -1 to indicate error */
+ vector_end = MIN(vector_end, s->vectors);
- struct stat buf;
+ for (vector = vector_start; vector < vector_end; vector++) {
+ EventNotifier *notifier = &s->peers[s->vm_id].eventfds[vector];
- if (fstat(fd, &buf) < 0) {
- error_report("exiting: fstat on fd %d failed: %s",
- fd, strerror(errno));
- return -1;
- }
+ if (!msix_is_masked(dev, vector)) {
+ continue;
+ }
- if (s->ivshmem_size > buf.st_size) {
- error_report("Requested memory size greater"
- " than shared object size (%" PRIu64 " > %" PRIu64")",
- s->ivshmem_size, (uint64_t)buf.st_size);
- return -1;
- } else {
- return 0;
+ if (event_notifier_test_and_clear(notifier)) {
+ msix_set_pending(dev, vector);
+ }
}
}
-/* create the shared memory BAR when we are not using the server, so we can
- * create the BAR and map the memory immediately */
-static void create_shared_memory_BAR(IVShmemState *s, int fd) {
-
- void * ptr;
-
- s->shm_fd = fd;
-
- ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+static void watch_vector_notifier(IVShmemState *s, EventNotifier *n,
+ int vector)
+{
+ int eventfd = event_notifier_get_fd(n);
- memory_region_init_ram_ptr(&s->ivshmem, OBJECT(s), "ivshmem.bar2",
- s->ivshmem_size, ptr);
- vmstate_register_ram(&s->ivshmem, DEVICE(s));
- memory_region_add_subregion(&s->bar, 0, &s->ivshmem);
+ assert(!s->msi_vectors[vector].pdev);
+ s->msi_vectors[vector].pdev = PCI_DEVICE(s);
- /* region for shared memory */
- pci_register_bar(PCI_DEVICE(s), 2, s->ivshmem_attr, &s->bar);
+ qemu_set_fd_handler(eventfd, ivshmem_vector_notify,
+ NULL, &s->msi_vectors[vector]);
}
static void ivshmem_add_eventfd(IVShmemState *s, int posn, int i)
@@ -382,25 +400,22 @@ static void ivshmem_del_eventfd(IVShmemState *s, int posn, int i)
&s->peers[posn].eventfds[i]);
}
-static void close_guest_eventfds(IVShmemState *s, int posn)
+static void close_peer_eventfds(IVShmemState *s, int posn)
{
- int i, guest_curr_max;
+ int i, n;
- if (!ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
- return;
- }
- if (posn < 0 || posn >= s->nb_peers) {
- return;
- }
+ assert(posn >= 0 && posn < s->nb_peers);
+ n = s->peers[posn].nb_eventfds;
- guest_curr_max = s->peers[posn].nb_eventfds;
-
- memory_region_transaction_begin();
- for (i = 0; i < guest_curr_max; i++) {
- ivshmem_del_eventfd(s, posn, i);
+ if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
+ memory_region_transaction_begin();
+ for (i = 0; i < n; i++) {
+ ivshmem_del_eventfd(s, posn, i);
+ }
+ memory_region_transaction_commit();
}
- memory_region_transaction_commit();
- for (i = 0; i < guest_curr_max; i++) {
+
+ for (i = 0; i < n; i++) {
event_notifier_cleanup(&s->peers[posn].eventfds[i]);
}
@@ -408,187 +423,320 @@ static void close_guest_eventfds(IVShmemState *s, int posn)
s->peers[posn].nb_eventfds = 0;
}
-/* this function increase the dynamic storage need to store data about other
- * guests */
-static int increase_dynamic_storage(IVShmemState *s, int new_min_size)
+static void resize_peers(IVShmemState *s, int nb_peers)
{
+ int old_nb_peers = s->nb_peers;
+ int i;
- int j, old_nb_alloc;
-
- /* check for integer overflow */
- if (new_min_size >= INT_MAX / sizeof(Peer) - 1 || new_min_size <= 0) {
- return -1;
- }
+ assert(nb_peers > old_nb_peers);
+ IVSHMEM_DPRINTF("bumping storage to %d peers\n", nb_peers);
- old_nb_alloc = s->nb_peers;
+ s->peers = g_realloc(s->peers, nb_peers * sizeof(Peer));
+ s->nb_peers = nb_peers;
- if (new_min_size >= s->nb_peers) {
- /* +1 because #new_min_size is used as last array index */
- s->nb_peers = new_min_size + 1;
- } else {
- return 0;
+ for (i = old_nb_peers; i < nb_peers; i++) {
+ s->peers[i].eventfds = g_new0(EventNotifier, s->vectors);
+ s->peers[i].nb_eventfds = 0;
}
+}
- IVSHMEM_DPRINTF("bumping storage to %d guests\n", s->nb_peers);
- s->peers = g_realloc(s->peers, s->nb_peers * sizeof(Peer));
+static void ivshmem_add_kvm_msi_virq(IVShmemState *s, int vector,
+ Error **errp)
+{
+ PCIDevice *pdev = PCI_DEVICE(s);
+ MSIMessage msg = msix_get_message(pdev, vector);
+ int ret;
- /* zero out new pointers */
- for (j = old_nb_alloc; j < s->nb_peers; j++) {
- s->peers[j].eventfds = NULL;
- s->peers[j].nb_eventfds = 0;
+ IVSHMEM_DPRINTF("ivshmem_add_kvm_msi_virq vector:%d\n", vector);
+ assert(!s->msi_vectors[vector].pdev);
+
+ ret = kvm_irqchip_add_msi_route(kvm_state, msg, pdev);
+ if (ret < 0) {
+ error_setg(errp, "kvm_irqchip_add_msi_route failed");
+ return;
}
- return 0;
+ s->msi_vectors[vector].virq = ret;
+ s->msi_vectors[vector].pdev = pdev;
}
-static void ivshmem_read(void *opaque, const uint8_t *buf, int size)
+static void setup_interrupt(IVShmemState *s, int vector, Error **errp)
{
- IVShmemState *s = opaque;
- int incoming_fd, tmp_fd;
- int guest_max_eventfd;
- long incoming_posn;
-
- if (fifo8_is_empty(&s->incoming_fifo) && size == sizeof(incoming_posn)) {
- memcpy(&incoming_posn, buf, size);
- } else {
- const uint8_t *p;
- uint32_t num;
-
- IVSHMEM_DPRINTF("short read of %d bytes\n", size);
- num = MAX(size, sizeof(long) - fifo8_num_used(&s->incoming_fifo));
- fifo8_push_all(&s->incoming_fifo, buf, num);
- if (fifo8_num_used(&s->incoming_fifo) < sizeof(incoming_posn)) {
+ EventNotifier *n = &s->peers[s->vm_id].eventfds[vector];
+ bool with_irqfd = kvm_msi_via_irqfd_enabled() &&
+ ivshmem_has_feature(s, IVSHMEM_MSI);
+ PCIDevice *pdev = PCI_DEVICE(s);
+ Error *err = NULL;
+
+ IVSHMEM_DPRINTF("setting up interrupt for vector: %d\n", vector);
+
+ if (!with_irqfd) {
+ IVSHMEM_DPRINTF("with eventfd\n");
+ watch_vector_notifier(s, n, vector);
+ } else if (msix_enabled(pdev)) {
+ IVSHMEM_DPRINTF("with irqfd\n");
+ ivshmem_add_kvm_msi_virq(s, vector, &err);
+ if (err) {
+ error_propagate(errp, err);
return;
}
- size -= num;
- buf += num;
- p = fifo8_pop_buf(&s->incoming_fifo, sizeof(incoming_posn), &num);
- g_assert(num == sizeof(incoming_posn));
- memcpy(&incoming_posn, p, sizeof(incoming_posn));
- if (size > 0) {
- fifo8_push_all(&s->incoming_fifo, buf, size);
+
+ if (!msix_is_masked(pdev, vector)) {
+ kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL,
+ s->msi_vectors[vector].virq);
+ /* TODO handle error */
}
+ } else {
+ /* it will be delayed until msix is enabled, in write_config */
+ IVSHMEM_DPRINTF("with irqfd, delayed until msix enabled\n");
}
+}
- if (incoming_posn < -1) {
- IVSHMEM_DPRINTF("invalid incoming_posn %ld\n", incoming_posn);
+static void process_msg_shmem(IVShmemState *s, int fd, Error **errp)
+{
+ struct stat buf;
+ size_t size;
+ void *ptr;
+
+ if (s->ivshmem_bar2) {
+ error_setg(errp, "server sent unexpected shared memory message");
+ close(fd);
+ return;
+ }
+
+ if (fstat(fd, &buf) < 0) {
+ error_setg_errno(errp, errno,
+ "can't determine size of shared memory sent by server");
+ close(fd);
return;
}
- /* pick off s->server_chr->msgfd and store it, posn should accompany msg */
- tmp_fd = qemu_chr_fe_get_msgfd(s->server_chr);
- IVSHMEM_DPRINTF("posn is %ld, fd is %d\n", incoming_posn, tmp_fd);
+ size = buf.st_size;
- /* make sure we have enough space for this guest */
- if (incoming_posn >= s->nb_peers) {
- if (increase_dynamic_storage(s, incoming_posn) < 0) {
- error_report("increase_dynamic_storage() failed");
- if (tmp_fd != -1) {
- close(tmp_fd);
- }
+ /* Legacy cruft */
+ if (s->legacy_size != SIZE_MAX) {
+ if (size < s->legacy_size) {
+ error_setg(errp, "server sent only %zd bytes of shared memory",
+ (size_t)buf.st_size);
+ close(fd);
return;
}
+ size = s->legacy_size;
}
- if (tmp_fd == -1) {
- /* if posn is positive and unseen before then this is our posn*/
- if ((incoming_posn >= 0) &&
- (s->peers[incoming_posn].eventfds == NULL)) {
- /* receive our posn */
- s->vm_id = incoming_posn;
- return;
- } else {
- /* otherwise an fd == -1 means an existing guest has gone away */
- IVSHMEM_DPRINTF("posn %ld has gone away\n", incoming_posn);
- close_guest_eventfds(s, incoming_posn);
- return;
- }
+ /* mmap the region and map into the BAR2 */
+ ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (ptr == MAP_FAILED) {
+ error_setg_errno(errp, errno, "Failed to mmap shared memory");
+ close(fd);
+ return;
}
+ memory_region_init_ram_ptr(&s->server_bar2, OBJECT(s),
+ "ivshmem.bar2", size, ptr);
+ qemu_set_ram_fd(memory_region_get_ram_addr(&s->server_bar2), fd);
+ s->ivshmem_bar2 = &s->server_bar2;
+}
- /* because of the implementation of get_msgfd, we need a dup */
- incoming_fd = dup(tmp_fd);
+static void process_msg_disconnect(IVShmemState *s, uint16_t posn,
+ Error **errp)
+{
+ IVSHMEM_DPRINTF("posn %d has gone away\n", posn);
+ if (posn >= s->nb_peers || posn == s->vm_id) {
+ error_setg(errp, "invalid peer %d", posn);
+ return;
+ }
+ close_peer_eventfds(s, posn);
+}
- if (incoming_fd == -1) {
- error_report("could not allocate file descriptor %s", strerror(errno));
- close(tmp_fd);
+static void process_msg_connect(IVShmemState *s, uint16_t posn, int fd,
+ Error **errp)
+{
+ Peer *peer = &s->peers[posn];
+ int vector;
+
+ /*
+ * The N-th connect message for this peer comes with the file
+ * descriptor for vector N-1. Count messages to find the vector.
+ */
+ if (peer->nb_eventfds >= s->vectors) {
+ error_setg(errp, "Too many eventfd received, device has %d vectors",
+ s->vectors);
+ close(fd);
return;
}
+ vector = peer->nb_eventfds++;
+
+ IVSHMEM_DPRINTF("eventfds[%d][%d] = %d\n", posn, vector, fd);
+ event_notifier_init_fd(&peer->eventfds[vector], fd);
+ fcntl_setfl(fd, O_NONBLOCK); /* msix/irqfd poll non block */
+
+ if (posn == s->vm_id) {
+ setup_interrupt(s, vector, errp);
+ /* TODO do we need to handle the error? */
+ }
- /* if the position is -1, then it's shared memory region fd */
- if (incoming_posn == -1) {
+ if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
+ ivshmem_add_eventfd(s, posn, vector);
+ }
+}
- void * map_ptr;
+static void process_msg(IVShmemState *s, int64_t msg, int fd, Error **errp)
+{
+ IVSHMEM_DPRINTF("posn is %" PRId64 ", fd is %d\n", msg, fd);
- s->max_peer = 0;
+ if (msg < -1 || msg > IVSHMEM_MAX_PEERS) {
+ error_setg(errp, "server sent invalid message %" PRId64, msg);
+ close(fd);
+ return;
+ }
- if (check_shm_size(s, incoming_fd) == -1) {
- exit(1);
- }
+ if (msg == -1) {
+ process_msg_shmem(s, fd, errp);
+ return;
+ }
- /* mmap the region and map into the BAR2 */
- map_ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED,
- incoming_fd, 0);
- memory_region_init_ram_ptr(&s->ivshmem, OBJECT(s),
- "ivshmem.bar2", s->ivshmem_size, map_ptr);
- vmstate_register_ram(&s->ivshmem, DEVICE(s));
+ if (msg >= s->nb_peers) {
+ resize_peers(s, msg + 1);
+ }
- IVSHMEM_DPRINTF("guest h/w addr = %p, size = %" PRIu64 "\n",
- map_ptr, s->ivshmem_size);
+ if (fd >= 0) {
+ process_msg_connect(s, msg, fd, errp);
+ } else {
+ process_msg_disconnect(s, msg, errp);
+ }
+}
- memory_region_add_subregion(&s->bar, 0, &s->ivshmem);
+static int ivshmem_can_receive(void *opaque)
+{
+ IVShmemState *s = opaque;
- /* only store the fd if it is successfully mapped */
- s->shm_fd = incoming_fd;
+ assert(s->msg_buffered_bytes < sizeof(s->msg_buf));
+ return sizeof(s->msg_buf) - s->msg_buffered_bytes;
+}
+static void ivshmem_read(void *opaque, const uint8_t *buf, int size)
+{
+ IVShmemState *s = opaque;
+ Error *err = NULL;
+ int fd;
+ int64_t msg;
+
+ assert(size >= 0 && s->msg_buffered_bytes + size <= sizeof(s->msg_buf));
+ memcpy((unsigned char *)&s->msg_buf + s->msg_buffered_bytes, buf, size);
+ s->msg_buffered_bytes += size;
+ if (s->msg_buffered_bytes < sizeof(s->msg_buf)) {
return;
}
+ msg = le64_to_cpu(s->msg_buf);
+ s->msg_buffered_bytes = 0;
- /* each guest has an array of eventfds, and we keep track of how many
- * guests for each VM */
- guest_max_eventfd = s->peers[incoming_posn].nb_eventfds;
+ fd = qemu_chr_fe_get_msgfd(s->server_chr);
+ IVSHMEM_DPRINTF("posn is %" PRId64 ", fd is %d\n", msg, fd);
- if (guest_max_eventfd == 0) {
- /* one eventfd per MSI vector */
- s->peers[incoming_posn].eventfds = g_new(EventNotifier, s->vectors);
+ process_msg(s, msg, fd, &err);
+ if (err) {
+ error_report_err(err);
}
+}
- /* this is an eventfd for a particular guest VM */
- IVSHMEM_DPRINTF("eventfds[%ld][%d] = %d\n", incoming_posn,
- guest_max_eventfd, incoming_fd);
- event_notifier_init_fd(&s->peers[incoming_posn].eventfds[guest_max_eventfd],
- incoming_fd);
+static int64_t ivshmem_recv_msg(IVShmemState *s, int *pfd, Error **errp)
+{
+ int64_t msg;
+ int n, ret;
+
+ n = 0;
+ do {
+ ret = qemu_chr_fe_read_all(s->server_chr, (uint8_t *)&msg + n,
+ sizeof(msg) - n);
+ if (ret < 0 && ret != -EINTR) {
+ error_setg_errno(errp, -ret, "read from server failed");
+ return INT64_MIN;
+ }
+ n += ret;
+ } while (n < sizeof(msg));
- /* increment count for particular guest */
- s->peers[incoming_posn].nb_eventfds++;
+ *pfd = qemu_chr_fe_get_msgfd(s->server_chr);
+ return msg;
+}
- /* keep track of the maximum VM ID */
- if (incoming_posn > s->max_peer) {
- s->max_peer = incoming_posn;
- }
+static void ivshmem_recv_setup(IVShmemState *s, Error **errp)
+{
+ Error *err = NULL;
+ int64_t msg;
+ int fd;
- if (incoming_posn == s->vm_id) {
- s->eventfd_chr[guest_max_eventfd] = create_eventfd_chr_device(s,
- &s->peers[s->vm_id].eventfds[guest_max_eventfd],
- guest_max_eventfd);
+ msg = ivshmem_recv_msg(s, &fd, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ if (msg != IVSHMEM_PROTOCOL_VERSION) {
+ error_setg(errp, "server sent version %" PRId64 ", expecting %d",
+ msg, IVSHMEM_PROTOCOL_VERSION);
+ return;
+ }
+ if (fd != -1) {
+ error_setg(errp, "server sent invalid version message");
+ return;
}
- if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
- ivshmem_add_eventfd(s, incoming_posn, guest_max_eventfd);
+ /*
+ * ivshmem-server sends the remaining initial messages in a fixed
+ * order, but the device has always accepted them in any order.
+ * Stay as compatible as practical, just in case people use
+ * servers that behave differently.
+ */
+
+ /*
+ * ivshmem_device_spec.txt has always required the ID message
+ * right here, and ivshmem-server has always complied. However,
+ * older versions of the device accepted it out of order, but
+ * broke when an interrupt setup message arrived before it.
+ */
+ msg = ivshmem_recv_msg(s, &fd, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ if (fd != -1 || msg < 0 || msg > IVSHMEM_MAX_PEERS) {
+ error_setg(errp, "server sent invalid ID message");
+ return;
}
+ s->vm_id = msg;
+
+ /*
+ * Receive more messages until we got shared memory.
+ */
+ do {
+ msg = ivshmem_recv_msg(s, &fd, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ process_msg(s, msg, fd, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ } while (msg != -1);
+
+ /*
+ * This function must either map the shared memory or fail. The
+ * loop above ensures that: it terminates normally only after it
+ * successfully processed the server's shared memory message.
+ * Assert that actually mapped the shared memory:
+ */
+ assert(s->ivshmem_bar2);
}
/* Select the MSI-X vectors used by device.
* ivshmem maps events to vectors statically, so
* we just enable all vectors on init and after reset. */
-static void ivshmem_use_msix(IVShmemState * s)
+static void ivshmem_msix_vector_use(IVShmemState *s)
{
PCIDevice *d = PCI_DEVICE(s);
int i;
- if (!msix_present(d)) {
- return;
- }
-
for (i = 0; i < s->vectors; i++) {
msix_vector_use(d, i);
}
@@ -596,295 +744,579 @@ static void ivshmem_use_msix(IVShmemState * s)
static void ivshmem_reset(DeviceState *d)
{
- IVShmemState *s = IVSHMEM(d);
+ IVShmemState *s = IVSHMEM_COMMON(d);
s->intrstatus = 0;
- ivshmem_use_msix(s);
+ s->intrmask = 0;
+ if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
+ ivshmem_msix_vector_use(s);
+ }
}
-static uint64_t ivshmem_get_size(IVShmemState * s) {
+static int ivshmem_setup_interrupts(IVShmemState *s)
+{
+ /* allocate QEMU callback data for receiving interrupts */
+ s->msi_vectors = g_malloc0(s->vectors * sizeof(MSIVector));
- uint64_t value;
- char *ptr;
+ if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
+ if (msix_init_exclusive_bar(PCI_DEVICE(s), s->vectors, 1)) {
+ return -1;
+ }
- value = strtoull(s->sizearg, &ptr, 10);
- switch (*ptr) {
- case 0: case 'M': case 'm':
- value <<= 20;
- break;
- case 'G': case 'g':
- value <<= 30;
- break;
- default:
- error_report("invalid ram size: %s", s->sizearg);
- exit(1);
+ IVSHMEM_DPRINTF("msix initialized (%d vectors)\n", s->vectors);
+ ivshmem_msix_vector_use(s);
}
- /* BARs must be a power of 2 */
- if (!is_power_of_two(value)) {
- error_report("size must be power of 2");
- exit(1);
+ return 0;
+}
+
+static void ivshmem_enable_irqfd(IVShmemState *s)
+{
+ PCIDevice *pdev = PCI_DEVICE(s);
+ int i;
+
+ for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) {
+ Error *err = NULL;
+
+ ivshmem_add_kvm_msi_virq(s, i, &err);
+ if (err) {
+ error_report_err(err);
+ /* TODO do we need to handle the error? */
+ }
}
- return value;
+ if (msix_set_vector_notifiers(pdev,
+ ivshmem_vector_unmask,
+ ivshmem_vector_mask,
+ ivshmem_vector_poll)) {
+ error_report("ivshmem: msix_set_vector_notifiers failed");
+ }
}
-static void ivshmem_setup_msi(IVShmemState * s)
+static void ivshmem_remove_kvm_msi_virq(IVShmemState *s, int vector)
{
- if (msix_init_exclusive_bar(PCI_DEVICE(s), s->vectors, 1)) {
- IVSHMEM_DPRINTF("msix initialization failed\n");
- exit(1);
- }
+ IVSHMEM_DPRINTF("ivshmem_remove_kvm_msi_virq vector:%d\n", vector);
- IVSHMEM_DPRINTF("msix initialized (%d vectors)\n", s->vectors);
+ if (s->msi_vectors[vector].pdev == NULL) {
+ return;
+ }
- /* allocate QEMU char devices for receiving interrupts */
- s->eventfd_table = g_malloc0(s->vectors * sizeof(EventfdEntry));
+ /* it was cleaned when masked in the frontend. */
+ kvm_irqchip_release_virq(kvm_state, s->msi_vectors[vector].virq);
- ivshmem_use_msix(s);
+ s->msi_vectors[vector].pdev = NULL;
}
-static void ivshmem_save(QEMUFile* f, void *opaque)
+static void ivshmem_disable_irqfd(IVShmemState *s)
{
- IVShmemState *proxy = opaque;
- PCIDevice *pci_dev = PCI_DEVICE(proxy);
-
- IVSHMEM_DPRINTF("ivshmem_save\n");
- pci_device_save(pci_dev, f);
+ PCIDevice *pdev = PCI_DEVICE(s);
+ int i;
- if (ivshmem_has_feature(proxy, IVSHMEM_MSI)) {
- msix_save(pci_dev, f);
- } else {
- qemu_put_be32(f, proxy->intrstatus);
- qemu_put_be32(f, proxy->intrmask);
+ for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) {
+ ivshmem_remove_kvm_msi_virq(s, i);
}
+ msix_unset_vector_notifiers(pdev);
}
-static int ivshmem_load(QEMUFile* f, void *opaque, int version_id)
+static void ivshmem_write_config(PCIDevice *pdev, uint32_t address,
+ uint32_t val, int len)
{
- IVSHMEM_DPRINTF("ivshmem_load\n");
+ IVShmemState *s = IVSHMEM_COMMON(pdev);
+ int is_enabled, was_enabled = msix_enabled(pdev);
- IVShmemState *proxy = opaque;
- PCIDevice *pci_dev = PCI_DEVICE(proxy);
- int ret;
+ pci_default_write_config(pdev, address, val, len);
+ is_enabled = msix_enabled(pdev);
- if (version_id > 0) {
- return -EINVAL;
+ if (kvm_msi_via_irqfd_enabled()) {
+ if (!was_enabled && is_enabled) {
+ ivshmem_enable_irqfd(s);
+ } else if (was_enabled && !is_enabled) {
+ ivshmem_disable_irqfd(s);
+ }
}
+}
- if (proxy->role_val == IVSHMEM_PEER) {
- error_report("'peer' devices are not migratable");
- return -EINVAL;
+static void ivshmem_common_realize(PCIDevice *dev, Error **errp)
+{
+ IVShmemState *s = IVSHMEM_COMMON(dev);
+ Error *err = NULL;
+ uint8_t *pci_conf;
+ uint8_t attr = PCI_BASE_ADDRESS_SPACE_MEMORY |
+ PCI_BASE_ADDRESS_MEM_PREFETCH;
+
+ /* IRQFD requires MSI */
+ if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) &&
+ !ivshmem_has_feature(s, IVSHMEM_MSI)) {
+ error_setg(errp, "ioeventfd/irqfd requires MSI");
+ return;
}
- ret = pci_device_load(pci_dev, f);
- if (ret) {
- return ret;
+ pci_conf = dev->config;
+ pci_conf[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
+
+ memory_region_init_io(&s->ivshmem_mmio, OBJECT(s), &ivshmem_mmio_ops, s,
+ "ivshmem-mmio", IVSHMEM_REG_BAR_SIZE);
+
+ /* region for registers*/
+ pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY,
+ &s->ivshmem_mmio);
+
+ if (!s->not_legacy_32bit) {
+ attr |= PCI_BASE_ADDRESS_MEM_TYPE_64;
}
- if (ivshmem_has_feature(proxy, IVSHMEM_MSI)) {
- msix_load(pci_dev, f);
- ivshmem_use_msix(proxy);
+ if (s->hostmem != NULL) {
+ IVSHMEM_DPRINTF("using hostmem\n");
+
+ s->ivshmem_bar2 = host_memory_backend_get_memory(s->hostmem,
+ &error_abort);
} else {
- proxy->intrstatus = qemu_get_be32(f);
- proxy->intrmask = qemu_get_be32(f);
+ assert(s->server_chr);
+
+ IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n",
+ s->server_chr->filename);
+
+ /* we allocate enough space for 16 peers and grow as needed */
+ resize_peers(s, 16);
+
+ /*
+ * Receive setup messages from server synchronously.
+ * Older versions did it asynchronously, but that creates a
+ * number of entertaining race conditions.
+ */
+ ivshmem_recv_setup(s, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ if (s->master == ON_OFF_AUTO_ON && s->vm_id != 0) {
+ error_setg(errp,
+ "master must connect to the server before any peers");
+ return;
+ }
+
+ qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive,
+ ivshmem_read, NULL, s);
+
+ if (ivshmem_setup_interrupts(s) < 0) {
+ error_setg(errp, "failed to initialize interrupts");
+ return;
+ }
}
- return 0;
-}
+ vmstate_register_ram(s->ivshmem_bar2, DEVICE(s));
+ pci_register_bar(PCI_DEVICE(s), 2, attr, s->ivshmem_bar2);
-static void ivshmem_write_config(PCIDevice *pci_dev, uint32_t address,
- uint32_t val, int len)
-{
- pci_default_write_config(pci_dev, address, val, len);
+ if (s->master == ON_OFF_AUTO_AUTO) {
+ s->master = s->vm_id == 0 ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF;
+ }
+
+ if (!ivshmem_is_master(s)) {
+ error_setg(&s->migration_blocker,
+ "Migration is disabled when using feature 'peer mode' in device 'ivshmem'");
+ migrate_add_blocker(s->migration_blocker);
+ }
}
-static int pci_ivshmem_init(PCIDevice *dev)
+static void ivshmem_exit(PCIDevice *dev)
{
- IVShmemState *s = IVSHMEM(dev);
- uint8_t *pci_conf;
+ IVShmemState *s = IVSHMEM_COMMON(dev);
+ int i;
- if (s->sizearg == NULL)
- s->ivshmem_size = 4 << 20; /* 4 MB default */
- else {
- s->ivshmem_size = ivshmem_get_size(s);
+ if (s->migration_blocker) {
+ migrate_del_blocker(s->migration_blocker);
+ error_free(s->migration_blocker);
}
- fifo8_create(&s->incoming_fifo, sizeof(long));
+ if (memory_region_is_mapped(s->ivshmem_bar2)) {
+ if (!s->hostmem) {
+ void *addr = memory_region_get_ram_ptr(s->ivshmem_bar2);
+ int fd;
- register_savevm(DEVICE(dev), "ivshmem", 0, 0, ivshmem_save, ivshmem_load,
- dev);
+ if (munmap(addr, memory_region_size(s->ivshmem_bar2) == -1)) {
+ error_report("Failed to munmap shared memory %s",
+ strerror(errno));
+ }
- /* IRQFD requires MSI */
- if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) &&
- !ivshmem_has_feature(s, IVSHMEM_MSI)) {
- error_report("ioeventfd/irqfd requires MSI");
- exit(1);
+ fd = qemu_get_ram_fd(memory_region_get_ram_addr(s->ivshmem_bar2));
+ close(fd);
+ }
+
+ vmstate_unregister_ram(s->ivshmem_bar2, DEVICE(dev));
}
- /* check that role is reasonable */
- if (s->role) {
- if (strncmp(s->role, "peer", 5) == 0) {
- s->role_val = IVSHMEM_PEER;
- } else if (strncmp(s->role, "master", 7) == 0) {
- s->role_val = IVSHMEM_MASTER;
- } else {
- error_report("'role' must be 'peer' or 'master'");
- exit(1);
+ if (s->peers) {
+ for (i = 0; i < s->nb_peers; i++) {
+ close_peer_eventfds(s, i);
}
- } else {
- s->role_val = IVSHMEM_MASTER; /* default */
+ g_free(s->peers);
}
- if (s->role_val == IVSHMEM_PEER) {
- error_setg(&s->migration_blocker,
- "Migration is disabled when using feature 'peer mode' in device 'ivshmem'");
- migrate_add_blocker(s->migration_blocker);
+ if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
+ msix_uninit_exclusive_bar(dev);
}
- pci_conf = dev->config;
- pci_conf[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
+ g_free(s->msi_vectors);
+}
- pci_config_set_interrupt_pin(pci_conf, 1);
+static int ivshmem_pre_load(void *opaque)
+{
+ IVShmemState *s = opaque;
- s->shm_fd = 0;
+ if (!ivshmem_is_master(s)) {
+ error_report("'peer' devices are not migratable");
+ return -EINVAL;
+ }
- memory_region_init_io(&s->ivshmem_mmio, OBJECT(s), &ivshmem_mmio_ops, s,
- "ivshmem-mmio", IVSHMEM_REG_BAR_SIZE);
+ return 0;
+}
- /* region for registers*/
- pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY,
- &s->ivshmem_mmio);
+static int ivshmem_post_load(void *opaque, int version_id)
+{
+ IVShmemState *s = opaque;
- memory_region_init(&s->bar, OBJECT(s), "ivshmem-bar2-container", s->ivshmem_size);
- s->ivshmem_attr = PCI_BASE_ADDRESS_SPACE_MEMORY |
- PCI_BASE_ADDRESS_MEM_PREFETCH;
- if (s->ivshmem_64bit) {
- s->ivshmem_attr |= PCI_BASE_ADDRESS_MEM_TYPE_64;
+ if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
+ ivshmem_msix_vector_use(s);
}
+ return 0;
+}
- if ((s->server_chr != NULL) &&
- (strncmp(s->server_chr->filename, "unix:", 5) == 0)) {
- /* if we get a UNIX socket as the parameter we will talk
- * to the ivshmem server to receive the memory region */
+static void ivshmem_common_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
- if (s->shmobj != NULL) {
- error_report("WARNING: do not specify both 'chardev' "
- "and 'shm' with ivshmem");
- }
+ k->realize = ivshmem_common_realize;
+ k->exit = ivshmem_exit;
+ k->config_write = ivshmem_write_config;
+ k->vendor_id = PCI_VENDOR_ID_IVSHMEM;
+ k->device_id = PCI_DEVICE_ID_IVSHMEM;
+ k->class_id = PCI_CLASS_MEMORY_RAM;
+ k->revision = 1;
+ dc->reset = ivshmem_reset;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ dc->desc = "Inter-VM shared memory";
+}
- IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n",
- s->server_chr->filename);
+static const TypeInfo ivshmem_common_info = {
+ .name = TYPE_IVSHMEM_COMMON,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(IVShmemState),
+ .abstract = true,
+ .class_init = ivshmem_common_class_init,
+};
- if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
- ivshmem_setup_msi(s);
- }
+static void ivshmem_check_memdev_is_busy(Object *obj, const char *name,
+ Object *val, Error **errp)
+{
+ MemoryRegion *mr;
- /* we allocate enough space for 16 guests and grow as needed */
- s->nb_peers = 16;
- s->vm_id = -1;
+ mr = host_memory_backend_get_memory(MEMORY_BACKEND(val), &error_abort);
+ if (memory_region_is_mapped(mr)) {
+ char *path = object_get_canonical_path_component(val);
+ error_setg(errp, "can't use already busy memdev: %s", path);
+ g_free(path);
+ } else {
+ qdev_prop_allow_set_link_before_realize(obj, name, val, errp);
+ }
+}
+
+static const VMStateDescription ivshmem_plain_vmsd = {
+ .name = TYPE_IVSHMEM_PLAIN,
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .pre_load = ivshmem_pre_load,
+ .post_load = ivshmem_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(parent_obj, IVShmemState),
+ VMSTATE_UINT32(intrstatus, IVShmemState),
+ VMSTATE_UINT32(intrmask, IVShmemState),
+ VMSTATE_END_OF_LIST()
+ },
+};
- /* allocate/initialize space for interrupt handling */
- s->peers = g_malloc0(s->nb_peers * sizeof(Peer));
+static Property ivshmem_plain_properties[] = {
+ DEFINE_PROP_ON_OFF_AUTO("master", IVShmemState, master, ON_OFF_AUTO_OFF),
+ DEFINE_PROP_END_OF_LIST(),
+};
- pci_register_bar(dev, 2, s->ivshmem_attr, &s->bar);
+static void ivshmem_plain_init(Object *obj)
+{
+ IVShmemState *s = IVSHMEM_PLAIN(obj);
- s->eventfd_chr = g_malloc0(s->vectors * sizeof(CharDriverState *));
+ object_property_add_link(obj, "memdev", TYPE_MEMORY_BACKEND,
+ (Object **)&s->hostmem,
+ ivshmem_check_memdev_is_busy,
+ OBJ_PROP_LINK_UNREF_ON_RELEASE,
+ &error_abort);
+}
- qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive, ivshmem_read,
- ivshmem_event, s);
- } else {
- /* just map the file immediately, we're not using a server */
- int fd;
+static void ivshmem_plain_realize(PCIDevice *dev, Error **errp)
+{
+ IVShmemState *s = IVSHMEM_COMMON(dev);
- if (s->shmobj == NULL) {
- error_report("Must specify 'chardev' or 'shm' to ivshmem");
- exit(1);
- }
+ if (!s->hostmem) {
+ error_setg(errp, "You must specify a 'memdev'");
+ return;
+ }
- IVSHMEM_DPRINTF("using shm_open (shm object = %s)\n", s->shmobj);
+ ivshmem_common_realize(dev, errp);
+}
- /* try opening with O_EXCL and if it succeeds zero the memory
- * by truncating to 0 */
- if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR|O_EXCL,
- S_IRWXU|S_IRWXG|S_IRWXO)) > 0) {
- /* truncate file to length PCI device's memory */
- if (ftruncate(fd, s->ivshmem_size) != 0) {
- error_report("could not truncate shared file");
- }
+static void ivshmem_plain_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
- } else if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR,
- S_IRWXU|S_IRWXG|S_IRWXO)) < 0) {
- error_report("could not open shared file");
- exit(1);
+ k->realize = ivshmem_plain_realize;
+ dc->props = ivshmem_plain_properties;
+ dc->vmsd = &ivshmem_plain_vmsd;
+}
- }
+static const TypeInfo ivshmem_plain_info = {
+ .name = TYPE_IVSHMEM_PLAIN,
+ .parent = TYPE_IVSHMEM_COMMON,
+ .instance_size = sizeof(IVShmemState),
+ .instance_init = ivshmem_plain_init,
+ .class_init = ivshmem_plain_class_init,
+};
- if (check_shm_size(s, fd) == -1) {
- exit(1);
- }
+static const VMStateDescription ivshmem_doorbell_vmsd = {
+ .name = TYPE_IVSHMEM_DOORBELL,
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .pre_load = ivshmem_pre_load,
+ .post_load = ivshmem_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(parent_obj, IVShmemState),
+ VMSTATE_MSIX(parent_obj, IVShmemState),
+ VMSTATE_UINT32(intrstatus, IVShmemState),
+ VMSTATE_UINT32(intrmask, IVShmemState),
+ VMSTATE_END_OF_LIST()
+ },
+};
- create_shared_memory_BAR(s, fd);
+static Property ivshmem_doorbell_properties[] = {
+ DEFINE_PROP_CHR("chardev", IVShmemState, server_chr),
+ DEFINE_PROP_UINT32("vectors", IVShmemState, vectors, 1),
+ DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD,
+ true),
+ DEFINE_PROP_ON_OFF_AUTO("master", IVShmemState, master, ON_OFF_AUTO_OFF),
+ DEFINE_PROP_END_OF_LIST(),
+};
+static void ivshmem_doorbell_init(Object *obj)
+{
+ IVShmemState *s = IVSHMEM_DOORBELL(obj);
+
+ s->features |= (1 << IVSHMEM_MSI);
+ s->legacy_size = SIZE_MAX; /* whatever the server sends */
+}
+
+static void ivshmem_doorbell_realize(PCIDevice *dev, Error **errp)
+{
+ IVShmemState *s = IVSHMEM_COMMON(dev);
+
+ if (!s->server_chr) {
+ error_setg(errp, "You must specify a 'chardev'");
+ return;
}
- dev->config_write = ivshmem_write_config;
+ ivshmem_common_realize(dev, errp);
+}
- return 0;
+static void ivshmem_doorbell_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->realize = ivshmem_doorbell_realize;
+ dc->props = ivshmem_doorbell_properties;
+ dc->vmsd = &ivshmem_doorbell_vmsd;
}
-static void pci_ivshmem_uninit(PCIDevice *dev)
+static const TypeInfo ivshmem_doorbell_info = {
+ .name = TYPE_IVSHMEM_DOORBELL,
+ .parent = TYPE_IVSHMEM_COMMON,
+ .instance_size = sizeof(IVShmemState),
+ .instance_init = ivshmem_doorbell_init,
+ .class_init = ivshmem_doorbell_class_init,
+};
+
+static int ivshmem_load_old(QEMUFile *f, void *opaque, int version_id)
{
- IVShmemState *s = IVSHMEM(dev);
+ IVShmemState *s = opaque;
+ PCIDevice *pdev = PCI_DEVICE(s);
+ int ret;
- if (s->migration_blocker) {
- migrate_del_blocker(s->migration_blocker);
- error_free(s->migration_blocker);
+ IVSHMEM_DPRINTF("ivshmem_load_old\n");
+
+ if (version_id != 0) {
+ return -EINVAL;
+ }
+
+ ret = ivshmem_pre_load(s);
+ if (ret) {
+ return ret;
+ }
+
+ ret = pci_device_load(pdev, f);
+ if (ret) {
+ return ret;
+ }
+
+ if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
+ msix_load(pdev, f);
+ ivshmem_msix_vector_use(s);
+ } else {
+ s->intrstatus = qemu_get_be32(f);
+ s->intrmask = qemu_get_be32(f);
}
- memory_region_del_subregion(&s->bar, &s->ivshmem);
- vmstate_unregister_ram(&s->ivshmem, DEVICE(dev));
- unregister_savevm(DEVICE(dev), "ivshmem", s);
- fifo8_destroy(&s->incoming_fifo);
+ return 0;
}
+static bool test_msix(void *opaque, int version_id)
+{
+ IVShmemState *s = opaque;
+
+ return ivshmem_has_feature(s, IVSHMEM_MSI);
+}
+
+static bool test_no_msix(void *opaque, int version_id)
+{
+ return !test_msix(opaque, version_id);
+}
+
+static const VMStateDescription ivshmem_vmsd = {
+ .name = "ivshmem",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .pre_load = ivshmem_pre_load,
+ .post_load = ivshmem_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(parent_obj, IVShmemState),
+
+ VMSTATE_MSIX_TEST(parent_obj, IVShmemState, test_msix),
+ VMSTATE_UINT32_TEST(intrstatus, IVShmemState, test_no_msix),
+ VMSTATE_UINT32_TEST(intrmask, IVShmemState, test_no_msix),
+
+ VMSTATE_END_OF_LIST()
+ },
+ .load_state_old = ivshmem_load_old,
+ .minimum_version_id_old = 0
+};
+
static Property ivshmem_properties[] = {
DEFINE_PROP_CHR("chardev", IVShmemState, server_chr),
DEFINE_PROP_STRING("size", IVShmemState, sizearg),
DEFINE_PROP_UINT32("vectors", IVShmemState, vectors, 1),
- DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD, false),
+ DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD,
+ false),
DEFINE_PROP_BIT("msi", IVShmemState, features, IVSHMEM_MSI, true),
DEFINE_PROP_STRING("shm", IVShmemState, shmobj),
DEFINE_PROP_STRING("role", IVShmemState, role),
- DEFINE_PROP_UINT32("use64", IVShmemState, ivshmem_64bit, 1),
+ DEFINE_PROP_UINT32("use64", IVShmemState, not_legacy_32bit, 1),
DEFINE_PROP_END_OF_LIST(),
};
+static void desugar_shm(IVShmemState *s)
+{
+ Object *obj;
+ char *path;
+
+ obj = object_new("memory-backend-file");
+ path = g_strdup_printf("/dev/shm/%s", s->shmobj);
+ object_property_set_str(obj, path, "mem-path", &error_abort);
+ g_free(path);
+ object_property_set_int(obj, s->legacy_size, "size", &error_abort);
+ object_property_set_bool(obj, true, "share", &error_abort);
+ object_property_add_child(OBJECT(s), "internal-shm-backend", obj,
+ &error_abort);
+ user_creatable_complete(obj, &error_abort);
+ s->hostmem = MEMORY_BACKEND(obj);
+}
+
+static void ivshmem_realize(PCIDevice *dev, Error **errp)
+{
+ IVShmemState *s = IVSHMEM_COMMON(dev);
+
+ if (!qtest_enabled()) {
+ error_report("ivshmem is deprecated, please use ivshmem-plain"
+ " or ivshmem-doorbell instead");
+ }
+
+ if (!!s->server_chr + !!s->shmobj != 1) {
+ error_setg(errp, "You must specify either 'shm' or 'chardev'");
+ return;
+ }
+
+ if (s->sizearg == NULL) {
+ s->legacy_size = 4 << 20; /* 4 MB default */
+ } else {
+ char *end;
+ int64_t size = qemu_strtosz(s->sizearg, &end);
+ if (size < 0 || (size_t)size != size || *end != '\0'
+ || !is_power_of_2(size)) {
+ error_setg(errp, "Invalid size %s", s->sizearg);
+ return;
+ }
+ s->legacy_size = size;
+ }
+
+ /* check that role is reasonable */
+ if (s->role) {
+ if (strncmp(s->role, "peer", 5) == 0) {
+ s->master = ON_OFF_AUTO_OFF;
+ } else if (strncmp(s->role, "master", 7) == 0) {
+ s->master = ON_OFF_AUTO_ON;
+ } else {
+ error_setg(errp, "'role' must be 'peer' or 'master'");
+ return;
+ }
+ } else {
+ s->master = ON_OFF_AUTO_AUTO;
+ }
+
+ if (s->shmobj) {
+ desugar_shm(s);
+ }
+
+ /*
+ * Note: we don't use INTx with IVSHMEM_MSI at all, so this is a
+ * bald-faced lie then. But it's a backwards compatible lie.
+ */
+ pci_config_set_interrupt_pin(dev->config, 1);
+
+ ivshmem_common_realize(dev, errp);
+}
+
static void ivshmem_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
- k->init = pci_ivshmem_init;
- k->exit = pci_ivshmem_uninit;
- k->vendor_id = PCI_VENDOR_ID_IVSHMEM;
- k->device_id = PCI_DEVICE_ID_IVSHMEM;
- k->class_id = PCI_CLASS_MEMORY_RAM;
- dc->reset = ivshmem_reset;
+ k->realize = ivshmem_realize;
+ k->revision = 0;
+ dc->desc = "Inter-VM shared memory (legacy)";
dc->props = ivshmem_properties;
- set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ dc->vmsd = &ivshmem_vmsd;
}
static const TypeInfo ivshmem_info = {
.name = TYPE_IVSHMEM,
- .parent = TYPE_PCI_DEVICE,
+ .parent = TYPE_IVSHMEM_COMMON,
.instance_size = sizeof(IVShmemState),
.class_init = ivshmem_class_init,
};
static void ivshmem_register_types(void)
{
+ type_register_static(&ivshmem_common_info);
+ type_register_static(&ivshmem_plain_info);
+ type_register_static(&ivshmem_doorbell_info);
type_register_static(&ivshmem_info);
}
diff --git a/qemu/hw/misc/macio/cuda.c b/qemu/hw/misc/macio/cuda.c
index f3984e3a2..f15f30110 100644
--- a/qemu/hw/misc/macio/cuda.c
+++ b/qemu/hw/misc/macio/cuda.c
@@ -22,11 +22,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/ppc/mac.h"
#include "hw/input/adb.h"
#include "qemu/timer.h"
#include "sysemu/sysemu.h"
+#include "qemu/cutils.h"
/* XXX: implement all timer modes */
@@ -57,6 +59,8 @@
#define IER_SET 0x80 /* set bits in IER */
#define IER_CLR 0 /* clear bits in IER */
#define SR_INT 0x04 /* Shift register full/empty */
+#define SR_DATA_INT 0x08
+#define SR_CLOCK_INT 0x10
#define T1_INT 0x40 /* Timer 1 interrupt */
#define T2_INT 0x20 /* Timer 2 interrupt */
@@ -103,11 +107,28 @@
#define CUDA_COMBINED_FORMAT_IIC 0x25
#define CUDA_TIMER_FREQ (4700000 / 6)
-#define CUDA_ADB_POLL_FREQ 50
/* CUDA returns time_t's offset from Jan 1, 1904, not 1970 */
#define RTC_OFFSET 2082844800
+/* CUDA registers */
+#define CUDA_REG_B 0x00
+#define CUDA_REG_A 0x01
+#define CUDA_REG_DIRB 0x02
+#define CUDA_REG_DIRA 0x03
+#define CUDA_REG_T1CL 0x04
+#define CUDA_REG_T1CH 0x05
+#define CUDA_REG_T1LL 0x06
+#define CUDA_REG_T1LH 0x07
+#define CUDA_REG_T2CL 0x08
+#define CUDA_REG_T2CH 0x09
+#define CUDA_REG_SR 0x0a
+#define CUDA_REG_ACR 0x0b
+#define CUDA_REG_PCR 0x0c
+#define CUDA_REG_IFR 0x0d
+#define CUDA_REG_IER 0x0e
+#define CUDA_REG_ANH 0x0f
+
static void cuda_update(CUDAState *s);
static void cuda_receive_packet_from_host(CUDAState *s,
const uint8_t *data, int len);
@@ -116,47 +137,48 @@ static void cuda_timer_update(CUDAState *s, CUDATimer *ti,
static void cuda_update_irq(CUDAState *s)
{
- if (s->ifr & s->ier & (SR_INT | T1_INT)) {
+ if (s->ifr & s->ier & (SR_INT | T1_INT | T2_INT)) {
qemu_irq_raise(s->irq);
} else {
qemu_irq_lower(s->irq);
}
}
-static uint64_t get_tb(uint64_t freq)
+static uint64_t get_tb(uint64_t time, uint64_t freq)
{
- return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
- freq, get_ticks_per_sec());
+ return muldiv64(time, freq, NANOSECONDS_PER_SECOND);
}
-static unsigned int get_counter(CUDATimer *s)
+static unsigned int get_counter(CUDATimer *ti)
{
int64_t d;
unsigned int counter;
uint64_t tb_diff;
+ uint64_t current_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
/* Reverse of the tb calculation algorithm that Mac OS X uses on bootup. */
- tb_diff = get_tb(s->frequency) - s->load_time;
- d = (tb_diff * 0xBF401675E5DULL) / (s->frequency << 24);
+ tb_diff = get_tb(current_time, ti->frequency) - ti->load_time;
+ d = (tb_diff * 0xBF401675E5DULL) / (ti->frequency << 24);
- if (s->index == 0) {
+ if (ti->index == 0) {
/* the timer goes down from latch to -1 (period of latch + 2) */
- if (d <= (s->counter_value + 1)) {
- counter = (s->counter_value - d) & 0xffff;
+ if (d <= (ti->counter_value + 1)) {
+ counter = (ti->counter_value - d) & 0xffff;
} else {
- counter = (d - (s->counter_value + 1)) % (s->latch + 2);
- counter = (s->latch - counter) & 0xffff;
+ counter = (d - (ti->counter_value + 1)) % (ti->latch + 2);
+ counter = (ti->latch - counter) & 0xffff;
}
} else {
- counter = (s->counter_value - d) & 0xffff;
+ counter = (ti->counter_value - d) & 0xffff;
}
return counter;
}
static void set_counter(CUDAState *s, CUDATimer *ti, unsigned int val)
{
- CUDA_DPRINTF("T%d.counter=%d\n", 1 + (ti->timer == NULL), val);
- ti->load_time = get_tb(s->frequency);
+ CUDA_DPRINTF("T%d.counter=%d\n", 1 + ti->index, val);
+ ti->load_time = get_tb(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
+ s->frequency);
ti->counter_value = val;
cuda_timer_update(s, ti, ti->load_time);
}
@@ -168,7 +190,7 @@ static int64_t get_next_irq_time(CUDATimer *s, int64_t current_time)
/* current counter value */
d = muldiv64(current_time - s->load_time,
- CUDA_TIMER_FREQ, get_ticks_per_sec());
+ CUDA_TIMER_FREQ, NANOSECONDS_PER_SECOND);
/* the timer goes down from latch to -1 (period of latch + 2) */
if (d <= (s->counter_value + 1)) {
counter = (s->counter_value - d) & 0xffff;
@@ -187,7 +209,7 @@ static int64_t get_next_irq_time(CUDATimer *s, int64_t current_time)
}
CUDA_DPRINTF("latch=%d counter=%" PRId64 " delta_next=%" PRId64 "\n",
s->latch, d, next_time - d);
- next_time = muldiv64(next_time, get_ticks_per_sec(), CUDA_TIMER_FREQ) +
+ next_time = muldiv64(next_time, NANOSECONDS_PER_SECOND, CUDA_TIMER_FREQ) +
s->load_time;
if (next_time <= current_time)
next_time = current_time + 1;
@@ -199,7 +221,7 @@ static void cuda_timer_update(CUDAState *s, CUDATimer *ti,
{
if (!ti->timer)
return;
- if ((s->acr & T1MODE) != T1MODE_CONT) {
+ if (ti->index == 0 && (s->acr & T1MODE) != T1MODE_CONT) {
timer_del(ti->timer);
} else {
ti->next_irq_time = get_next_irq_time(ti, current_time);
@@ -217,6 +239,41 @@ static void cuda_timer1(void *opaque)
cuda_update_irq(s);
}
+static void cuda_timer2(void *opaque)
+{
+ CUDAState *s = opaque;
+ CUDATimer *ti = &s->timers[1];
+
+ cuda_timer_update(s, ti, ti->next_irq_time);
+ s->ifr |= T2_INT;
+ cuda_update_irq(s);
+}
+
+static void cuda_set_sr_int(void *opaque)
+{
+ CUDAState *s = opaque;
+
+ CUDA_DPRINTF("CUDA: %s:%d\n", __func__, __LINE__);
+ s->ifr |= SR_INT;
+ cuda_update_irq(s);
+}
+
+static void cuda_delay_set_sr_int(CUDAState *s)
+{
+ int64_t expire;
+
+ if (s->dirb == 0xff) {
+ /* Not in Mac OS, fire the IRQ directly */
+ cuda_set_sr_int(s);
+ return;
+ }
+
+ CUDA_DPRINTF("CUDA: %s:%d\n", __func__, __LINE__);
+
+ expire = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 300 * SCALE_US;
+ timer_mod(s->sr_delay_timer, expire);
+}
+
static uint32_t cuda_readb(void *opaque, hwaddr addr)
{
CUDAState *s = opaque;
@@ -224,66 +281,68 @@ static uint32_t cuda_readb(void *opaque, hwaddr addr)
addr = (addr >> 9) & 0xf;
switch(addr) {
- case 0:
+ case CUDA_REG_B:
val = s->b;
break;
- case 1:
+ case CUDA_REG_A:
val = s->a;
break;
- case 2:
+ case CUDA_REG_DIRB:
val = s->dirb;
break;
- case 3:
+ case CUDA_REG_DIRA:
val = s->dira;
break;
- case 4:
+ case CUDA_REG_T1CL:
val = get_counter(&s->timers[0]) & 0xff;
s->ifr &= ~T1_INT;
cuda_update_irq(s);
break;
- case 5:
+ case CUDA_REG_T1CH:
val = get_counter(&s->timers[0]) >> 8;
cuda_update_irq(s);
break;
- case 6:
+ case CUDA_REG_T1LL:
val = s->timers[0].latch & 0xff;
break;
- case 7:
+ case CUDA_REG_T1LH:
/* XXX: check this */
val = (s->timers[0].latch >> 8) & 0xff;
break;
- case 8:
+ case CUDA_REG_T2CL:
val = get_counter(&s->timers[1]) & 0xff;
s->ifr &= ~T2_INT;
+ cuda_update_irq(s);
break;
- case 9:
+ case CUDA_REG_T2CH:
val = get_counter(&s->timers[1]) >> 8;
break;
- case 10:
+ case CUDA_REG_SR:
val = s->sr;
- s->ifr &= ~SR_INT;
+ s->ifr &= ~(SR_INT | SR_CLOCK_INT | SR_DATA_INT);
cuda_update_irq(s);
break;
- case 11:
+ case CUDA_REG_ACR:
val = s->acr;
break;
- case 12:
+ case CUDA_REG_PCR:
val = s->pcr;
break;
- case 13:
+ case CUDA_REG_IFR:
val = s->ifr;
- if (s->ifr & s->ier)
+ if (s->ifr & s->ier) {
val |= 0x80;
+ }
break;
- case 14:
+ case CUDA_REG_IER:
val = s->ier | 0x80;
break;
default:
- case 15:
+ case CUDA_REG_ANH:
val = s->anh;
break;
}
- if (addr != 13 || val != 0) {
+ if (addr != CUDA_REG_IFR || val != 0) {
CUDA_DPRINTF("read: reg=0x%x val=%02x\n", (int)addr, val);
}
@@ -298,61 +357,65 @@ static void cuda_writeb(void *opaque, hwaddr addr, uint32_t val)
CUDA_DPRINTF("write: reg=0x%x val=%02x\n", (int)addr, val);
switch(addr) {
- case 0:
+ case CUDA_REG_B:
s->b = val;
cuda_update(s);
break;
- case 1:
+ case CUDA_REG_A:
s->a = val;
break;
- case 2:
+ case CUDA_REG_DIRB:
s->dirb = val;
break;
- case 3:
+ case CUDA_REG_DIRA:
s->dira = val;
break;
- case 4:
+ case CUDA_REG_T1CL:
s->timers[0].latch = (s->timers[0].latch & 0xff00) | val;
cuda_timer_update(s, &s->timers[0], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
break;
- case 5:
+ case CUDA_REG_T1CH:
s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8);
s->ifr &= ~T1_INT;
set_counter(s, &s->timers[0], s->timers[0].latch);
break;
- case 6:
+ case CUDA_REG_T1LL:
s->timers[0].latch = (s->timers[0].latch & 0xff00) | val;
cuda_timer_update(s, &s->timers[0], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
break;
- case 7:
+ case CUDA_REG_T1LH:
s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8);
s->ifr &= ~T1_INT;
cuda_timer_update(s, &s->timers[0], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
break;
- case 8:
- s->timers[1].latch = val;
- set_counter(s, &s->timers[1], val);
+ case CUDA_REG_T2CL:
+ s->timers[1].latch = (s->timers[1].latch & 0xff00) | val;
break;
- case 9:
- set_counter(s, &s->timers[1], (val << 8) | s->timers[1].latch);
+ case CUDA_REG_T2CH:
+ /* To ensure T2 generates an interrupt on zero crossing with the
+ common timer code, write the value directly from the latch to
+ the counter */
+ s->timers[1].latch = (s->timers[1].latch & 0xff) | (val << 8);
+ s->ifr &= ~T2_INT;
+ set_counter(s, &s->timers[1], s->timers[1].latch);
break;
- case 10:
+ case CUDA_REG_SR:
s->sr = val;
break;
- case 11:
+ case CUDA_REG_ACR:
s->acr = val;
cuda_timer_update(s, &s->timers[0], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
cuda_update(s);
break;
- case 12:
+ case CUDA_REG_PCR:
s->pcr = val;
break;
- case 13:
+ case CUDA_REG_IFR:
/* reset bits */
s->ifr &= ~val;
cuda_update_irq(s);
break;
- case 14:
+ case CUDA_REG_IER:
if (val & IER_SET) {
/* set bits */
s->ier |= val & 0x7f;
@@ -363,7 +426,7 @@ static void cuda_writeb(void *opaque, hwaddr addr, uint32_t val)
cuda_update_irq(s);
break;
default:
- case 15:
+ case CUDA_REG_ANH:
s->anh = val;
break;
}
@@ -384,8 +447,7 @@ static void cuda_update(CUDAState *s)
if (s->data_out_index < sizeof(s->data_out)) {
CUDA_DPRINTF("send: %02x\n", s->sr);
s->data_out[s->data_out_index++] = s->sr;
- s->ifr |= SR_INT;
- cuda_update_irq(s);
+ cuda_delay_set_sr_int(s);
}
}
} else {
@@ -398,8 +460,7 @@ static void cuda_update(CUDAState *s)
if (s->data_in_index >= s->data_in_size) {
s->b = (s->b | TREQ);
}
- s->ifr |= SR_INT;
- cuda_update_irq(s);
+ cuda_delay_set_sr_int(s);
}
}
}
@@ -411,15 +472,13 @@ static void cuda_update(CUDAState *s)
s->b = (s->b | TREQ);
else
s->b = (s->b & ~TREQ);
- s->ifr |= SR_INT;
- cuda_update_irq(s);
+ cuda_delay_set_sr_int(s);
} else {
if (!(s->last_b & TIP)) {
/* handle end of host to cuda transfer */
packet_received = (s->data_out_index > 0);
/* always an IRQ at the end of transfer */
- s->ifr |= SR_INT;
- cuda_update_irq(s);
+ cuda_delay_set_sr_int(s);
}
/* signal if there is data to read */
if (s->data_in_index < s->data_in_size) {
@@ -456,8 +515,7 @@ static void cuda_send_packet_to_host(CUDAState *s,
s->data_in_size = len;
s->data_in_index = 0;
cuda_update(s);
- s->ifr |= SR_INT;
- cuda_update_irq(s);
+ cuda_delay_set_sr_int(s);
}
static void cuda_adb_poll(void *opaque)
@@ -466,7 +524,7 @@ static void cuda_adb_poll(void *opaque)
uint8_t obuf[ADB_MAX_OUT_LEN + 2];
int olen;
- olen = adb_poll(&s->adb_bus, obuf + 2);
+ olen = adb_poll(&s->adb_bus, obuf + 2, s->adb_poll_mask);
if (olen > 0) {
obuf[0] = ADB_PACKET;
obuf[1] = 0x40; /* polled data */
@@ -474,75 +532,213 @@ static void cuda_adb_poll(void *opaque)
}
timer_mod(s->adb_poll_timer,
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
- (get_ticks_per_sec() / CUDA_ADB_POLL_FREQ));
+ (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms)));
}
-static void cuda_receive_packet(CUDAState *s,
- const uint8_t *data, int len)
+/* description of commands */
+typedef struct CudaCommand {
+ uint8_t command;
+ const char *name;
+ bool (*handler)(CUDAState *s,
+ const uint8_t *in_args, int in_len,
+ uint8_t *out_args, int *out_len);
+} CudaCommand;
+
+static bool cuda_cmd_autopoll(CUDAState *s,
+ const uint8_t *in_data, int in_len,
+ uint8_t *out_data, int *out_len)
{
- uint8_t obuf[16];
int autopoll;
+
+ if (in_len != 1) {
+ return false;
+ }
+
+ autopoll = (in_data[0] != 0);
+ if (autopoll != s->autopoll) {
+ s->autopoll = autopoll;
+ if (autopoll) {
+ timer_mod(s->adb_poll_timer,
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms)));
+ } else {
+ timer_del(s->adb_poll_timer);
+ }
+ }
+ return true;
+}
+
+static bool cuda_cmd_set_autorate(CUDAState *s,
+ const uint8_t *in_data, int in_len,
+ uint8_t *out_data, int *out_len)
+{
+ if (in_len != 1) {
+ return false;
+ }
+
+ /* we don't want a period of 0 ms */
+ /* FIXME: check what real hardware does */
+ if (in_data[0] == 0) {
+ return false;
+ }
+
+ s->autopoll_rate_ms = in_data[0];
+ if (s->autopoll) {
+ timer_mod(s->adb_poll_timer,
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms)));
+ }
+ return true;
+}
+
+static bool cuda_cmd_set_device_list(CUDAState *s,
+ const uint8_t *in_data, int in_len,
+ uint8_t *out_data, int *out_len)
+{
+ if (in_len != 2) {
+ return false;
+ }
+
+ s->adb_poll_mask = (((uint16_t)in_data[0]) << 8) | in_data[1];
+ return true;
+}
+
+static bool cuda_cmd_powerdown(CUDAState *s,
+ const uint8_t *in_data, int in_len,
+ uint8_t *out_data, int *out_len)
+{
+ if (in_len != 0) {
+ return false;
+ }
+
+ qemu_system_shutdown_request();
+ return true;
+}
+
+static bool cuda_cmd_reset_system(CUDAState *s,
+ const uint8_t *in_data, int in_len,
+ uint8_t *out_data, int *out_len)
+{
+ if (in_len != 0) {
+ return false;
+ }
+
+ qemu_system_reset_request();
+ return true;
+}
+
+static bool cuda_cmd_set_file_server_flag(CUDAState *s,
+ const uint8_t *in_data, int in_len,
+ uint8_t *out_data, int *out_len)
+{
+ if (in_len != 1) {
+ return false;
+ }
+
+ qemu_log_mask(LOG_UNIMP,
+ "CUDA: unimplemented command FILE_SERVER_FLAG %d\n",
+ in_data[0]);
+ return true;
+}
+
+static bool cuda_cmd_set_power_message(CUDAState *s,
+ const uint8_t *in_data, int in_len,
+ uint8_t *out_data, int *out_len)
+{
+ if (in_len != 1) {
+ return false;
+ }
+
+ qemu_log_mask(LOG_UNIMP,
+ "CUDA: unimplemented command SET_POWER_MESSAGE %d\n",
+ in_data[0]);
+ return true;
+}
+
+static bool cuda_cmd_get_time(CUDAState *s,
+ const uint8_t *in_data, int in_len,
+ uint8_t *out_data, int *out_len)
+{
uint32_t ti;
- switch(data[0]) {
- case CUDA_AUTOPOLL:
- autopoll = (data[1] != 0);
- if (autopoll != s->autopoll) {
- s->autopoll = autopoll;
- if (autopoll) {
- timer_mod(s->adb_poll_timer,
- qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
- (get_ticks_per_sec() / CUDA_ADB_POLL_FREQ));
+ if (in_len != 0) {
+ return false;
+ }
+
+ ti = s->tick_offset + (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)
+ / NANOSECONDS_PER_SECOND);
+ out_data[0] = ti >> 24;
+ out_data[1] = ti >> 16;
+ out_data[2] = ti >> 8;
+ out_data[3] = ti;
+ *out_len = 4;
+ return true;
+}
+
+static bool cuda_cmd_set_time(CUDAState *s,
+ const uint8_t *in_data, int in_len,
+ uint8_t *out_data, int *out_len)
+{
+ uint32_t ti;
+
+ if (in_len != 4) {
+ return false;
+ }
+
+ ti = (((uint32_t)in_data[0]) << 24) + (((uint32_t)in_data[1]) << 16)
+ + (((uint32_t)in_data[2]) << 8) + in_data[3];
+ s->tick_offset = ti - (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)
+ / NANOSECONDS_PER_SECOND);
+ return true;
+}
+
+static const CudaCommand handlers[] = {
+ { CUDA_AUTOPOLL, "AUTOPOLL", cuda_cmd_autopoll },
+ { CUDA_SET_AUTO_RATE, "SET_AUTO_RATE", cuda_cmd_set_autorate },
+ { CUDA_SET_DEVICE_LIST, "SET_DEVICE_LIST", cuda_cmd_set_device_list },
+ { CUDA_POWERDOWN, "POWERDOWN", cuda_cmd_powerdown },
+ { CUDA_RESET_SYSTEM, "RESET_SYSTEM", cuda_cmd_reset_system },
+ { CUDA_FILE_SERVER_FLAG, "FILE_SERVER_FLAG",
+ cuda_cmd_set_file_server_flag },
+ { CUDA_SET_POWER_MESSAGES, "SET_POWER_MESSAGES",
+ cuda_cmd_set_power_message },
+ { CUDA_GET_TIME, "GET_TIME", cuda_cmd_get_time },
+ { CUDA_SET_TIME, "SET_TIME", cuda_cmd_set_time },
+};
+
+static void cuda_receive_packet(CUDAState *s,
+ const uint8_t *data, int len)
+{
+ uint8_t obuf[16] = { CUDA_PACKET, 0, data[0] };
+ int i, out_len = 0;
+
+ for (i = 0; i < ARRAY_SIZE(handlers); i++) {
+ const CudaCommand *desc = &handlers[i];
+ if (desc->command == data[0]) {
+ CUDA_DPRINTF("handling command %s\n", desc->name);
+ out_len = 0;
+ if (desc->handler(s, data + 1, len - 1, obuf + 3, &out_len)) {
+ cuda_send_packet_to_host(s, obuf, 3 + out_len);
} else {
- timer_del(s->adb_poll_timer);
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "CUDA: %s: wrong parameters %d\n",
+ desc->name, len);
+ obuf[0] = ERROR_PACKET;
+ obuf[1] = 0x5; /* bad parameters */
+ obuf[2] = CUDA_PACKET;
+ obuf[3] = data[0];
+ cuda_send_packet_to_host(s, obuf, 4);
}
+ return;
}
- obuf[0] = CUDA_PACKET;
- obuf[1] = data[1];
- cuda_send_packet_to_host(s, obuf, 2);
- break;
- case CUDA_SET_TIME:
- ti = (((uint32_t)data[1]) << 24) + (((uint32_t)data[2]) << 16) + (((uint32_t)data[3]) << 8) + data[4];
- s->tick_offset = ti - (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / get_ticks_per_sec());
- obuf[0] = CUDA_PACKET;
- obuf[1] = 0;
- obuf[2] = 0;
- cuda_send_packet_to_host(s, obuf, 3);
- break;
- case CUDA_GET_TIME:
- ti = s->tick_offset + (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / get_ticks_per_sec());
- obuf[0] = CUDA_PACKET;
- obuf[1] = 0;
- obuf[2] = 0;
- obuf[3] = ti >> 24;
- obuf[4] = ti >> 16;
- obuf[5] = ti >> 8;
- obuf[6] = ti;
- cuda_send_packet_to_host(s, obuf, 7);
- break;
- case CUDA_FILE_SERVER_FLAG:
- case CUDA_SET_DEVICE_LIST:
- case CUDA_SET_AUTO_RATE:
- case CUDA_SET_POWER_MESSAGES:
- obuf[0] = CUDA_PACKET;
- obuf[1] = 0;
- cuda_send_packet_to_host(s, obuf, 2);
- break;
- case CUDA_POWERDOWN:
- obuf[0] = CUDA_PACKET;
- obuf[1] = 0;
- cuda_send_packet_to_host(s, obuf, 2);
- qemu_system_shutdown_request();
- break;
- case CUDA_RESET_SYSTEM:
- obuf[0] = CUDA_PACKET;
- obuf[1] = 0;
- cuda_send_packet_to_host(s, obuf, 2);
- qemu_system_reset_request();
- break;
- default:
- break;
}
+
+ qemu_log_mask(LOG_GUEST_ERROR, "CUDA: unknown command 0x%02x\n", data[0]);
+ obuf[0] = ERROR_PACKET;
+ obuf[1] = 0x2; /* unknown command */
+ obuf[2] = CUDA_PACKET;
+ obuf[3] = data[0];
+ cuda_send_packet_to_host(s, obuf, 4);
}
static void cuda_receive_packet_from_host(CUDAState *s,
@@ -560,19 +756,21 @@ static void cuda_receive_packet_from_host(CUDAState *s,
switch(data[0]) {
case ADB_PACKET:
{
- uint8_t obuf[ADB_MAX_OUT_LEN + 2];
+ uint8_t obuf[ADB_MAX_OUT_LEN + 3];
int olen;
olen = adb_request(&s->adb_bus, obuf + 2, data + 1, len - 1);
if (olen > 0) {
obuf[0] = ADB_PACKET;
obuf[1] = 0x00;
+ cuda_send_packet_to_host(s, obuf, olen + 2);
} else {
/* error */
obuf[0] = ADB_PACKET;
obuf[1] = -olen;
+ obuf[2] = data[1];
olen = 0;
+ cuda_send_packet_to_host(s, obuf, olen + 3);
}
- cuda_send_packet_to_host(s, obuf, olen + 2);
}
break;
case CUDA_PACKET:
@@ -638,15 +836,17 @@ static const VMStateDescription vmstate_cuda_timer = {
static const VMStateDescription vmstate_cuda = {
.name = "cuda",
- .version_id = 2,
- .minimum_version_id = 2,
+ .version_id = 4,
+ .minimum_version_id = 4,
.fields = (VMStateField[]) {
VMSTATE_UINT8(a, CUDAState),
VMSTATE_UINT8(b, CUDAState),
+ VMSTATE_UINT8(last_b, CUDAState),
VMSTATE_UINT8(dira, CUDAState),
VMSTATE_UINT8(dirb, CUDAState),
VMSTATE_UINT8(sr, CUDAState),
VMSTATE_UINT8(acr, CUDAState),
+ VMSTATE_UINT8(last_acr, CUDAState),
VMSTATE_UINT8(pcr, CUDAState),
VMSTATE_UINT8(ifr, CUDAState),
VMSTATE_UINT8(ier, CUDAState),
@@ -655,12 +855,15 @@ static const VMStateDescription vmstate_cuda = {
VMSTATE_INT32(data_in_index, CUDAState),
VMSTATE_INT32(data_out_index, CUDAState),
VMSTATE_UINT8(autopoll, CUDAState),
+ VMSTATE_UINT8(autopoll_rate_ms, CUDAState),
+ VMSTATE_UINT16(adb_poll_mask, CUDAState),
VMSTATE_BUFFER(data_in, CUDAState),
VMSTATE_BUFFER(data_out, CUDAState),
VMSTATE_UINT32(tick_offset, CUDAState),
VMSTATE_STRUCT_ARRAY(timers, CUDAState, 2, 1,
vmstate_cuda_timer, CUDATimer),
VMSTATE_TIMER_PTR(adb_poll_timer, CUDAState),
+ VMSTATE_TIMER_PTR(sr_delay_timer, CUDAState),
VMSTATE_END_OF_LIST()
}
};
@@ -671,7 +874,7 @@ static void cuda_reset(DeviceState *dev)
s->b = 0;
s->a = 0;
- s->dirb = 0;
+ s->dirb = 0xff;
s->dira = 0;
s->sr = 0;
s->acr = 0;
@@ -688,8 +891,9 @@ static void cuda_reset(DeviceState *dev)
s->timers[0].latch = 0xffff;
set_counter(s, &s->timers[0], 0xffff);
- s->timers[1].latch = 0;
- set_counter(s, &s->timers[1], 0xffff);
+ s->timers[1].latch = 0xffff;
+
+ s->sr_delay_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_set_sr_int, s);
}
static void cuda_realizefn(DeviceState *dev, Error **errp)
@@ -699,12 +903,15 @@ static void cuda_realizefn(DeviceState *dev, Error **errp)
s->timers[0].timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_timer1, s);
s->timers[0].frequency = s->frequency;
- s->timers[1].frequency = s->frequency;
+ s->timers[1].timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_timer2, s);
+ s->timers[1].frequency = (SCALE_US * 6000) / 4700;
qemu_get_timedate(&tm, 0);
s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET;
s->adb_poll_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_adb_poll, s);
+ s->autopoll_rate_ms = 20;
+ s->adb_poll_mask = 0xffff;
}
static void cuda_initfn(Object *obj)
@@ -713,7 +920,7 @@ static void cuda_initfn(Object *obj)
CUDAState *s = CUDA(obj);
int i;
- memory_region_init_io(&s->mem, NULL, &cuda_ops, s, "cuda", 0x2000);
+ memory_region_init_io(&s->mem, obj, &cuda_ops, s, "cuda", 0x2000);
sysbus_init_mmio(d, &s->mem);
sysbus_init_irq(d, &s->irq);
@@ -738,6 +945,7 @@ static void cuda_class_init(ObjectClass *oc, void *data)
dc->reset = cuda_reset;
dc->vmsd = &vmstate_cuda;
dc->props = cuda_properties;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
}
static const TypeInfo cuda_type_info = {
diff --git a/qemu/hw/misc/macio/mac_dbdma.c b/qemu/hw/misc/macio/mac_dbdma.c
index b25e8511b..6051f17db 100644
--- a/qemu/hw/misc/macio/mac_dbdma.c
+++ b/qemu/hw/misc/macio/mac_dbdma.c
@@ -36,6 +36,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/isa/isa.h"
#include "hw/ppc/mac_dbdma.h"
@@ -556,12 +557,13 @@ void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq,
DBDMA_DPRINTF("DBDMA_register_channel 0x%x\n", nchan);
+ assert(rw);
+ assert(flush);
+
ch->irq = irq;
- ch->channel = nchan;
ch->rw = rw;
ch->flush = flush;
ch->io.opaque = opaque;
- ch->io.channel = ch;
}
static void
@@ -590,10 +592,11 @@ dbdma_control_write(DBDMA_channel *ch)
if ((ch->regs[DBDMA_STATUS] & RUN) && !(status & RUN)) {
/* RUN is cleared */
status &= ~(ACTIVE|DEAD);
- if ((status & FLUSH) && ch->flush) {
- ch->flush(&ch->io);
- status &= ~FLUSH;
- }
+ }
+
+ if ((status & FLUSH) && ch->flush) {
+ ch->flush(&ch->io);
+ status &= ~FLUSH;
}
DBDMA_DPRINTF(" status 0x%08x\n", status);
@@ -603,9 +606,6 @@ dbdma_control_write(DBDMA_channel *ch)
if (status & ACTIVE) {
DBDMA_kick(dbdma_from_ch(ch));
}
- if ((status & FLUSH) && ch->flush) {
- ch->flush(&ch->io);
- }
}
static void dbdma_write(void *opaque, hwaddr addr,
@@ -715,20 +715,52 @@ static const MemoryRegionOps dbdma_ops = {
},
};
-static const VMStateDescription vmstate_dbdma_channel = {
- .name = "dbdma_channel",
+static const VMStateDescription vmstate_dbdma_io = {
+ .name = "dbdma_io",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(addr, struct DBDMA_io),
+ VMSTATE_INT32(len, struct DBDMA_io),
+ VMSTATE_INT32(is_last, struct DBDMA_io),
+ VMSTATE_INT32(is_dma_out, struct DBDMA_io),
+ VMSTATE_BOOL(processing, struct DBDMA_io),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_dbdma_cmd = {
+ .name = "dbdma_cmd",
.version_id = 0,
.minimum_version_id = 0,
.fields = (VMStateField[]) {
+ VMSTATE_UINT16(req_count, dbdma_cmd),
+ VMSTATE_UINT16(command, dbdma_cmd),
+ VMSTATE_UINT32(phy_addr, dbdma_cmd),
+ VMSTATE_UINT32(cmd_dep, dbdma_cmd),
+ VMSTATE_UINT16(res_count, dbdma_cmd),
+ VMSTATE_UINT16(xfer_status, dbdma_cmd),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_dbdma_channel = {
+ .name = "dbdma_channel",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(regs, struct DBDMA_channel, DBDMA_REGS),
+ VMSTATE_STRUCT(io, struct DBDMA_channel, 0, vmstate_dbdma_io, DBDMA_io),
+ VMSTATE_STRUCT(current, struct DBDMA_channel, 0, vmstate_dbdma_cmd,
+ dbdma_cmd),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_dbdma = {
.name = "dbdma",
- .version_id = 2,
- .minimum_version_id = 2,
+ .version_id = 3,
+ .minimum_version_id = 3,
.fields = (VMStateField[]) {
VMSTATE_STRUCT_ARRAY(channels, DBDMAState, DBDMA_CHANNELS, 1,
vmstate_dbdma_channel, DBDMA_channel),
@@ -745,6 +777,20 @@ static void dbdma_reset(void *opaque)
memset(s->channels[i].regs, 0, DBDMA_SIZE);
}
+static void dbdma_unassigned_rw(DBDMA_io *io)
+{
+ DBDMA_channel *ch = io->channel;
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: use of unassigned channel %d\n",
+ __func__, ch->channel);
+}
+
+static void dbdma_unassigned_flush(DBDMA_io *io)
+{
+ DBDMA_channel *ch = io->channel;
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: use of unassigned channel %d\n",
+ __func__, ch->channel);
+}
+
void* DBDMA_init (MemoryRegion **dbdma_mem)
{
DBDMAState *s;
@@ -754,7 +800,13 @@ void* DBDMA_init (MemoryRegion **dbdma_mem)
for (i = 0; i < DBDMA_CHANNELS; i++) {
DBDMA_io *io = &s->channels[i].io;
+ DBDMA_channel *ch = &s->channels[i];
qemu_iovec_init(&io->iov, 1);
+
+ ch->rw = dbdma_unassigned_rw;
+ ch->flush = dbdma_unassigned_flush;
+ ch->channel = i;
+ ch->io.channel = ch;
}
memory_region_init_io(&s->mem, NULL, &dbdma_ops, s, "dbdma", 0x1000);
diff --git a/qemu/hw/misc/macio/macio.c b/qemu/hw/misc/macio/macio.c
index e3c0242d4..be03926b9 100644
--- a/qemu/hw/misc/macio/macio.c
+++ b/qemu/hw/misc/macio/macio.c
@@ -22,6 +22,8 @@
* 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/hw.h"
#include "hw/ppc/mac.h"
#include "hw/pci/pci.h"
@@ -105,10 +107,10 @@ static void macio_escc_legacy_setup(MacIOState *macio_state)
0xF0, 0xE0,
};
- memory_region_init(escc_legacy, NULL, "escc-legacy", 256);
+ memory_region_init(escc_legacy, OBJECT(macio_state), "escc-legacy", 256);
for (i = 0; i < ARRAY_SIZE(maps); i += 2) {
MemoryRegion *port = g_new(MemoryRegion, 1);
- memory_region_init_alias(port, NULL, "escc-legacy-port",
+ memory_region_init_alias(port, OBJECT(macio_state), "escc-legacy-port",
macio_state->escc_mem, maps[i+1], 0x2);
memory_region_add_subregion(escc_legacy, maps[i], port);
}
@@ -131,6 +133,10 @@ static void macio_common_realize(PCIDevice *d, Error **errp)
MacIOState *s = MACIO(d);
SysBusDevice *sysbus_dev;
Error *err = NULL;
+ MemoryRegion *dbdma_mem;
+
+ s->dbdma = DBDMA_init(&dbdma_mem);
+ memory_region_add_subregion(&s->bar, 0x08000, dbdma_mem);
object_property_set_bool(OBJECT(&s->cuda), true, "realized", &err);
if (err) {
@@ -248,7 +254,7 @@ static uint64_t timer_read(void *opaque, hwaddr addr, unsigned size)
uint64_t systime = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
uint64_t kltime;
- kltime = muldiv64(systime, 4194300, get_ticks_per_sec() * 4);
+ kltime = muldiv64(systime, 4194300, NANOSECONDS_PER_SECOND * 4);
kltime = muldiv64(kltime, 18432000, 1048575);
switch (addr) {
@@ -328,16 +334,12 @@ static void macio_newworld_init(Object *obj)
static void macio_instance_init(Object *obj)
{
MacIOState *s = MACIO(obj);
- MemoryRegion *dbdma_mem;
- memory_region_init(&s->bar, NULL, "macio", 0x80000);
+ memory_region_init(&s->bar, obj, "macio", 0x80000);
object_initialize(&s->cuda, sizeof(s->cuda), TYPE_CUDA);
qdev_set_parent_bus(DEVICE(&s->cuda), sysbus_get_default());
object_property_add_child(obj, "cuda", OBJECT(&s->cuda), NULL);
-
- s->dbdma = DBDMA_init(&dbdma_mem);
- memory_region_add_subregion(&s->bar, 0x08000, dbdma_mem);
}
static const VMStateDescription vmstate_macio_oldworld = {
@@ -393,6 +395,7 @@ static void macio_class_init(ObjectClass *klass, void *data)
k->vendor_id = PCI_VENDOR_ID_APPLE;
k->class_id = PCI_CLASS_OTHERS << 8;
dc->props = macio_properties;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
}
static const TypeInfo macio_oldworld_type_info = {
diff --git a/qemu/hw/misc/max111x.c b/qemu/hw/misc/max111x.c
index bef3651d6..9014f0f70 100644
--- a/qemu/hw/misc/max111x.c
+++ b/qemu/hw/misc/max111x.c
@@ -10,7 +10,8 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
-#include "hw/ssi.h"
+#include "qemu/osdep.h"
+#include "hw/ssi/ssi.h"
typedef struct {
SSISlave parent_obj;
diff --git a/qemu/hw/misc/milkymist-hpdmc.c b/qemu/hw/misc/milkymist-hpdmc.c
index f5f4c1b34..b97000fc4 100644
--- a/qemu/hw/misc/milkymist-hpdmc.c
+++ b/qemu/hw/misc/milkymist-hpdmc.c
@@ -21,6 +21,7 @@
* http://www.milkymist.org/socdoc/hpdmc.pdf
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "trace.h"
diff --git a/qemu/hw/misc/milkymist-pfpu.c b/qemu/hw/misc/milkymist-pfpu.c
index 08b604f13..57acd7b36 100644
--- a/qemu/hw/misc/milkymist-pfpu.c
+++ b/qemu/hw/misc/milkymist-pfpu.c
@@ -22,6 +22,7 @@
*
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "trace.h"
diff --git a/qemu/hw/misc/mips_cmgcr.c b/qemu/hw/misc/mips_cmgcr.c
new file mode 100644
index 000000000..37be23995
--- /dev/null
+++ b/qemu/hw/misc/mips_cmgcr.c
@@ -0,0 +1,160 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved.
+ * Authors: Sanjay Lal <sanjayl@kymasys.com>
+ *
+ * Copyright (C) 2015 Imagination Technologies
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "sysemu/sysemu.h"
+#include "hw/misc/mips_cmgcr.h"
+#include "hw/misc/mips_cpc.h"
+
+static inline bool is_cpc_connected(MIPSGCRState *s)
+{
+ return s->cpc_mr != NULL;
+}
+
+static inline void update_cpc_base(MIPSGCRState *gcr, uint64_t val)
+{
+ if (is_cpc_connected(gcr)) {
+ gcr->cpc_base = val & GCR_CPC_BASE_MSK;
+ memory_region_transaction_begin();
+ memory_region_set_address(gcr->cpc_mr,
+ gcr->cpc_base & GCR_CPC_BASE_CPCBASE_MSK);
+ memory_region_set_enabled(gcr->cpc_mr,
+ gcr->cpc_base & GCR_CPC_BASE_CPCEN_MSK);
+ memory_region_transaction_commit();
+ }
+}
+
+/* Read GCR registers */
+static uint64_t gcr_read(void *opaque, hwaddr addr, unsigned size)
+{
+ MIPSGCRState *gcr = (MIPSGCRState *) opaque;
+
+ switch (addr) {
+ /* Global Control Block Register */
+ case GCR_CONFIG_OFS:
+ /* Set PCORES to 0 */
+ return 0;
+ case GCR_BASE_OFS:
+ return gcr->gcr_base;
+ case GCR_REV_OFS:
+ return gcr->gcr_rev;
+ case GCR_CPC_BASE_OFS:
+ return gcr->cpc_base;
+ case GCR_CPC_STATUS_OFS:
+ return is_cpc_connected(gcr);
+ case GCR_L2_CONFIG_OFS:
+ /* L2 BYPASS */
+ return GCR_L2_CONFIG_BYPASS_MSK;
+ /* Core-Local and Core-Other Control Blocks */
+ case MIPS_CLCB_OFS + GCR_CL_CONFIG_OFS:
+ case MIPS_COCB_OFS + GCR_CL_CONFIG_OFS:
+ /* Set PVP to # of VPs - 1 */
+ return gcr->num_vps - 1;
+ case MIPS_CLCB_OFS + GCR_CL_OTHER_OFS:
+ return 0;
+ default:
+ qemu_log_mask(LOG_UNIMP, "Read %d bytes at GCR offset 0x%" HWADDR_PRIx
+ "\n", size, addr);
+ return 0;
+ }
+ return 0;
+}
+
+/* Write GCR registers */
+static void gcr_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
+{
+ MIPSGCRState *gcr = (MIPSGCRState *)opaque;
+
+ switch (addr) {
+ case GCR_CPC_BASE_OFS:
+ update_cpc_base(gcr, data);
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "Write %d bytes at GCR offset 0x%" HWADDR_PRIx
+ " 0x%" PRIx64 "\n", size, addr, data);
+ break;
+ }
+}
+
+static const MemoryRegionOps gcr_ops = {
+ .read = gcr_read,
+ .write = gcr_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {
+ .max_access_size = 8,
+ },
+};
+
+static void mips_gcr_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ MIPSGCRState *s = MIPS_GCR(obj);
+
+ object_property_add_link(obj, "cpc", TYPE_MEMORY_REGION,
+ (Object **)&s->cpc_mr,
+ qdev_prop_allow_set_link_before_realize,
+ OBJ_PROP_LINK_UNREF_ON_RELEASE,
+ &error_abort);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &gcr_ops, s,
+ "mips-gcr", GCR_ADDRSPACE_SZ);
+ sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static void mips_gcr_reset(DeviceState *dev)
+{
+ MIPSGCRState *s = MIPS_GCR(dev);
+
+ update_cpc_base(s, 0);
+}
+
+static const VMStateDescription vmstate_mips_gcr = {
+ .name = "mips-gcr",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(cpc_base, MIPSGCRState),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static Property mips_gcr_properties[] = {
+ DEFINE_PROP_INT32("num-vp", MIPSGCRState, num_vps, 1),
+ DEFINE_PROP_INT32("gcr-rev", MIPSGCRState, gcr_rev, 0x800),
+ DEFINE_PROP_UINT64("gcr-base", MIPSGCRState, gcr_base, GCR_BASE_ADDR),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void mips_gcr_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ dc->props = mips_gcr_properties;
+ dc->vmsd = &vmstate_mips_gcr;
+ dc->reset = mips_gcr_reset;
+}
+
+static const TypeInfo mips_gcr_info = {
+ .name = TYPE_MIPS_GCR,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(MIPSGCRState),
+ .instance_init = mips_gcr_init,
+ .class_init = mips_gcr_class_init,
+};
+
+static void mips_gcr_register_types(void)
+{
+ type_register_static(&mips_gcr_info);
+}
+
+type_init(mips_gcr_register_types)
diff --git a/qemu/hw/misc/mips_cpc.c b/qemu/hw/misc/mips_cpc.c
new file mode 100644
index 000000000..d2b8e42da
--- /dev/null
+++ b/qemu/hw/misc/mips_cpc.c
@@ -0,0 +1,177 @@
+/*
+ * Cluster Power Controller emulation
+ *
+ * Copyright (c) 2016 Imagination Technologies
+ *
+ * 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 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/sysbus.h"
+
+#include "hw/misc/mips_cpc.h"
+
+static inline uint64_t cpc_vp_run_mask(MIPSCPCState *cpc)
+{
+ return (1ULL << cpc->num_vp) - 1;
+}
+
+static void cpc_run_vp(MIPSCPCState *cpc, uint64_t vp_run)
+{
+ CPUState *cs = first_cpu;
+
+ CPU_FOREACH(cs) {
+ uint64_t i = 1ULL << cs->cpu_index;
+ if (i & vp_run & ~cpc->vp_running) {
+ cpu_interrupt(cs, CPU_INTERRUPT_WAKE);
+ cpc->vp_running |= i;
+ }
+ }
+}
+
+static void cpc_stop_vp(MIPSCPCState *cpc, uint64_t vp_stop)
+{
+ CPUState *cs = first_cpu;
+
+ CPU_FOREACH(cs) {
+ uint64_t i = 1ULL << cs->cpu_index;
+ if (i & vp_stop & cpc->vp_running) {
+ cs->halted = 1;
+ cpu_reset_interrupt(cs, CPU_INTERRUPT_WAKE);
+ cpc->vp_running &= ~i;
+ }
+ }
+}
+
+static void cpc_write(void *opaque, hwaddr offset, uint64_t data,
+ unsigned size)
+{
+ MIPSCPCState *s = opaque;
+
+ switch (offset) {
+ case CPC_CL_BASE_OFS + CPC_VP_RUN_OFS:
+ case CPC_CO_BASE_OFS + CPC_VP_RUN_OFS:
+ cpc_run_vp(s, data & cpc_vp_run_mask(s));
+ break;
+ case CPC_CL_BASE_OFS + CPC_VP_STOP_OFS:
+ case CPC_CO_BASE_OFS + CPC_VP_STOP_OFS:
+ cpc_stop_vp(s, data & cpc_vp_run_mask(s));
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Bad offset 0x%x\n", __func__, (int)offset);
+ break;
+ }
+
+ return;
+}
+
+static uint64_t cpc_read(void *opaque, hwaddr offset, unsigned size)
+{
+ MIPSCPCState *s = opaque;
+
+ switch (offset) {
+ case CPC_CL_BASE_OFS + CPC_VP_RUNNING_OFS:
+ case CPC_CO_BASE_OFS + CPC_VP_RUNNING_OFS:
+ return s->vp_running;
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Bad offset 0x%x\n", __func__, (int)offset);
+ return 0;
+ }
+}
+
+static const MemoryRegionOps cpc_ops = {
+ .read = cpc_read,
+ .write = cpc_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {
+ .max_access_size = 8,
+ },
+};
+
+static void mips_cpc_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ MIPSCPCState *s = MIPS_CPC(obj);
+
+ memory_region_init_io(&s->mr, OBJECT(s), &cpc_ops, s, "mips-cpc",
+ CPC_ADDRSPACE_SZ);
+ sysbus_init_mmio(sbd, &s->mr);
+}
+
+static void mips_cpc_realize(DeviceState *dev, Error **errp)
+{
+ MIPSCPCState *s = MIPS_CPC(dev);
+
+ if (s->vp_start_running > cpc_vp_run_mask(s)) {
+ error_setg(errp,
+ "incorrect vp_start_running 0x%" PRIx64 " for num_vp = %d",
+ s->vp_running, s->num_vp);
+ return;
+ }
+}
+
+static void mips_cpc_reset(DeviceState *dev)
+{
+ MIPSCPCState *s = MIPS_CPC(dev);
+
+ /* Reflect the fact that all VPs are halted on reset */
+ s->vp_running = 0;
+
+ /* Put selected VPs into run state */
+ cpc_run_vp(s, s->vp_start_running);
+}
+
+static const VMStateDescription vmstate_mips_cpc = {
+ .name = "mips-cpc",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(vp_running, MIPSCPCState),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static Property mips_cpc_properties[] = {
+ DEFINE_PROP_UINT32("num-vp", MIPSCPCState, num_vp, 0x1),
+ DEFINE_PROP_UINT64("vp-start-running", MIPSCPCState, vp_start_running, 0x1),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void mips_cpc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = mips_cpc_realize;
+ dc->reset = mips_cpc_reset;
+ dc->vmsd = &vmstate_mips_cpc;
+ dc->props = mips_cpc_properties;
+}
+
+static const TypeInfo mips_cpc_info = {
+ .name = TYPE_MIPS_CPC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(MIPSCPCState),
+ .instance_init = mips_cpc_init,
+ .class_init = mips_cpc_class_init,
+};
+
+static void mips_cpc_register_types(void)
+{
+ type_register_static(&mips_cpc_info);
+}
+
+type_init(mips_cpc_register_types)
diff --git a/qemu/hw/misc/mips_itu.c b/qemu/hw/misc/mips_itu.c
new file mode 100644
index 000000000..da5455062
--- /dev/null
+++ b/qemu/hw/misc/mips_itu.c
@@ -0,0 +1,521 @@
+/*
+ * Inter-Thread Communication Unit emulation.
+ *
+ * Copyright (c) 2016 Imagination Technologies
+ *
+ * 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 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "sysemu/sysemu.h"
+#include "hw/misc/mips_itu.h"
+
+#define ITC_TAG_ADDRSPACE_SZ (ITC_ADDRESSMAP_NUM * 8)
+/* Initialize as 4kB area to fit all 32 cells with default 128B grain.
+ Storage may be resized by the software. */
+#define ITC_STORAGE_ADDRSPACE_SZ 0x1000
+
+#define ITC_FIFO_NUM_MAX 16
+#define ITC_SEMAPH_NUM_MAX 16
+#define ITC_AM1_NUMENTRIES_OFS 20
+
+#define ITC_CELL_PV_MAX_VAL 0xFFFF
+
+#define ITC_CELL_TAG_FIFO_DEPTH 28
+#define ITC_CELL_TAG_FIFO_PTR 18
+#define ITC_CELL_TAG_FIFO 17
+#define ITC_CELL_TAG_T 16
+#define ITC_CELL_TAG_F 1
+#define ITC_CELL_TAG_E 0
+
+#define ITC_AM0_BASE_ADDRESS_MASK 0xFFFFFC00ULL
+#define ITC_AM0_EN_MASK 0x1
+
+#define ITC_AM1_ADDR_MASK_MASK 0x1FC00
+#define ITC_AM1_ENTRY_GRAIN_MASK 0x7
+
+typedef enum ITCView {
+ ITCVIEW_BYPASS = 0,
+ ITCVIEW_CONTROL = 1,
+ ITCVIEW_EF_SYNC = 2,
+ ITCVIEW_EF_TRY = 3,
+ ITCVIEW_PV_SYNC = 4,
+ ITCVIEW_PV_TRY = 5
+} ITCView;
+
+MemoryRegion *mips_itu_get_tag_region(MIPSITUState *itu)
+{
+ return &itu->tag_io;
+}
+
+static uint64_t itc_tag_read(void *opaque, hwaddr addr, unsigned size)
+{
+ MIPSITUState *tag = (MIPSITUState *)opaque;
+ uint64_t index = addr >> 3;
+
+ if (index >= ITC_ADDRESSMAP_NUM) {
+ qemu_log_mask(LOG_GUEST_ERROR, "Read 0x%" PRIx64 "\n", addr);
+ return 0;
+ }
+
+ return tag->ITCAddressMap[index];
+}
+
+static void itc_reconfigure(MIPSITUState *tag)
+{
+ uint64_t *am = &tag->ITCAddressMap[0];
+ MemoryRegion *mr = &tag->storage_io;
+ hwaddr address = am[0] & ITC_AM0_BASE_ADDRESS_MASK;
+ uint64_t size = (1 << 10) + (am[1] & ITC_AM1_ADDR_MASK_MASK);
+ bool is_enabled = (am[0] & ITC_AM0_EN_MASK) != 0;
+
+ memory_region_transaction_begin();
+ if (!(size & (size - 1))) {
+ memory_region_set_size(mr, size);
+ }
+ memory_region_set_address(mr, address);
+ memory_region_set_enabled(mr, is_enabled);
+ memory_region_transaction_commit();
+}
+
+static void itc_tag_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ MIPSITUState *tag = (MIPSITUState *)opaque;
+ uint64_t *am = &tag->ITCAddressMap[0];
+ uint64_t am_old, mask;
+ uint64_t index = addr >> 3;
+
+ switch (index) {
+ case 0:
+ mask = ITC_AM0_BASE_ADDRESS_MASK | ITC_AM0_EN_MASK;
+ break;
+ case 1:
+ mask = ITC_AM1_ADDR_MASK_MASK | ITC_AM1_ENTRY_GRAIN_MASK;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "Bad write 0x%" PRIx64 "\n", addr);
+ return;
+ }
+
+ am_old = am[index];
+ am[index] = (data & mask) | (am_old & ~mask);
+ if (am_old != am[index]) {
+ itc_reconfigure(tag);
+ }
+}
+
+static const MemoryRegionOps itc_tag_ops = {
+ .read = itc_tag_read,
+ .write = itc_tag_write,
+ .impl = {
+ .max_access_size = 8,
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static inline uint32_t get_num_cells(MIPSITUState *s)
+{
+ return s->num_fifo + s->num_semaphores;
+}
+
+static inline ITCView get_itc_view(hwaddr addr)
+{
+ return (addr >> 3) & 0xf;
+}
+
+static inline int get_cell_stride_shift(const MIPSITUState *s)
+{
+ /* Minimum interval (for EntryGain = 0) is 128 B */
+ return 7 + (s->ITCAddressMap[1] & ITC_AM1_ENTRY_GRAIN_MASK);
+}
+
+static inline ITCStorageCell *get_cell(MIPSITUState *s,
+ hwaddr addr)
+{
+ uint32_t cell_idx = addr >> get_cell_stride_shift(s);
+ uint32_t num_cells = get_num_cells(s);
+
+ if (cell_idx >= num_cells) {
+ cell_idx = num_cells - 1;
+ }
+
+ return &s->cell[cell_idx];
+}
+
+static void wake_blocked_threads(ITCStorageCell *c)
+{
+ CPUState *cs;
+ CPU_FOREACH(cs) {
+ if (cs->halted && (c->blocked_threads & (1ULL << cs->cpu_index))) {
+ cpu_interrupt(cs, CPU_INTERRUPT_WAKE);
+ }
+ }
+ c->blocked_threads = 0;
+}
+
+static void QEMU_NORETURN block_thread_and_exit(ITCStorageCell *c)
+{
+ c->blocked_threads |= 1ULL << current_cpu->cpu_index;
+ cpu_restore_state(current_cpu, current_cpu->mem_io_pc);
+ current_cpu->halted = 1;
+ current_cpu->exception_index = EXCP_HLT;
+ cpu_loop_exit(current_cpu);
+}
+
+/* ITC Bypass View */
+
+static inline uint64_t view_bypass_read(ITCStorageCell *c)
+{
+ if (c->tag.FIFO) {
+ return c->data[c->fifo_out];
+ } else {
+ return c->data[0];
+ }
+}
+
+static inline void view_bypass_write(ITCStorageCell *c, uint64_t val)
+{
+ if (c->tag.FIFO && (c->tag.FIFOPtr > 0)) {
+ int idx = (c->fifo_out + c->tag.FIFOPtr - 1) % ITC_CELL_DEPTH;
+ c->data[idx] = val;
+ }
+
+ /* ignore a write to the semaphore cell */
+}
+
+/* ITC Control View */
+
+static inline uint64_t view_control_read(ITCStorageCell *c)
+{
+ return ((uint64_t)c->tag.FIFODepth << ITC_CELL_TAG_FIFO_DEPTH) |
+ (c->tag.FIFOPtr << ITC_CELL_TAG_FIFO_PTR) |
+ (c->tag.FIFO << ITC_CELL_TAG_FIFO) |
+ (c->tag.T << ITC_CELL_TAG_T) |
+ (c->tag.E << ITC_CELL_TAG_E) |
+ (c->tag.F << ITC_CELL_TAG_F);
+}
+
+static inline void view_control_write(ITCStorageCell *c, uint64_t val)
+{
+ c->tag.T = (val >> ITC_CELL_TAG_T) & 1;
+ c->tag.E = (val >> ITC_CELL_TAG_E) & 1;
+ c->tag.F = (val >> ITC_CELL_TAG_F) & 1;
+
+ if (c->tag.E) {
+ c->tag.FIFOPtr = 0;
+ }
+}
+
+/* ITC Empty/Full View */
+
+static uint64_t view_ef_common_read(ITCStorageCell *c, bool blocking)
+{
+ uint64_t ret = 0;
+
+ if (!c->tag.FIFO) {
+ return 0;
+ }
+
+ c->tag.F = 0;
+
+ if (blocking && c->tag.E) {
+ block_thread_and_exit(c);
+ }
+
+ if (c->blocked_threads) {
+ wake_blocked_threads(c);
+ }
+
+ if (c->tag.FIFOPtr > 0) {
+ ret = c->data[c->fifo_out];
+ c->fifo_out = (c->fifo_out + 1) % ITC_CELL_DEPTH;
+ c->tag.FIFOPtr--;
+ }
+
+ if (c->tag.FIFOPtr == 0) {
+ c->tag.E = 1;
+ }
+
+ return ret;
+}
+
+static uint64_t view_ef_sync_read(ITCStorageCell *c)
+{
+ return view_ef_common_read(c, true);
+}
+
+static uint64_t view_ef_try_read(ITCStorageCell *c)
+{
+ return view_ef_common_read(c, false);
+}
+
+static inline void view_ef_common_write(ITCStorageCell *c, uint64_t val,
+ bool blocking)
+{
+ if (!c->tag.FIFO) {
+ return;
+ }
+
+ c->tag.E = 0;
+
+ if (blocking && c->tag.F) {
+ block_thread_and_exit(c);
+ }
+
+ if (c->blocked_threads) {
+ wake_blocked_threads(c);
+ }
+
+ if (c->tag.FIFOPtr < ITC_CELL_DEPTH) {
+ int idx = (c->fifo_out + c->tag.FIFOPtr) % ITC_CELL_DEPTH;
+ c->data[idx] = val;
+ c->tag.FIFOPtr++;
+ }
+
+ if (c->tag.FIFOPtr == ITC_CELL_DEPTH) {
+ c->tag.F = 1;
+ }
+}
+
+static void view_ef_sync_write(ITCStorageCell *c, uint64_t val)
+{
+ view_ef_common_write(c, val, true);
+}
+
+static void view_ef_try_write(ITCStorageCell *c, uint64_t val)
+{
+ view_ef_common_write(c, val, false);
+}
+
+/* ITC P/V View */
+
+static uint64_t view_pv_common_read(ITCStorageCell *c, bool blocking)
+{
+ uint64_t ret = c->data[0];
+
+ if (c->tag.FIFO) {
+ return 0;
+ }
+
+ if (c->data[0] > 0) {
+ c->data[0]--;
+ } else if (blocking) {
+ block_thread_and_exit(c);
+ }
+
+ return ret;
+}
+
+static uint64_t view_pv_sync_read(ITCStorageCell *c)
+{
+ return view_pv_common_read(c, true);
+}
+
+static uint64_t view_pv_try_read(ITCStorageCell *c)
+{
+ return view_pv_common_read(c, false);
+}
+
+static inline void view_pv_common_write(ITCStorageCell *c)
+{
+ if (c->tag.FIFO) {
+ return;
+ }
+
+ if (c->data[0] < ITC_CELL_PV_MAX_VAL) {
+ c->data[0]++;
+ }
+
+ if (c->blocked_threads) {
+ wake_blocked_threads(c);
+ }
+}
+
+static void view_pv_sync_write(ITCStorageCell *c)
+{
+ view_pv_common_write(c);
+}
+
+static void view_pv_try_write(ITCStorageCell *c)
+{
+ view_pv_common_write(c);
+}
+
+static uint64_t itc_storage_read(void *opaque, hwaddr addr, unsigned size)
+{
+ MIPSITUState *s = (MIPSITUState *)opaque;
+ ITCStorageCell *cell = get_cell(s, addr);
+ ITCView view = get_itc_view(addr);
+ uint64_t ret = -1;
+
+ switch (view) {
+ case ITCVIEW_BYPASS:
+ ret = view_bypass_read(cell);
+ break;
+ case ITCVIEW_CONTROL:
+ ret = view_control_read(cell);
+ break;
+ case ITCVIEW_EF_SYNC:
+ ret = view_ef_sync_read(cell);
+ break;
+ case ITCVIEW_EF_TRY:
+ ret = view_ef_try_read(cell);
+ break;
+ case ITCVIEW_PV_SYNC:
+ ret = view_pv_sync_read(cell);
+ break;
+ case ITCVIEW_PV_TRY:
+ ret = view_pv_try_read(cell);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "itc_storage_read: Bad ITC View %d\n", (int)view);
+ break;
+ }
+
+ return ret;
+}
+
+static void itc_storage_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+ MIPSITUState *s = (MIPSITUState *)opaque;
+ ITCStorageCell *cell = get_cell(s, addr);
+ ITCView view = get_itc_view(addr);
+
+ switch (view) {
+ case ITCVIEW_BYPASS:
+ view_bypass_write(cell, data);
+ break;
+ case ITCVIEW_CONTROL:
+ view_control_write(cell, data);
+ break;
+ case ITCVIEW_EF_SYNC:
+ view_ef_sync_write(cell, data);
+ break;
+ case ITCVIEW_EF_TRY:
+ view_ef_try_write(cell, data);
+ break;
+ case ITCVIEW_PV_SYNC:
+ view_pv_sync_write(cell);
+ break;
+ case ITCVIEW_PV_TRY:
+ view_pv_try_write(cell);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "itc_storage_write: Bad ITC View %d\n", (int)view);
+ break;
+ }
+
+}
+
+static const MemoryRegionOps itc_storage_ops = {
+ .read = itc_storage_read,
+ .write = itc_storage_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void itc_reset_cells(MIPSITUState *s)
+{
+ int i;
+
+ memset(s->cell, 0, get_num_cells(s) * sizeof(s->cell[0]));
+
+ for (i = 0; i < s->num_fifo; i++) {
+ s->cell[i].tag.E = 1;
+ s->cell[i].tag.FIFO = 1;
+ s->cell[i].tag.FIFODepth = ITC_CELL_DEPTH_SHIFT;
+ }
+}
+
+static void mips_itu_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ MIPSITUState *s = MIPS_ITU(obj);
+
+ memory_region_init_io(&s->storage_io, OBJECT(s), &itc_storage_ops, s,
+ "mips-itc-storage", ITC_STORAGE_ADDRSPACE_SZ);
+ sysbus_init_mmio(sbd, &s->storage_io);
+
+ memory_region_init_io(&s->tag_io, OBJECT(s), &itc_tag_ops, s,
+ "mips-itc-tag", ITC_TAG_ADDRSPACE_SZ);
+}
+
+static void mips_itu_realize(DeviceState *dev, Error **errp)
+{
+ MIPSITUState *s = MIPS_ITU(dev);
+
+ if (s->num_fifo > ITC_FIFO_NUM_MAX) {
+ error_setg(errp, "Exceed maximum number of FIFO cells: %d",
+ s->num_fifo);
+ return;
+ }
+ if (s->num_semaphores > ITC_SEMAPH_NUM_MAX) {
+ error_setg(errp, "Exceed maximum number of Semaphore cells: %d",
+ s->num_semaphores);
+ return;
+ }
+
+ s->cell = g_new(ITCStorageCell, get_num_cells(s));
+}
+
+static void mips_itu_reset(DeviceState *dev)
+{
+ MIPSITUState *s = MIPS_ITU(dev);
+
+ s->ITCAddressMap[0] = 0;
+ s->ITCAddressMap[1] =
+ ((ITC_STORAGE_ADDRSPACE_SZ - 1) & ITC_AM1_ADDR_MASK_MASK) |
+ (get_num_cells(s) << ITC_AM1_NUMENTRIES_OFS);
+ itc_reconfigure(s);
+
+ itc_reset_cells(s);
+}
+
+static Property mips_itu_properties[] = {
+ DEFINE_PROP_INT32("num-fifo", MIPSITUState, num_fifo,
+ ITC_FIFO_NUM_MAX),
+ DEFINE_PROP_INT32("num-semaphores", MIPSITUState, num_semaphores,
+ ITC_SEMAPH_NUM_MAX),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void mips_itu_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->props = mips_itu_properties;
+ dc->realize = mips_itu_realize;
+ dc->reset = mips_itu_reset;
+}
+
+static const TypeInfo mips_itu_info = {
+ .name = TYPE_MIPS_ITU,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(MIPSITUState),
+ .instance_init = mips_itu_init,
+ .class_init = mips_itu_class_init,
+};
+
+static void mips_itu_register_types(void)
+{
+ type_register_static(&mips_itu_info);
+}
+
+type_init(mips_itu_register_types)
diff --git a/qemu/hw/misc/mst_fpga.c b/qemu/hw/misc/mst_fpga.c
index d5090799f..48d7dfb2d 100644
--- a/qemu/hw/misc/mst_fpga.c
+++ b/qemu/hw/misc/mst_fpga.c
@@ -10,6 +10,7 @@
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
diff --git a/qemu/hw/misc/omap_clk.c b/qemu/hw/misc/omap_clk.c
index 80a3c50e1..19151d07d 100644
--- a/qemu/hw/misc/omap_clk.c
+++ b/qemu/hw/misc/omap_clk.c
@@ -18,6 +18,7 @@
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/arm/omap.h"
@@ -1239,7 +1240,7 @@ void omap_clk_init(struct omap_mpu_state_s *mpu)
for (i = onchip_clks, count = 0; *i; i ++)
if ((*i)->flags & flag)
count ++;
- mpu->clks = (struct clk *) g_malloc0(sizeof(struct clk) * (count + 1));
+ mpu->clks = g_new0(struct clk, count + 1);
for (i = onchip_clks, j = mpu->clks; *i; i ++)
if ((*i)->flags & flag) {
memcpy(j, *i, sizeof(struct clk));
diff --git a/qemu/hw/misc/omap_gpmc.c b/qemu/hw/misc/omap_gpmc.c
index 74fc91c8e..67d8e2f02 100644
--- a/qemu/hw/misc/omap_gpmc.c
+++ b/qemu/hw/misc/omap_gpmc.c
@@ -18,6 +18,7 @@
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/block/flash.h"
#include "hw/arm/omap.h"
@@ -826,8 +827,7 @@ struct omap_gpmc_s *omap_gpmc_init(struct omap_mpu_state_s *mpu,
qemu_irq irq, qemu_irq drq)
{
int cs;
- struct omap_gpmc_s *s = (struct omap_gpmc_s *)
- g_malloc0(sizeof(struct omap_gpmc_s));
+ struct omap_gpmc_s *s = g_new0(struct omap_gpmc_s, 1);
memory_region_init_io(&s->iomem, NULL, &omap_gpmc_ops, s, "omap-gpmc", 0x1000);
memory_region_add_subregion(get_system_memory(), base, &s->iomem);
diff --git a/qemu/hw/misc/omap_l4.c b/qemu/hw/misc/omap_l4.c
index 245ceac84..88c533a0f 100644
--- a/qemu/hw/misc/omap_l4.c
+++ b/qemu/hw/misc/omap_l4.c
@@ -17,6 +17,7 @@
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/arm/omap.h"
diff --git a/qemu/hw/misc/omap_sdrc.c b/qemu/hw/misc/omap_sdrc.c
index 3de0c0e9d..dff37ecaf 100644
--- a/qemu/hw/misc/omap_sdrc.c
+++ b/qemu/hw/misc/omap_sdrc.c
@@ -17,6 +17,7 @@
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/arm/omap.h"
@@ -157,8 +158,7 @@ static const MemoryRegionOps omap_sdrc_ops = {
struct omap_sdrc_s *omap_sdrc_init(MemoryRegion *sysmem,
hwaddr base)
{
- struct omap_sdrc_s *s = (struct omap_sdrc_s *)
- g_malloc0(sizeof(struct omap_sdrc_s));
+ struct omap_sdrc_s *s = g_new0(struct omap_sdrc_s, 1);
omap_sdrc_reset(s);
diff --git a/qemu/hw/misc/omap_tap.c b/qemu/hw/misc/omap_tap.c
index 6f02bb9e4..e6ea8ee23 100644
--- a/qemu/hw/misc/omap_tap.c
+++ b/qemu/hw/misc/omap_tap.c
@@ -18,6 +18,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/arm/omap.h"
diff --git a/qemu/hw/misc/pc-testdev.c b/qemu/hw/misc/pc-testdev.c
index 18e94e07b..086893dcc 100644
--- a/qemu/hw/misc/pc-testdev.c
+++ b/qemu/hw/misc/pc-testdev.c
@@ -35,7 +35,7 @@
* git://git.kernel.org/pub/scm/virt/kvm/kvm-unit-tests.git
*/
-#include "config-host.h"
+#include "qemu/osdep.h"
#if defined(CONFIG_POSIX)
#include <sys/mman.h>
#endif
diff --git a/qemu/hw/misc/pci-testdev.c b/qemu/hw/misc/pci-testdev.c
index 26b9b8617..2f2e98977 100644
--- a/qemu/hw/misc/pci-testdev.c
+++ b/qemu/hw/misc/pci-testdev.c
@@ -17,10 +17,10 @@
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "qemu/event_notifier.h"
-#include "qemu/osdep.h"
typedef struct PCITestDevHdr {
uint8_t test;
@@ -239,6 +239,7 @@ static void pci_testdev_realize(PCIDevice *pci_dev, Error **errp)
uint8_t *pci_conf;
char *name;
int r, i;
+ bool fastmmio = kvm_ioeventfd_any_length_enabled();
pci_conf = pci_dev->config;
@@ -261,8 +262,12 @@ static void pci_testdev_realize(PCIDevice *pci_dev, Error **errp)
memcpy(test->hdr->name, name, strlen(name) + 1);
g_free(name);
test->hdr->offset = cpu_to_le32(IOTEST_SIZE(i) + i * IOTEST_ACCESS_WIDTH);
- test->size = IOTEST_ACCESS_WIDTH;
test->match_data = strcmp(IOTEST_TEST(i), "wildcard-eventfd");
+ if (fastmmio && IOTEST_IS_MEM(i) && !test->match_data) {
+ test->size = 0;
+ } else {
+ test->size = IOTEST_ACCESS_WIDTH;
+ }
test->hdr->test = i;
test->hdr->data = test->match_data ? IOTEST_DATAMATCH : IOTEST_NOMATCH;
test->hdr->width = IOTEST_ACCESS_WIDTH;
diff --git a/qemu/hw/misc/puv3_pm.c b/qemu/hw/misc/puv3_pm.c
index 37f23695d..577cebaac 100644
--- a/qemu/hw/misc/puv3_pm.c
+++ b/qemu/hw/misc/puv3_pm.c
@@ -8,6 +8,7 @@
* published by the Free Software Foundation, or any later version.
* See the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
diff --git a/qemu/hw/misc/pvpanic.c b/qemu/hw/misc/pvpanic.c
index 994f8af8e..0ac1e6ac9 100644
--- a/qemu/hw/misc/pvpanic.c
+++ b/qemu/hw/misc/pvpanic.c
@@ -12,6 +12,7 @@
*
*/
+#include "qemu/osdep.h"
#include "qapi/qmp/qobject.h"
#include "qapi/qmp/qjson.h"
#include "sysemu/sysemu.h"
@@ -41,8 +42,7 @@ static void handle_event(int event)
}
if (event & PVPANIC_PANICKED) {
- qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_PAUSE, &error_abort);
- vm_stop(RUN_STATE_GUEST_PANICKED);
+ qemu_system_guest_panicked();
return;
}
}
diff --git a/qemu/hw/misc/sga.c b/qemu/hw/misc/sga.c
index 83d2fd9d3..03b006d6f 100644
--- a/qemu/hw/misc/sga.c
+++ b/qemu/hw/misc/sga.c
@@ -24,6 +24,7 @@
* sgabios code originally available at code.google.com/p/sgabios
*
*/
+#include "qemu/osdep.h"
#include "hw/pci/pci.h"
#include "hw/i386/pc.h"
#include "hw/loader.h"
diff --git a/qemu/hw/misc/slavio_misc.c b/qemu/hw/misc/slavio_misc.c
index ec50f1075..edd5de070 100644
--- a/qemu/hw/misc/slavio_misc.c
+++ b/qemu/hw/misc/slavio_misc.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "sysemu/sysemu.h"
#include "hw/sysbus.h"
#include "trace.h"
diff --git a/qemu/hw/misc/stm32f2xx_syscfg.c b/qemu/hw/misc/stm32f2xx_syscfg.c
index 4ae4042bf..d0d7076ef 100644
--- a/qemu/hw/misc/stm32f2xx_syscfg.c
+++ b/qemu/hw/misc/stm32f2xx_syscfg.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/misc/stm32f2xx_syscfg.h"
#ifndef STM_SYSCFG_ERR_DEBUG
diff --git a/qemu/hw/misc/tmp105.c b/qemu/hw/misc/tmp105.c
index f3fe8b81f..f5c2472b5 100644
--- a/qemu/hw/misc/tmp105.c
+++ b/qemu/hw/misc/tmp105.c
@@ -18,9 +18,11 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i2c/i2c.h"
#include "tmp105.h"
+#include "qapi/error.h"
#include "qapi/visitor.h"
static void tmp105_interrupt_update(TMP105State *s)
@@ -52,26 +54,26 @@ static void tmp105_alarm_update(TMP105State *s)
tmp105_interrupt_update(s);
}
-static void tmp105_get_temperature(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void tmp105_get_temperature(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
TMP105State *s = TMP105(obj);
int64_t value = s->temperature * 1000 / 256;
- visit_type_int(v, &value, name, errp);
+ visit_type_int(v, name, &value, errp);
}
/* Units are 0.001 centigrades relative to 0 C. s->temperature is 8.8
* fixed point, so units are 1/256 centigrades. A simple ratio will do.
*/
-static void tmp105_set_temperature(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void tmp105_set_temperature(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
TMP105State *s = TMP105(obj);
Error *local_err = NULL;
int64_t temp;
- visit_type_int(v, &temp, name, &local_err);
+ visit_type_int(v, name, &temp, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
diff --git a/qemu/hw/misc/vmport.c b/qemu/hw/misc/vmport.c
index cd5716a46..689678980 100644
--- a/qemu/hw/misc/vmport.c
+++ b/qemu/hw/misc/vmport.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/isa/isa.h"
#include "hw/i386/pc.h"
diff --git a/qemu/hw/misc/zynq-xadc.c b/qemu/hw/misc/zynq-xadc.c
new file mode 100644
index 000000000..71fbccd79
--- /dev/null
+++ b/qemu/hw/misc/zynq-xadc.c
@@ -0,0 +1,303 @@
+/*
+ * ADC registers for Xilinx Zynq Platform
+ *
+ * Copyright (c) 2015 Guenter Roeck
+ * Based on hw/misc/zynq_slcr.c, written by Michal Simek
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "hw/misc/zynq-xadc.h"
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+
+enum {
+ CFG = 0x000 / 4,
+ INT_STS,
+ INT_MASK,
+ MSTS,
+ CMDFIFO,
+ RDFIFO,
+ MCTL,
+};
+
+#define CFG_ENABLE BIT(31)
+#define CFG_CFIFOTH_SHIFT 20
+#define CFG_CFIFOTH_LENGTH 4
+#define CFG_DFIFOTH_SHIFT 16
+#define CFG_DFIFOTH_LENGTH 4
+#define CFG_WEDGE BIT(13)
+#define CFG_REDGE BIT(12)
+#define CFG_TCKRATE_SHIFT 8
+#define CFG_TCKRATE_LENGTH 2
+
+#define CFG_TCKRATE_DIV(x) (0x1 << (x - 1))
+
+#define CFG_IGAP_SHIFT 0
+#define CFG_IGAP_LENGTH 5
+
+#define INT_CFIFO_LTH BIT(9)
+#define INT_DFIFO_GTH BIT(8)
+#define INT_OT BIT(7)
+#define INT_ALM_SHIFT 0
+#define INT_ALM_LENGTH 7
+#define INT_ALM_MASK (((1 << INT_ALM_LENGTH) - 1) << INT_ALM_SHIFT)
+
+#define INT_ALL (INT_CFIFO_LTH | INT_DFIFO_GTH | INT_OT | INT_ALM_MASK)
+
+#define MSTS_CFIFO_LVL_SHIFT 16
+#define MSTS_CFIFO_LVL_LENGTH 4
+#define MSTS_DFIFO_LVL_SHIFT 12
+#define MSTS_DFIFO_LVL_LENGTH 4
+#define MSTS_CFIFOF BIT(11)
+#define MSTS_CFIFOE BIT(10)
+#define MSTS_DFIFOF BIT(9)
+#define MSTS_DFIFOE BIT(8)
+#define MSTS_OT BIT(7)
+#define MSTS_ALM_SHIFT 0
+#define MSTS_ALM_LENGTH 7
+
+#define MCTL_RESET BIT(4)
+
+#define CMD_NOP 0x00
+#define CMD_READ 0x01
+#define CMD_WRITE 0x02
+
+static void zynq_xadc_update_ints(ZynqXADCState *s)
+{
+
+ /* We are fast, commands are actioned instantly so the CFIFO is always
+ * empty (and below threshold).
+ */
+ s->regs[INT_STS] |= INT_CFIFO_LTH;
+
+ if (s->xadc_dfifo_entries >
+ extract32(s->regs[CFG], CFG_DFIFOTH_SHIFT, CFG_DFIFOTH_LENGTH)) {
+ s->regs[INT_STS] |= INT_DFIFO_GTH;
+ }
+
+ qemu_set_irq(s->qemu_irq, !!(s->regs[INT_STS] & ~s->regs[INT_MASK]));
+}
+
+static void zynq_xadc_reset(DeviceState *d)
+{
+ ZynqXADCState *s = ZYNQ_XADC(d);
+
+ s->regs[CFG] = 0x14 << CFG_IGAP_SHIFT |
+ CFG_TCKRATE_DIV(4) << CFG_TCKRATE_SHIFT | CFG_REDGE;
+ s->regs[INT_STS] = INT_CFIFO_LTH;
+ s->regs[INT_MASK] = 0xffffffff;
+ s->regs[CMDFIFO] = 0;
+ s->regs[RDFIFO] = 0;
+ s->regs[MCTL] = MCTL_RESET;
+
+ memset(s->xadc_regs, 0, sizeof(s->xadc_regs));
+ memset(s->xadc_dfifo, 0, sizeof(s->xadc_dfifo));
+ s->xadc_dfifo_entries = 0;
+
+ zynq_xadc_update_ints(s);
+}
+
+static uint16_t xadc_pop_dfifo(ZynqXADCState *s)
+{
+ uint16_t rv = s->xadc_dfifo[0];
+ int i;
+
+ if (s->xadc_dfifo_entries > 0) {
+ s->xadc_dfifo_entries--;
+ }
+ for (i = 0; i < s->xadc_dfifo_entries; i++) {
+ s->xadc_dfifo[i] = s->xadc_dfifo[i + 1];
+ }
+ s->xadc_dfifo[s->xadc_dfifo_entries] = 0;
+ zynq_xadc_update_ints(s);
+ return rv;
+}
+
+static void xadc_push_dfifo(ZynqXADCState *s, uint16_t regval)
+{
+ if (s->xadc_dfifo_entries < ZYNQ_XADC_FIFO_DEPTH) {
+ s->xadc_dfifo[s->xadc_dfifo_entries++] = s->xadc_read_reg_previous;
+ }
+ s->xadc_read_reg_previous = regval;
+ zynq_xadc_update_ints(s);
+}
+
+static bool zynq_xadc_check_offset(hwaddr offset, bool rnw)
+{
+ switch (offset) {
+ case CFG:
+ case INT_MASK:
+ case INT_STS:
+ case MCTL:
+ return true;
+ case RDFIFO:
+ case MSTS:
+ return rnw; /* read only */
+ case CMDFIFO:
+ return !rnw; /* write only */
+ default:
+ return false;
+ }
+}
+
+static uint64_t zynq_xadc_read(void *opaque, hwaddr offset, unsigned size)
+{
+ ZynqXADCState *s = opaque;
+ int reg = offset / 4;
+ uint32_t rv = 0;
+
+ if (!zynq_xadc_check_offset(reg, true)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "zynq_xadc: Invalid read access to "
+ "addr %" HWADDR_PRIx "\n", offset);
+ return 0;
+ }
+
+ switch (reg) {
+ case CFG:
+ case INT_MASK:
+ case INT_STS:
+ case MCTL:
+ rv = s->regs[reg];
+ break;
+ case MSTS:
+ rv = MSTS_CFIFOE;
+ rv |= s->xadc_dfifo_entries << MSTS_DFIFO_LVL_SHIFT;
+ if (!s->xadc_dfifo_entries) {
+ rv |= MSTS_DFIFOE;
+ } else if (s->xadc_dfifo_entries == ZYNQ_XADC_FIFO_DEPTH) {
+ rv |= MSTS_DFIFOF;
+ }
+ break;
+ case RDFIFO:
+ rv = xadc_pop_dfifo(s);
+ break;
+ }
+ return rv;
+}
+
+static void zynq_xadc_write(void *opaque, hwaddr offset, uint64_t val,
+ unsigned size)
+{
+ ZynqXADCState *s = (ZynqXADCState *)opaque;
+ int reg = offset / 4;
+ int xadc_reg;
+ int xadc_cmd;
+ int xadc_data;
+
+ if (!zynq_xadc_check_offset(reg, false)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "zynq_xadc: Invalid write access "
+ "to addr %" HWADDR_PRIx "\n", offset);
+ return;
+ }
+
+ switch (reg) {
+ case CFG:
+ s->regs[CFG] = val;
+ break;
+ case INT_STS:
+ s->regs[INT_STS] &= ~val;
+ break;
+ case INT_MASK:
+ s->regs[INT_MASK] = val & INT_ALL;
+ break;
+ case CMDFIFO:
+ xadc_cmd = extract32(val, 26, 4);
+ xadc_reg = extract32(val, 16, 10);
+ xadc_data = extract32(val, 0, 16);
+
+ if (s->regs[MCTL] & MCTL_RESET) {
+ qemu_log_mask(LOG_GUEST_ERROR, "zynq_xadc: Sending command "
+ "while comm channel held in reset: %" PRIx32 "\n",
+ (uint32_t) val);
+ break;
+ }
+
+ if (xadc_reg >= ZYNQ_XADC_NUM_ADC_REGS && xadc_cmd != CMD_NOP) {
+ qemu_log_mask(LOG_GUEST_ERROR, "read/write op to invalid xadc "
+ "reg 0x%x\n", xadc_reg);
+ break;
+ }
+
+ switch (xadc_cmd) {
+ case CMD_READ:
+ xadc_push_dfifo(s, s->xadc_regs[xadc_reg]);
+ break;
+ case CMD_WRITE:
+ s->xadc_regs[xadc_reg] = xadc_data;
+ /* fallthrough */
+ case CMD_NOP:
+ xadc_push_dfifo(s, 0);
+ break;
+ }
+ break;
+ case MCTL:
+ s->regs[MCTL] = val & 0x00fffeff;
+ break;
+ }
+ zynq_xadc_update_ints(s);
+}
+
+static const MemoryRegionOps xadc_ops = {
+ .read = zynq_xadc_read,
+ .write = zynq_xadc_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void zynq_xadc_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ ZynqXADCState *s = ZYNQ_XADC(obj);
+
+ memory_region_init_io(&s->iomem, obj, &xadc_ops, s, "zynq-xadc",
+ ZYNQ_XADC_MMIO_SIZE);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->qemu_irq);
+}
+
+static const VMStateDescription vmstate_zynq_xadc = {
+ .name = "zynq-xadc",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, ZynqXADCState, ZYNQ_XADC_NUM_IO_REGS),
+ VMSTATE_UINT16_ARRAY(xadc_regs, ZynqXADCState,
+ ZYNQ_XADC_NUM_ADC_REGS),
+ VMSTATE_UINT16_ARRAY(xadc_dfifo, ZynqXADCState,
+ ZYNQ_XADC_FIFO_DEPTH),
+ VMSTATE_UINT16(xadc_read_reg_previous, ZynqXADCState),
+ VMSTATE_UINT16(xadc_dfifo_entries, ZynqXADCState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void zynq_xadc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->vmsd = &vmstate_zynq_xadc;
+ dc->reset = zynq_xadc_reset;
+}
+
+static const TypeInfo zynq_xadc_info = {
+ .class_init = zynq_xadc_class_init,
+ .name = TYPE_ZYNQ_XADC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(ZynqXADCState),
+ .instance_init = zynq_xadc_init,
+};
+
+static void zynq_xadc_register_types(void)
+{
+ type_register_static(&zynq_xadc_info);
+}
+
+type_init(zynq_xadc_register_types)
diff --git a/qemu/hw/misc/zynq_slcr.c b/qemu/hw/misc/zynq_slcr.c
index 964f2532f..b1b7591ef 100644
--- a/qemu/hw/misc/zynq_slcr.c
+++ b/qemu/hw/misc/zynq_slcr.c
@@ -14,6 +14,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "qemu/timer.h"
#include "hw/sysbus.h"
@@ -393,12 +394,12 @@ static void zynq_slcr_write(void *opaque, hwaddr offset,
return;
}
- if (!s->regs[LOCKSTA]) {
- s->regs[offset / 4] = val;
- } else {
- DB_PRINT("SCLR registers are locked. Unlock them first\n");
+ if (s->regs[LOCKSTA]) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "SCLR registers are locked. Unlock them first\n");
return;
}
+ s->regs[offset] = val;
switch (offset) {
case PSS_RST_CTRL:
diff --git a/qemu/hw/moxie/moxiesim.c b/qemu/hw/moxie/moxiesim.c
index 80bcc5b4f..3069834cf 100644
--- a/qemu/hw/moxie/moxiesim.c
+++ b/qemu/hw/moxie/moxiesim.c
@@ -24,6 +24,10 @@
* 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-common.h"
+#include "cpu.h"
#include "hw/sysbus.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
@@ -34,6 +38,7 @@
#include "hw/loader.h"
#include "hw/char/serial.h"
#include "exec/address-spaces.h"
+#include "elf.h"
#define PHYS_MEM_BASE 0x80000000
@@ -52,8 +57,8 @@ static void load_kernel(MoxieCPU *cpu, LoaderParams *loader_params)
ram_addr_t initrd_offset;
kernel_size = load_elf(loader_params->kernel_filename, NULL, NULL,
- &entry, &kernel_low, &kernel_high, 1,
- ELF_MACHINE, 0);
+ &entry, &kernel_low, &kernel_high, 1, EM_MOXIE,
+ 0, 0);
if (kernel_size <= 0) {
fprintf(stderr, "qemu: could not load kernel '%s'\n",
@@ -123,11 +128,11 @@ static void moxiesim_init(MachineState *machine)
qemu_register_reset(main_cpu_reset, cpu);
/* Allocate RAM. */
- memory_region_init_ram(ram, NULL, "moxiesim.ram", ram_size, &error_abort);
+ memory_region_init_ram(ram, NULL, "moxiesim.ram", ram_size, &error_fatal);
vmstate_register_ram_global(ram);
memory_region_add_subregion(address_space_mem, ram_base, ram);
- memory_region_init_ram(rom, NULL, "moxie.rom", 128*0x1000, &error_abort);
+ memory_region_init_ram(rom, NULL, "moxie.rom", 128*0x1000, &error_fatal);
vmstate_register_ram_global(rom);
memory_region_add_subregion(get_system_memory(), 0x1000, rom);
@@ -146,16 +151,11 @@ static void moxiesim_init(MachineState *machine)
}
}
-static QEMUMachine moxiesim_machine = {
- .name = "moxiesim",
- .desc = "Moxie simulator platform",
- .init = moxiesim_init,
- .is_default = 1,
-};
-
-static void moxie_machine_init(void)
+static void moxiesim_machine_init(MachineClass *mc)
{
- qemu_register_machine(&moxiesim_machine);
+ mc->desc = "Moxie simulator platform";
+ mc->init = moxiesim_init;
+ mc->is_default = 1;
}
-machine_init(moxie_machine_init)
+DEFINE_MACHINE("moxiesim", moxiesim_machine_init)
diff --git a/qemu/hw/net/Makefile.objs b/qemu/hw/net/Makefile.objs
index 98801739e..64d044923 100644
--- a/qemu/hw/net/Makefile.objs
+++ b/qemu/hw/net/Makefile.objs
@@ -19,6 +19,7 @@ common-obj-$(CONFIG_XGMAC) += xgmac.o
common-obj-$(CONFIG_MIPSNET) += mipsnet.o
common-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o
common-obj-$(CONFIG_ALLWINNER_EMAC) += allwinner_emac.o
+common-obj-$(CONFIG_IMX_FEC) += imx_fec.o
common-obj-$(CONFIG_CADENCE) += cadence_gem.o
common-obj-$(CONFIG_STELLARIS_ENET) += stellaris_enet.o
diff --git a/qemu/hw/net/allwinner_emac.c b/qemu/hw/net/allwinner_emac.c
index 0407dee6d..16d4b63ba 100644
--- a/qemu/hw/net/allwinner_emac.c
+++ b/qemu/hw/net/allwinner_emac.c
@@ -16,6 +16,7 @@
* GNU General Public License for more details.
*
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "net/net.h"
#include "qemu/fifo8.h"
diff --git a/qemu/hw/net/cadence_gem.c b/qemu/hw/net/cadence_gem.c
index 494a346cf..0346f3e33 100644
--- a/qemu/hw/net/cadence_gem.c
+++ b/qemu/hw/net/cadence_gem.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include <zlib.h> /* For crc32 */
#include "hw/net/cadence_gem.h"
@@ -677,6 +678,10 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size)
} else {
unsigned crc_val;
+ if (size > sizeof(rxbuf) - sizeof(crc_val)) {
+ size = sizeof(rxbuf) - sizeof(crc_val);
+ }
+ bytes_to_copy = size;
/* The application wants the FCS field, which QEMU does not provide.
* We must try and calculate one.
*/
@@ -862,6 +867,14 @@ static void gem_transmit(CadenceGEMState *s)
break;
}
+ if (tx_desc_get_length(desc) > sizeof(tx_packet) - (p - tx_packet)) {
+ DB_PRINT("TX descriptor @ 0x%x too large: size 0x%x space 0x%x\n",
+ (unsigned)packet_desc_addr,
+ (unsigned)tx_desc_get_length(desc),
+ sizeof(tx_packet) - (p - tx_packet));
+ break;
+ }
+
/* Gather this fragment of the packet from "dma memory" to our contig.
* buffer.
*/
@@ -951,7 +964,7 @@ static void gem_phy_reset(CadenceGEMState *s)
s->phy_regs[PHY_REG_1000BTSTAT] = 0x7C00;
s->phy_regs[PHY_REG_EXTSTAT] = 0x3000;
s->phy_regs[PHY_REG_PHYSPCFC_CTL] = 0x0078;
- s->phy_regs[PHY_REG_PHYSPCFC_ST] = 0xBC00;
+ s->phy_regs[PHY_REG_PHYSPCFC_ST] = 0x7C00;
s->phy_regs[PHY_REG_EXT_PHYSPCFC_CTL] = 0x0C60;
s->phy_regs[PHY_REG_LED] = 0x4100;
s->phy_regs[PHY_REG_EXT_PHYSPCFC_CTL2] = 0x000A;
@@ -964,6 +977,7 @@ static void gem_reset(DeviceState *d)
{
int i;
CadenceGEMState *s = CADENCE_GEM(d);
+ const uint8_t *a;
DB_PRINT("\n");
@@ -982,6 +996,11 @@ static void gem_reset(DeviceState *d)
s->regs[GEM_DESCONF5] = 0x002f2145;
s->regs[GEM_DESCONF6] = 0x00000200;
+ /* Set MAC address */
+ a = &s->conf.macaddr.a[0];
+ s->regs[GEM_SPADDR1LO] = a[0] | (a[1] << 8) | (a[2] << 16) | (a[3] << 24);
+ s->regs[GEM_SPADDR1HI] = a[4] | (a[5] << 8);
+
for (i = 0; i < 4; i++) {
s->sar_active[i] = false;
}
diff --git a/qemu/hw/net/dp8393x.c b/qemu/hw/net/dp8393x.c
index ab607e484..0fa652c39 100644
--- a/qemu/hw/net/dp8393x.c
+++ b/qemu/hw/net/dp8393x.c
@@ -17,9 +17,11 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/devices.h"
#include "net/net.h"
+#include "qapi/error.h"
#include "qemu/timer.h"
#include <zlib.h>
@@ -292,7 +294,7 @@ static void dp8393x_set_next_tick(dp8393xState *s)
ticks = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
s->wt_last_update = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- delay = get_ticks_per_sec() * ticks / 5000000;
+ delay = NANOSECONDS_PER_SECOND * ticks / 5000000;
timer_mod(s->watchdog, s->wt_last_update + delay);
}
diff --git a/qemu/hw/net/e1000.c b/qemu/hw/net/e1000.c
index 09c9e9d53..8e79b550e 100644
--- a/qemu/hw/net/e1000.c
+++ b/qemu/hw/net/e1000.c
@@ -25,6 +25,7 @@
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "net/net.h"
@@ -37,24 +38,26 @@
#include "e1000_regs.h"
+static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
#define E1000_DEBUG
#ifdef E1000_DEBUG
enum {
- DEBUG_GENERAL, DEBUG_IO, DEBUG_MMIO, DEBUG_INTERRUPT,
- DEBUG_RX, DEBUG_TX, DEBUG_MDIC, DEBUG_EEPROM,
- DEBUG_UNKNOWN, DEBUG_TXSUM, DEBUG_TXERR, DEBUG_RXERR,
+ DEBUG_GENERAL, DEBUG_IO, DEBUG_MMIO, DEBUG_INTERRUPT,
+ DEBUG_RX, DEBUG_TX, DEBUG_MDIC, DEBUG_EEPROM,
+ DEBUG_UNKNOWN, DEBUG_TXSUM, DEBUG_TXERR, DEBUG_RXERR,
DEBUG_RXFILTER, DEBUG_PHY, DEBUG_NOTYET,
};
-#define DBGBIT(x) (1<<DEBUG_##x)
+#define DBGBIT(x) (1<<DEBUG_##x)
static int debugflags = DBGBIT(TXERR) | DBGBIT(GENERAL);
-#define DBGOUT(what, fmt, ...) do { \
+#define DBGOUT(what, fmt, ...) do { \
if (debugflags & DBGBIT(what)) \
fprintf(stderr, "e1000: " fmt, ## __VA_ARGS__); \
} while (0)
#else
-#define DBGOUT(what, fmt, ...) do {} while (0)
+#define DBGOUT(what, fmt, ...) do {} while (0)
#endif
#define IOPORT_SIZE 0x40
@@ -118,7 +121,7 @@ typedef struct E1000State_st {
} tx;
struct {
- uint32_t val_in; // shifted in from guest driver
+ uint32_t val_in; /* shifted in from guest driver */
uint16_t bitnum_in;
uint16_t bitnum_out;
uint16_t reading;
@@ -135,11 +138,15 @@ typedef struct E1000State_st {
/* Compatibility flags for migration to/from qemu 1.3.0 and older */
#define E1000_FLAG_AUTONEG_BIT 0
#define E1000_FLAG_MIT_BIT 1
+#define E1000_FLAG_MAC_BIT 2
#define E1000_FLAG_AUTONEG (1 << E1000_FLAG_AUTONEG_BIT)
#define E1000_FLAG_MIT (1 << E1000_FLAG_MIT_BIT)
+#define E1000_FLAG_MAC (1 << E1000_FLAG_MAC_BIT)
uint32_t compat_flags;
} E1000State;
+#define chkflag(x) (s->compat_flags & E1000_FLAG_##x)
+
typedef struct E1000BaseClass {
PCIDeviceClass parent_class;
uint16_t phy_id2;
@@ -155,20 +162,36 @@ typedef struct E1000BaseClass {
#define E1000_DEVICE_GET_CLASS(obj) \
OBJECT_GET_CLASS(E1000BaseClass, (obj), TYPE_E1000_BASE)
-#define defreg(x) x = (E1000_##x>>2)
+#define defreg(x) x = (E1000_##x>>2)
enum {
- defreg(CTRL), defreg(EECD), defreg(EERD), defreg(GPRC),
- defreg(GPTC), defreg(ICR), defreg(ICS), defreg(IMC),
- defreg(IMS), defreg(LEDCTL), defreg(MANC), defreg(MDIC),
- defreg(MPC), defreg(PBA), defreg(RCTL), defreg(RDBAH),
- defreg(RDBAL), defreg(RDH), defreg(RDLEN), defreg(RDT),
- defreg(STATUS), defreg(SWSM), defreg(TCTL), defreg(TDBAH),
- defreg(TDBAL), defreg(TDH), defreg(TDLEN), defreg(TDT),
- defreg(TORH), defreg(TORL), defreg(TOTH), defreg(TOTL),
- defreg(TPR), defreg(TPT), defreg(TXDCTL), defreg(WUFC),
- defreg(RA), defreg(MTA), defreg(CRCERRS),defreg(VFTA),
- defreg(VET), defreg(RDTR), defreg(RADV), defreg(TADV),
- defreg(ITR),
+ defreg(CTRL), defreg(EECD), defreg(EERD), defreg(GPRC),
+ defreg(GPTC), defreg(ICR), defreg(ICS), defreg(IMC),
+ defreg(IMS), defreg(LEDCTL), defreg(MANC), defreg(MDIC),
+ defreg(MPC), defreg(PBA), defreg(RCTL), defreg(RDBAH),
+ defreg(RDBAL), defreg(RDH), defreg(RDLEN), defreg(RDT),
+ defreg(STATUS), defreg(SWSM), defreg(TCTL), defreg(TDBAH),
+ defreg(TDBAL), defreg(TDH), defreg(TDLEN), defreg(TDT),
+ defreg(TORH), defreg(TORL), defreg(TOTH), defreg(TOTL),
+ defreg(TPR), defreg(TPT), defreg(TXDCTL), defreg(WUFC),
+ defreg(RA), defreg(MTA), defreg(CRCERRS), defreg(VFTA),
+ defreg(VET), defreg(RDTR), defreg(RADV), defreg(TADV),
+ defreg(ITR), defreg(FCRUC), defreg(TDFH), defreg(TDFT),
+ defreg(TDFHS), defreg(TDFTS), defreg(TDFPC), defreg(RDFH),
+ defreg(RDFT), defreg(RDFHS), defreg(RDFTS), defreg(RDFPC),
+ defreg(IPAV), defreg(WUC), defreg(WUS), defreg(AIT),
+ defreg(IP6AT), defreg(IP4AT), defreg(FFLT), defreg(FFMT),
+ defreg(FFVT), defreg(WUPM), defreg(PBM), defreg(SCC),
+ defreg(ECOL), defreg(MCC), defreg(LATECOL), defreg(COLC),
+ defreg(DC), defreg(TNCRS), defreg(SEC), defreg(CEXTERR),
+ defreg(RLEC), defreg(XONRXC), defreg(XONTXC), defreg(XOFFRXC),
+ defreg(XOFFTXC), defreg(RFC), defreg(RJC), defreg(RNBC),
+ defreg(TSCTFC), defreg(MGTPRC), defreg(MGTPDC), defreg(MGTPTC),
+ defreg(RUC), defreg(ROC), defreg(GORCL), defreg(GORCH),
+ defreg(GOTCL), defreg(GOTCH), defreg(BPRC), defreg(MPRC),
+ defreg(TSCTC), defreg(PRC64), defreg(PRC127), defreg(PRC255),
+ defreg(PRC511), defreg(PRC1023), defreg(PRC1522), defreg(PTC64),
+ defreg(PTC127), defreg(PTC255), defreg(PTC511), defreg(PTC1023),
+ defreg(PTC1522), defreg(MPTC), defreg(BPTC)
};
static void
@@ -193,8 +216,7 @@ e1000_link_up(E1000State *s)
static bool
have_autoneg(E1000State *s)
{
- return (s->compat_flags & E1000_FLAG_AUTONEG) &&
- (s->phy_reg[PHY_CTRL] & MII_CR_AUTO_NEG_EN);
+ return chkflag(AUTONEG) && (s->phy_reg[PHY_CTRL] & MII_CR_AUTO_NEG_EN);
}
static void
@@ -226,18 +248,18 @@ enum { NPHYWRITEOPS = ARRAY_SIZE(phyreg_writeops) };
enum { PHY_R = 1, PHY_W = 2, PHY_RW = PHY_R | PHY_W };
static const char phy_regcap[0x20] = {
- [PHY_STATUS] = PHY_R, [M88E1000_EXT_PHY_SPEC_CTRL] = PHY_RW,
- [PHY_ID1] = PHY_R, [M88E1000_PHY_SPEC_CTRL] = PHY_RW,
- [PHY_CTRL] = PHY_RW, [PHY_1000T_CTRL] = PHY_RW,
- [PHY_LP_ABILITY] = PHY_R, [PHY_1000T_STATUS] = PHY_R,
- [PHY_AUTONEG_ADV] = PHY_RW, [M88E1000_RX_ERR_CNTR] = PHY_R,
- [PHY_ID2] = PHY_R, [M88E1000_PHY_SPEC_STATUS] = PHY_R,
+ [PHY_STATUS] = PHY_R, [M88E1000_EXT_PHY_SPEC_CTRL] = PHY_RW,
+ [PHY_ID1] = PHY_R, [M88E1000_PHY_SPEC_CTRL] = PHY_RW,
+ [PHY_CTRL] = PHY_RW, [PHY_1000T_CTRL] = PHY_RW,
+ [PHY_LP_ABILITY] = PHY_R, [PHY_1000T_STATUS] = PHY_R,
+ [PHY_AUTONEG_ADV] = PHY_RW, [M88E1000_RX_ERR_CNTR] = PHY_R,
+ [PHY_ID2] = PHY_R, [M88E1000_PHY_SPEC_STATUS] = PHY_R,
[PHY_AUTONEG_EXP] = PHY_R,
};
/* PHY_ID2 documented in 8254x_GBe_SDM.pdf, pp. 250 */
static const uint16_t phy_reg_init[] = {
- [PHY_CTRL] = MII_CR_SPEED_SELECT_MSB |
+ [PHY_CTRL] = MII_CR_SPEED_SELECT_MSB |
MII_CR_FULL_DUPLEX |
MII_CR_AUTO_NEG_EN,
@@ -264,15 +286,15 @@ static const uint16_t phy_reg_init[] = {
};
static const uint32_t mac_reg_init[] = {
- [PBA] = 0x00100030,
- [LEDCTL] = 0x602,
- [CTRL] = E1000_CTRL_SWDPIN2 | E1000_CTRL_SWDPIN0 |
+ [PBA] = 0x00100030,
+ [LEDCTL] = 0x602,
+ [CTRL] = E1000_CTRL_SWDPIN2 | E1000_CTRL_SWDPIN0 |
E1000_CTRL_SPD_1000 | E1000_CTRL_SLU,
- [STATUS] = 0x80000000 | E1000_STATUS_GIO_MASTER_ENABLE |
+ [STATUS] = 0x80000000 | E1000_STATUS_GIO_MASTER_ENABLE |
E1000_STATUS_ASDV | E1000_STATUS_MTXCKOK |
E1000_STATUS_SPEED_1000 | E1000_STATUS_FD |
E1000_STATUS_LU,
- [MANC] = E1000_MANC_EN_MNG2HOST | E1000_MANC_RCV_TCO_EN |
+ [MANC] = E1000_MANC_EN_MNG2HOST | E1000_MANC_RCV_TCO_EN |
E1000_MANC_ARP_EN | E1000_MANC_0298_EN |
E1000_MANC_RMCP_EN,
};
@@ -319,7 +341,7 @@ set_interrupt_cause(E1000State *s, int index, uint32_t val)
if (s->mit_timer_on) {
return;
}
- if (s->compat_flags & E1000_FLAG_MIT) {
+ if (chkflag(MIT)) {
/* Compute the next mitigation delay according to pending
* interrupts and the current values of RADV (provided
* RDTR!=0), TADV and ITR.
@@ -335,6 +357,14 @@ set_interrupt_cause(E1000State *s, int index, uint32_t val)
}
mit_update_delay(&mit_delay, s->mac_reg[ITR]);
+ /*
+ * According to e1000 SPEC, the Ethernet controller guarantees
+ * a maximum observable interrupt rate of 7813 interrupts/sec.
+ * Thus if mit_delay < 500 then the delay should be set to the
+ * minimum delay possible which is 500.
+ */
+ mit_delay = (mit_delay < 500) ? 500 : mit_delay;
+
if (mit_delay) {
s->mit_timer_on = 1;
timer_mod(s->mit_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
@@ -510,17 +540,19 @@ set_eecd(E1000State *s, int index, uint32_t val)
s->eecd_state.old_eecd = val & (E1000_EECD_SK | E1000_EECD_CS |
E1000_EECD_DI|E1000_EECD_FWE_MASK|E1000_EECD_REQ);
- if (!(E1000_EECD_CS & val)) // CS inactive; nothing to do
- return;
- if (E1000_EECD_CS & (val ^ oldval)) { // CS rise edge; reset state
- s->eecd_state.val_in = 0;
- s->eecd_state.bitnum_in = 0;
- s->eecd_state.bitnum_out = 0;
- s->eecd_state.reading = 0;
+ if (!(E1000_EECD_CS & val)) { /* CS inactive; nothing to do */
+ return;
}
- if (!(E1000_EECD_SK & (val ^ oldval))) // no clock edge
+ if (E1000_EECD_CS & (val ^ oldval)) { /* CS rise edge; reset state */
+ s->eecd_state.val_in = 0;
+ s->eecd_state.bitnum_in = 0;
+ s->eecd_state.bitnum_out = 0;
+ s->eecd_state.reading = 0;
+ }
+ if (!(E1000_EECD_SK & (val ^ oldval))) { /* no clock edge */
return;
- if (!(E1000_EECD_SK & val)) { // falling edge
+ }
+ if (!(E1000_EECD_SK & val)) { /* falling edge */
s->eecd_state.bitnum_out++;
return;
}
@@ -565,6 +597,56 @@ putsum(uint8_t *data, uint32_t n, uint32_t sloc, uint32_t css, uint32_t cse)
}
}
+static inline void
+inc_reg_if_not_full(E1000State *s, int index)
+{
+ if (s->mac_reg[index] != 0xffffffff) {
+ s->mac_reg[index]++;
+ }
+}
+
+static inline void
+inc_tx_bcast_or_mcast_count(E1000State *s, const unsigned char *arr)
+{
+ if (!memcmp(arr, bcast, sizeof bcast)) {
+ inc_reg_if_not_full(s, BPTC);
+ } else if (arr[0] & 1) {
+ inc_reg_if_not_full(s, MPTC);
+ }
+}
+
+static void
+grow_8reg_if_not_full(E1000State *s, int index, int size)
+{
+ uint64_t sum = s->mac_reg[index] | (uint64_t)s->mac_reg[index+1] << 32;
+
+ if (sum + size < sum) {
+ sum = ~0ULL;
+ } else {
+ sum += size;
+ }
+ s->mac_reg[index] = sum;
+ s->mac_reg[index+1] = sum >> 32;
+}
+
+static void
+increase_size_stats(E1000State *s, const int *size_regs, int size)
+{
+ if (size > 1023) {
+ inc_reg_if_not_full(s, size_regs[5]);
+ } else if (size > 511) {
+ inc_reg_if_not_full(s, size_regs[4]);
+ } else if (size > 255) {
+ inc_reg_if_not_full(s, size_regs[3]);
+ } else if (size > 127) {
+ inc_reg_if_not_full(s, size_regs[2]);
+ } else if (size > 64) {
+ inc_reg_if_not_full(s, size_regs[1]);
+ } else if (size == 64) {
+ inc_reg_if_not_full(s, size_regs[0]);
+ }
+}
+
static inline int
vlan_enabled(E1000State *s)
{
@@ -602,40 +684,49 @@ fcs_len(E1000State *s)
static void
e1000_send_packet(E1000State *s, const uint8_t *buf, int size)
{
+ static const int PTCregs[6] = { PTC64, PTC127, PTC255, PTC511,
+ PTC1023, PTC1522 };
+
NetClientState *nc = qemu_get_queue(s->nic);
if (s->phy_reg[PHY_CTRL] & MII_CR_LOOPBACK) {
nc->info->receive(nc, buf, size);
} else {
qemu_send_packet(nc, buf, size);
}
+ inc_tx_bcast_or_mcast_count(s, buf);
+ increase_size_stats(s, PTCregs, size);
}
static void
xmit_seg(E1000State *s)
{
uint16_t len, *sp;
- unsigned int frames = s->tx.tso_frames, css, sofar, n;
+ unsigned int frames = s->tx.tso_frames, css, sofar;
struct e1000_tx *tp = &s->tx;
if (tp->tse && tp->cptse) {
css = tp->ipcss;
DBGOUT(TXSUM, "frames %d size %d ipcss %d\n",
frames, tp->size, css);
- if (tp->ip) { // IPv4
+ if (tp->ip) { /* IPv4 */
stw_be_p(tp->data+css+2, tp->size - css);
stw_be_p(tp->data+css+4,
- be16_to_cpup((uint16_t *)(tp->data+css+4))+frames);
- } else // IPv6
+ be16_to_cpup((uint16_t *)(tp->data+css+4))+frames);
+ } else { /* IPv6 */
stw_be_p(tp->data+css+4, tp->size - css);
+ }
css = tp->tucss;
len = tp->size - css;
DBGOUT(TXSUM, "tcp %d tucss %d len %d\n", tp->tcp, css, len);
if (tp->tcp) {
sofar = frames * tp->mss;
stl_be_p(tp->data+css+4, ldl_be_p(tp->data+css+4)+sofar); /* seq */
- if (tp->paylen - sofar > tp->mss)
- tp->data[css + 13] &= ~9; // PSH, FIN
- } else // UDP
+ if (tp->paylen - sofar > tp->mss) {
+ tp->data[css + 13] &= ~9; /* PSH, FIN */
+ } else if (frames) {
+ inc_reg_if_not_full(s, TSCTC);
+ }
+ } else /* UDP */
stw_be_p(tp->data+css+4, len);
if (tp->sum_needed & E1000_TXD_POPTS_TXSM) {
unsigned int phsum;
@@ -657,13 +748,15 @@ xmit_seg(E1000State *s)
memmove(tp->data, tp->data + 4, 8);
memcpy(tp->data + 8, tp->vlan_header, 4);
e1000_send_packet(s, tp->vlan, tp->size + 4);
- } else
+ } else {
e1000_send_packet(s, tp->data, tp->size);
- s->mac_reg[TPT]++;
- s->mac_reg[GPTC]++;
- n = s->mac_reg[TOTL];
- if ((s->mac_reg[TOTL] += s->tx.size) < n)
- s->mac_reg[TOTH]++;
+ }
+
+ inc_reg_if_not_full(s, TPT);
+ grow_8reg_if_not_full(s, TOTL, s->tx.size);
+ s->mac_reg[GPTC] = s->mac_reg[TPT];
+ s->mac_reg[GOTCL] = s->mac_reg[TOTL];
+ s->mac_reg[GOTCH] = s->mac_reg[TOTH];
}
static void
@@ -679,7 +772,7 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp)
struct e1000_tx *tp = &s->tx;
s->mit_ide |= (txd_lower & E1000_TXD_CMD_IDE);
- if (dtype == E1000_TXD_CMD_DEXT) { // context descriptor
+ if (dtype == E1000_TXD_CMD_DEXT) { /* context descriptor */
op = le32_to_cpu(xp->cmd_and_length);
tp->ipcss = xp->lower_setup.ip_fields.ipcss;
tp->ipcso = xp->lower_setup.ip_fields.ipcso;
@@ -694,7 +787,7 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp)
tp->tcp = (op & E1000_TXD_CMD_TCP) ? 1 : 0;
tp->tse = (op & E1000_TXD_CMD_TSE) ? 1 : 0;
tp->tso_frames = 0;
- if (tp->tucso == 0) { // this is probably wrong
+ if (tp->tucso == 0) { /* this is probably wrong */
DBGOUT(TXSUM, "TCP/UDP: cso 0!\n");
tp->tucso = tp->tucss + (tp->tcp ? 16 : 6);
}
@@ -718,7 +811,7 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp)
stw_be_p(tp->vlan_header + 2,
le16_to_cpu(dp->upper.fields.special));
}
-
+
addr = le64_to_cpu(dp->buffer_addr);
if (tp->tse && tp->cptse) {
msh = tp->hdr_len + tp->mss;
@@ -819,7 +912,8 @@ start_xmit(E1000State *s)
* bogus values to TDT/TDLEN.
* there's nothing too intelligent we could do about this.
*/
- if (s->mac_reg[TDH] == tdh_start) {
+ if (s->mac_reg[TDH] == tdh_start ||
+ tdh_start >= s->mac_reg[TDLEN] / sizeof(desc)) {
DBGOUT(TXERR, "TDH wraparound @%x, TDT %x, TDLEN %x\n",
tdh_start, s->mac_reg[TDT], s->mac_reg[TDLEN]);
break;
@@ -831,9 +925,9 @@ start_xmit(E1000State *s)
static int
receive_filter(E1000State *s, const uint8_t *buf, int size)
{
- static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
static const int mta_shift[] = {4, 3, 2, 0};
uint32_t f, rctl = s->mac_reg[RCTL], ra[2], *rp;
+ int isbcast = !memcmp(buf, bcast, sizeof bcast), ismcast = (buf[0] & 1);
if (is_vlan_packet(s, buf) && vlan_rx_filter_enabled(s)) {
uint16_t vid = be16_to_cpup((uint16_t *)(buf + 14));
@@ -843,14 +937,19 @@ receive_filter(E1000State *s, const uint8_t *buf, int size)
return 0;
}
- if (rctl & E1000_RCTL_UPE) // promiscuous
+ if (!isbcast && !ismcast && (rctl & E1000_RCTL_UPE)) { /* promiscuous ucast */
return 1;
+ }
- if ((buf[0] & 1) && (rctl & E1000_RCTL_MPE)) // promiscuous mcast
+ if (ismcast && (rctl & E1000_RCTL_MPE)) { /* promiscuous mcast */
+ inc_reg_if_not_full(s, MPRC);
return 1;
+ }
- if ((rctl & E1000_RCTL_BAM) && !memcmp(buf, bcast, sizeof bcast))
+ if (isbcast && (rctl & E1000_RCTL_BAM)) { /* broadcast enabled */
+ inc_reg_if_not_full(s, BPRC);
return 1;
+ }
for (rp = s->mac_reg + RA; rp < s->mac_reg + RA + 32; rp += 2) {
if (!(rp[1] & E1000_RAH_AV))
@@ -870,8 +969,10 @@ receive_filter(E1000State *s, const uint8_t *buf, int size)
f = mta_shift[(rctl >> E1000_RCTL_MO_SHIFT) & 3];
f = (((buf[5] << 8) | buf[4]) >> f) & 0xfff;
- if (s->mac_reg[MTA + (f >> 5)] & (1 << (f & 0x1f)))
+ if (s->mac_reg[MTA + (f >> 5)] & (1 << (f & 0x1f))) {
+ inc_reg_if_not_full(s, MPRC);
return 1;
+ }
DBGOUT(RXFILTER,
"dropping, inexact filter mismatch: %02x:%02x:%02x:%02x:%02x:%02x MO %d MTA[%d] %x\n",
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5],
@@ -960,6 +1061,8 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt)
size_t desc_offset;
size_t desc_size;
size_t total_size;
+ static const int PRCregs[6] = { PRC64, PRC127, PRC255, PRC511,
+ PRC1023, PRC1522 };
if (!(s->mac_reg[STATUS] & E1000_STATUS_LU)) {
return -1;
@@ -973,6 +1076,7 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt)
if (size < sizeof(min_buf)) {
iov_to_buf(iov, iovcnt, 0, min_buf, size);
memset(&min_buf[size], 0, sizeof(min_buf) - size);
+ inc_reg_if_not_full(s, RUC);
min_iov.iov_base = filter_buf = min_buf;
min_iov.iov_len = size = sizeof(min_buf);
iovcnt = 1;
@@ -988,6 +1092,7 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt)
(size > MAXIMUM_ETHERNET_VLAN_SIZE
&& !(s->mac_reg[RCTL] & E1000_RCTL_LPE)))
&& !(s->mac_reg[RCTL] & E1000_RCTL_SBP)) {
+ inc_reg_if_not_full(s, ROC);
return size;
}
@@ -1065,7 +1170,8 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt)
if (++s->mac_reg[RDH] * sizeof(desc) >= s->mac_reg[RDLEN])
s->mac_reg[RDH] = 0;
/* see comment in start_xmit; same here */
- if (s->mac_reg[RDH] == rdh_start) {
+ if (s->mac_reg[RDH] == rdh_start ||
+ rdh_start >= s->mac_reg[RDLEN] / sizeof(desc)) {
DBGOUT(RXERR, "RDH wraparound @%x, RDT %x, RDLEN %x\n",
rdh_start, s->mac_reg[RDT], s->mac_reg[RDLEN]);
set_ics(s, 0, E1000_ICS_RXO);
@@ -1073,16 +1179,17 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt)
}
} while (desc_offset < total_size);
- s->mac_reg[GPRC]++;
- s->mac_reg[TPR]++;
+ increase_size_stats(s, PRCregs, total_size);
+ inc_reg_if_not_full(s, TPR);
+ s->mac_reg[GPRC] = s->mac_reg[TPR];
/* TOR - Total Octets Received:
* This register includes bytes received in a packet from the <Destination
* Address> field through the <CRC> field, inclusively.
+ * Always include FCS length (4) in size.
*/
- n = s->mac_reg[TORL] + size + /* Always include FCS length. */ 4;
- if (n < s->mac_reg[TORL])
- s->mac_reg[TORH]++;
- s->mac_reg[TORL] = n;
+ grow_8reg_if_not_full(s, TORL, size+4);
+ s->mac_reg[GORCL] = s->mac_reg[TORL];
+ s->mac_reg[GORCH] = s->mac_reg[TORH];
n = E1000_ICS_RXT0;
if ((rdt = s->mac_reg[RDT]) < s->mac_reg[RDH])
@@ -1114,6 +1221,30 @@ mac_readreg(E1000State *s, int index)
}
static uint32_t
+mac_low4_read(E1000State *s, int index)
+{
+ return s->mac_reg[index] & 0xf;
+}
+
+static uint32_t
+mac_low11_read(E1000State *s, int index)
+{
+ return s->mac_reg[index] & 0x7ff;
+}
+
+static uint32_t
+mac_low13_read(E1000State *s, int index)
+{
+ return s->mac_reg[index] & 0x1fff;
+}
+
+static uint32_t
+mac_low16_read(E1000State *s, int index)
+{
+ return s->mac_reg[index] & 0xffff;
+}
+
+static uint32_t
mac_icr_read(E1000State *s, int index)
{
uint32_t ret = s->mac_reg[ICR];
@@ -1206,46 +1337,144 @@ set_ims(E1000State *s, int index, uint32_t val)
set_ics(s, 0, 0);
}
-#define getreg(x) [x] = mac_readreg
+#define getreg(x) [x] = mac_readreg
static uint32_t (*macreg_readops[])(E1000State *, int) = {
- getreg(PBA), getreg(RCTL), getreg(TDH), getreg(TXDCTL),
- getreg(WUFC), getreg(TDT), getreg(CTRL), getreg(LEDCTL),
- getreg(MANC), getreg(MDIC), getreg(SWSM), getreg(STATUS),
- getreg(TORL), getreg(TOTL), getreg(IMS), getreg(TCTL),
- getreg(RDH), getreg(RDT), getreg(VET), getreg(ICS),
- getreg(TDBAL), getreg(TDBAH), getreg(RDBAH), getreg(RDBAL),
- getreg(TDLEN), getreg(RDLEN), getreg(RDTR), getreg(RADV),
- getreg(TADV), getreg(ITR),
-
- [TOTH] = mac_read_clr8, [TORH] = mac_read_clr8, [GPRC] = mac_read_clr4,
- [GPTC] = mac_read_clr4, [TPR] = mac_read_clr4, [TPT] = mac_read_clr4,
- [ICR] = mac_icr_read, [EECD] = get_eecd, [EERD] = flash_eerd_read,
- [CRCERRS ... MPC] = &mac_readreg,
- [RA ... RA+31] = &mac_readreg,
- [MTA ... MTA+127] = &mac_readreg,
+ getreg(PBA), getreg(RCTL), getreg(TDH), getreg(TXDCTL),
+ getreg(WUFC), getreg(TDT), getreg(CTRL), getreg(LEDCTL),
+ getreg(MANC), getreg(MDIC), getreg(SWSM), getreg(STATUS),
+ getreg(TORL), getreg(TOTL), getreg(IMS), getreg(TCTL),
+ getreg(RDH), getreg(RDT), getreg(VET), getreg(ICS),
+ getreg(TDBAL), getreg(TDBAH), getreg(RDBAH), getreg(RDBAL),
+ getreg(TDLEN), getreg(RDLEN), getreg(RDTR), getreg(RADV),
+ getreg(TADV), getreg(ITR), getreg(FCRUC), getreg(IPAV),
+ getreg(WUC), getreg(WUS), getreg(SCC), getreg(ECOL),
+ getreg(MCC), getreg(LATECOL), getreg(COLC), getreg(DC),
+ getreg(TNCRS), getreg(SEC), getreg(CEXTERR), getreg(RLEC),
+ getreg(XONRXC), getreg(XONTXC), getreg(XOFFRXC), getreg(XOFFTXC),
+ getreg(RFC), getreg(RJC), getreg(RNBC), getreg(TSCTFC),
+ getreg(MGTPRC), getreg(MGTPDC), getreg(MGTPTC), getreg(GORCL),
+ getreg(GOTCL),
+
+ [TOTH] = mac_read_clr8, [TORH] = mac_read_clr8,
+ [GOTCH] = mac_read_clr8, [GORCH] = mac_read_clr8,
+ [PRC64] = mac_read_clr4, [PRC127] = mac_read_clr4,
+ [PRC255] = mac_read_clr4, [PRC511] = mac_read_clr4,
+ [PRC1023] = mac_read_clr4, [PRC1522] = mac_read_clr4,
+ [PTC64] = mac_read_clr4, [PTC127] = mac_read_clr4,
+ [PTC255] = mac_read_clr4, [PTC511] = mac_read_clr4,
+ [PTC1023] = mac_read_clr4, [PTC1522] = mac_read_clr4,
+ [GPRC] = mac_read_clr4, [GPTC] = mac_read_clr4,
+ [TPT] = mac_read_clr4, [TPR] = mac_read_clr4,
+ [RUC] = mac_read_clr4, [ROC] = mac_read_clr4,
+ [BPRC] = mac_read_clr4, [MPRC] = mac_read_clr4,
+ [TSCTC] = mac_read_clr4, [BPTC] = mac_read_clr4,
+ [MPTC] = mac_read_clr4,
+ [ICR] = mac_icr_read, [EECD] = get_eecd,
+ [EERD] = flash_eerd_read,
+ [RDFH] = mac_low13_read, [RDFT] = mac_low13_read,
+ [RDFHS] = mac_low13_read, [RDFTS] = mac_low13_read,
+ [RDFPC] = mac_low13_read,
+ [TDFH] = mac_low11_read, [TDFT] = mac_low11_read,
+ [TDFHS] = mac_low13_read, [TDFTS] = mac_low13_read,
+ [TDFPC] = mac_low13_read,
+ [AIT] = mac_low16_read,
+
+ [CRCERRS ... MPC] = &mac_readreg,
+ [IP6AT ... IP6AT+3] = &mac_readreg, [IP4AT ... IP4AT+6] = &mac_readreg,
+ [FFLT ... FFLT+6] = &mac_low11_read,
+ [RA ... RA+31] = &mac_readreg,
+ [WUPM ... WUPM+31] = &mac_readreg,
+ [MTA ... MTA+127] = &mac_readreg,
[VFTA ... VFTA+127] = &mac_readreg,
+ [FFMT ... FFMT+254] = &mac_low4_read,
+ [FFVT ... FFVT+254] = &mac_readreg,
+ [PBM ... PBM+16383] = &mac_readreg,
};
enum { NREADOPS = ARRAY_SIZE(macreg_readops) };
-#define putreg(x) [x] = mac_writereg
+#define putreg(x) [x] = mac_writereg
static void (*macreg_writeops[])(E1000State *, int, uint32_t) = {
- putreg(PBA), putreg(EERD), putreg(SWSM), putreg(WUFC),
- putreg(TDBAL), putreg(TDBAH), putreg(TXDCTL), putreg(RDBAH),
- putreg(RDBAL), putreg(LEDCTL), putreg(VET),
- [TDLEN] = set_dlen, [RDLEN] = set_dlen, [TCTL] = set_tctl,
- [TDT] = set_tctl, [MDIC] = set_mdic, [ICS] = set_ics,
- [TDH] = set_16bit, [RDH] = set_16bit, [RDT] = set_rdt,
- [IMC] = set_imc, [IMS] = set_ims, [ICR] = set_icr,
- [EECD] = set_eecd, [RCTL] = set_rx_control, [CTRL] = set_ctrl,
- [RDTR] = set_16bit, [RADV] = set_16bit, [TADV] = set_16bit,
- [ITR] = set_16bit,
- [RA ... RA+31] = &mac_writereg,
- [MTA ... MTA+127] = &mac_writereg,
+ putreg(PBA), putreg(EERD), putreg(SWSM), putreg(WUFC),
+ putreg(TDBAL), putreg(TDBAH), putreg(TXDCTL), putreg(RDBAH),
+ putreg(RDBAL), putreg(LEDCTL), putreg(VET), putreg(FCRUC),
+ putreg(TDFH), putreg(TDFT), putreg(TDFHS), putreg(TDFTS),
+ putreg(TDFPC), putreg(RDFH), putreg(RDFT), putreg(RDFHS),
+ putreg(RDFTS), putreg(RDFPC), putreg(IPAV), putreg(WUC),
+ putreg(WUS), putreg(AIT),
+
+ [TDLEN] = set_dlen, [RDLEN] = set_dlen, [TCTL] = set_tctl,
+ [TDT] = set_tctl, [MDIC] = set_mdic, [ICS] = set_ics,
+ [TDH] = set_16bit, [RDH] = set_16bit, [RDT] = set_rdt,
+ [IMC] = set_imc, [IMS] = set_ims, [ICR] = set_icr,
+ [EECD] = set_eecd, [RCTL] = set_rx_control, [CTRL] = set_ctrl,
+ [RDTR] = set_16bit, [RADV] = set_16bit, [TADV] = set_16bit,
+ [ITR] = set_16bit,
+
+ [IP6AT ... IP6AT+3] = &mac_writereg, [IP4AT ... IP4AT+6] = &mac_writereg,
+ [FFLT ... FFLT+6] = &mac_writereg,
+ [RA ... RA+31] = &mac_writereg,
+ [WUPM ... WUPM+31] = &mac_writereg,
+ [MTA ... MTA+127] = &mac_writereg,
[VFTA ... VFTA+127] = &mac_writereg,
+ [FFMT ... FFMT+254] = &mac_writereg, [FFVT ... FFVT+254] = &mac_writereg,
+ [PBM ... PBM+16383] = &mac_writereg,
};
enum { NWRITEOPS = ARRAY_SIZE(macreg_writeops) };
+enum { MAC_ACCESS_PARTIAL = 1, MAC_ACCESS_FLAG_NEEDED = 2 };
+
+#define markflag(x) ((E1000_FLAG_##x << 2) | MAC_ACCESS_FLAG_NEEDED)
+/* In the array below the meaning of the bits is: [f|f|f|f|f|f|n|p]
+ * f - flag bits (up to 6 possible flags)
+ * n - flag needed
+ * p - partially implenented */
+static const uint8_t mac_reg_access[0x8000] = {
+ [RDTR] = markflag(MIT), [TADV] = markflag(MIT),
+ [RADV] = markflag(MIT), [ITR] = markflag(MIT),
+
+ [IPAV] = markflag(MAC), [WUC] = markflag(MAC),
+ [IP6AT] = markflag(MAC), [IP4AT] = markflag(MAC),
+ [FFVT] = markflag(MAC), [WUPM] = markflag(MAC),
+ [ECOL] = markflag(MAC), [MCC] = markflag(MAC),
+ [DC] = markflag(MAC), [TNCRS] = markflag(MAC),
+ [RLEC] = markflag(MAC), [XONRXC] = markflag(MAC),
+ [XOFFTXC] = markflag(MAC), [RFC] = markflag(MAC),
+ [TSCTFC] = markflag(MAC), [MGTPRC] = markflag(MAC),
+ [WUS] = markflag(MAC), [AIT] = markflag(MAC),
+ [FFLT] = markflag(MAC), [FFMT] = markflag(MAC),
+ [SCC] = markflag(MAC), [FCRUC] = markflag(MAC),
+ [LATECOL] = markflag(MAC), [COLC] = markflag(MAC),
+ [SEC] = markflag(MAC), [CEXTERR] = markflag(MAC),
+ [XONTXC] = markflag(MAC), [XOFFRXC] = markflag(MAC),
+ [RJC] = markflag(MAC), [RNBC] = markflag(MAC),
+ [MGTPDC] = markflag(MAC), [MGTPTC] = markflag(MAC),
+ [RUC] = markflag(MAC), [ROC] = markflag(MAC),
+ [GORCL] = markflag(MAC), [GORCH] = markflag(MAC),
+ [GOTCL] = markflag(MAC), [GOTCH] = markflag(MAC),
+ [BPRC] = markflag(MAC), [MPRC] = markflag(MAC),
+ [TSCTC] = markflag(MAC), [PRC64] = markflag(MAC),
+ [PRC127] = markflag(MAC), [PRC255] = markflag(MAC),
+ [PRC511] = markflag(MAC), [PRC1023] = markflag(MAC),
+ [PRC1522] = markflag(MAC), [PTC64] = markflag(MAC),
+ [PTC127] = markflag(MAC), [PTC255] = markflag(MAC),
+ [PTC511] = markflag(MAC), [PTC1023] = markflag(MAC),
+ [PTC1522] = markflag(MAC), [MPTC] = markflag(MAC),
+ [BPTC] = markflag(MAC),
+
+ [TDFH] = markflag(MAC) | MAC_ACCESS_PARTIAL,
+ [TDFT] = markflag(MAC) | MAC_ACCESS_PARTIAL,
+ [TDFHS] = markflag(MAC) | MAC_ACCESS_PARTIAL,
+ [TDFTS] = markflag(MAC) | MAC_ACCESS_PARTIAL,
+ [TDFPC] = markflag(MAC) | MAC_ACCESS_PARTIAL,
+ [RDFH] = markflag(MAC) | MAC_ACCESS_PARTIAL,
+ [RDFT] = markflag(MAC) | MAC_ACCESS_PARTIAL,
+ [RDFHS] = markflag(MAC) | MAC_ACCESS_PARTIAL,
+ [RDFTS] = markflag(MAC) | MAC_ACCESS_PARTIAL,
+ [RDFPC] = markflag(MAC) | MAC_ACCESS_PARTIAL,
+ [PBM] = markflag(MAC) | MAC_ACCESS_PARTIAL,
+};
+
static void
e1000_mmio_write(void *opaque, hwaddr addr, uint64_t val,
unsigned size)
@@ -1254,9 +1483,20 @@ e1000_mmio_write(void *opaque, hwaddr addr, uint64_t val,
unsigned int index = (addr & 0x1ffff) >> 2;
if (index < NWRITEOPS && macreg_writeops[index]) {
- macreg_writeops[index](s, index, val);
+ if (!(mac_reg_access[index] & MAC_ACCESS_FLAG_NEEDED)
+ || (s->compat_flags & (mac_reg_access[index] >> 2))) {
+ if (mac_reg_access[index] & MAC_ACCESS_PARTIAL) {
+ DBGOUT(GENERAL, "Writing to register at offset: 0x%08x. "
+ "It is not fully implemented.\n", index<<2);
+ }
+ macreg_writeops[index](s, index, val);
+ } else { /* "flag needed" bit is set, but the flag is not active */
+ DBGOUT(MMIO, "MMIO write attempt to disabled reg. addr=0x%08x\n",
+ index<<2);
+ }
} else if (index < NREADOPS && macreg_readops[index]) {
- DBGOUT(MMIO, "e1000_mmio_writel RO %x: 0x%04"PRIx64"\n", index<<2, val);
+ DBGOUT(MMIO, "e1000_mmio_writel RO %x: 0x%04"PRIx64"\n",
+ index<<2, val);
} else {
DBGOUT(UNKNOWN, "MMIO unknown write addr=0x%08x,val=0x%08"PRIx64"\n",
index<<2, val);
@@ -1269,11 +1509,21 @@ e1000_mmio_read(void *opaque, hwaddr addr, unsigned size)
E1000State *s = opaque;
unsigned int index = (addr & 0x1ffff) >> 2;
- if (index < NREADOPS && macreg_readops[index])
- {
- return macreg_readops[index](s, index);
+ if (index < NREADOPS && macreg_readops[index]) {
+ if (!(mac_reg_access[index] & MAC_ACCESS_FLAG_NEEDED)
+ || (s->compat_flags & (mac_reg_access[index] >> 2))) {
+ if (mac_reg_access[index] & MAC_ACCESS_PARTIAL) {
+ DBGOUT(GENERAL, "Reading register at offset: 0x%08x. "
+ "It is not fully implemented.\n", index<<2);
+ }
+ return macreg_readops[index](s, index);
+ } else { /* "flag needed" bit is set, but the flag is not active */
+ DBGOUT(MMIO, "MMIO read attempt of disabled reg. addr=0x%08x\n",
+ index<<2);
+ }
+ } else {
+ DBGOUT(UNKNOWN, "MMIO unknown read addr=0x%08x\n", index<<2);
}
- DBGOUT(UNKNOWN, "MMIO unknown read addr=0x%08x\n", index<<2);
return 0;
}
@@ -1340,7 +1590,7 @@ static int e1000_post_load(void *opaque, int version_id)
E1000State *s = opaque;
NetClientState *nc = qemu_get_queue(s->nic);
- if (!(s->compat_flags & E1000_FLAG_MIT)) {
+ if (!chkflag(MIT)) {
s->mac_reg[ITR] = s->mac_reg[RDTR] = s->mac_reg[RADV] =
s->mac_reg[TADV] = 0;
s->mit_irq_level = false;
@@ -1367,7 +1617,14 @@ static bool e1000_mit_state_needed(void *opaque)
{
E1000State *s = opaque;
- return s->compat_flags & E1000_FLAG_MIT;
+ return chkflag(MIT);
+}
+
+static bool e1000_full_mac_needed(void *opaque)
+{
+ E1000State *s = opaque;
+
+ return chkflag(MAC);
}
static const VMStateDescription vmstate_e1000_mit_state = {
@@ -1385,6 +1642,17 @@ static const VMStateDescription vmstate_e1000_mit_state = {
}
};
+static const VMStateDescription vmstate_e1000_full_mac_state = {
+ .name = "e1000/full_mac_state",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = e1000_full_mac_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(mac_reg, E1000State, 0x8000),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_e1000 = {
.name = "e1000",
.version_id = 2,
@@ -1464,6 +1732,7 @@ static const VMStateDescription vmstate_e1000 = {
},
.subsections = (const VMStateDescription*[]) {
&vmstate_e1000_mit_state,
+ &vmstate_e1000_full_mac_state,
NULL
}
};
@@ -1596,6 +1865,8 @@ static Property e1000_properties[] = {
compat_flags, E1000_FLAG_AUTONEG_BIT, true),
DEFINE_PROP_BIT("mitigation", E1000State,
compat_flags, E1000_FLAG_MIT_BIT, true),
+ DEFINE_PROP_BIT("extra_mac_registers", E1000State,
+ compat_flags, E1000_FLAG_MAC_BIT, true),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1647,7 +1918,7 @@ static const TypeInfo e1000_base_info = {
static const E1000Info e1000_devices[] = {
{
- .name = "e1000-82540em",
+ .name = "e1000",
.device_id = E1000_DEV_ID_82540EM,
.revision = 0x03,
.phy_id2 = E1000_PHY_ID2_8254xx_DEFAULT,
@@ -1666,11 +1937,6 @@ static const E1000Info e1000_devices[] = {
},
};
-static const TypeInfo e1000_default_info = {
- .name = "e1000",
- .parent = "e1000-82540em",
-};
-
static void e1000_register_types(void)
{
int i;
@@ -1688,7 +1954,6 @@ static void e1000_register_types(void)
type_register(&type_info);
}
- type_register_static(&e1000_default_info);
}
type_init(e1000_register_types)
diff --git a/qemu/hw/net/e1000_regs.h b/qemu/hw/net/e1000_regs.h
index 60b96aaf1..1c40244ab 100644
--- a/qemu/hw/net/e1000_regs.h
+++ b/qemu/hw/net/e1000_regs.h
@@ -158,7 +158,8 @@
#define E1000_PHY_CTRL 0x00F10 /* PHY Control Register in CSR */
#define FEXTNVM_SW_CONFIG 0x0001
#define E1000_PBA 0x01000 /* Packet Buffer Allocation - RW */
-#define E1000_PBS 0x01008 /* Packet Buffer Size */
+#define E1000_PBM 0x10000 /* Packet Buffer Memory - RW */
+#define E1000_PBS 0x01008 /* Packet Buffer Size - RW */
#define E1000_EEMNGCTL 0x01010 /* MNG EEprom Control */
#define E1000_FLASH_UPDATES 1000
#define E1000_EEARBC 0x01024 /* EEPROM Auto Read Bus Control */
@@ -191,6 +192,11 @@
#define E1000_RAID 0x02C08 /* Receive Ack Interrupt Delay - RW */
#define E1000_TXDMAC 0x03000 /* TX DMA Control - RW */
#define E1000_KABGTXD 0x03004 /* AFE Band Gap Transmit Ref Data */
+#define E1000_RDFH 0x02410 /* Receive Data FIFO Head Register - RW */
+#define E1000_RDFT 0x02418 /* Receive Data FIFO Tail Register - RW */
+#define E1000_RDFHS 0x02420 /* Receive Data FIFO Head Saved Register - RW */
+#define E1000_RDFTS 0x02428 /* Receive Data FIFO Tail Saved Register - RW */
+#define E1000_RDFPC 0x02430 /* Receive Data FIFO Packet Count - RW */
#define E1000_TDFH 0x03410 /* TX Data FIFO Head - RW */
#define E1000_TDFT 0x03418 /* TX Data FIFO Tail - RW */
#define E1000_TDFHS 0x03420 /* TX Data FIFO Head Saved - RW */
diff --git a/qemu/hw/net/eepro100.c b/qemu/hw/net/eepro100.c
index 60333b7fc..9b4b9b59d 100644
--- a/qemu/hw/net/eepro100.c
+++ b/qemu/hw/net/eepro100.c
@@ -40,7 +40,7 @@
* * Wake-on-LAN is not implemented.
*/
-#include <stddef.h> /* offsetof */
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "net/net.h"
@@ -774,6 +774,11 @@ static void tx_command(EEPRO100State *s)
#if 0
uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, tbd_address + 6);
#endif
+ if (tx_buffer_size == 0) {
+ /* Prevent an endless loop. */
+ logout("loop in %s:%u\n", __FILE__, __LINE__);
+ break;
+ }
tbd_address += 8;
TRACE(RXTX, logout
("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n",
@@ -855,6 +860,10 @@ static void set_multicast_list(EEPRO100State *s)
static void action_command(EEPRO100State *s)
{
+ /* The loop below won't stop if it gets special handcrafted data.
+ Therefore we limit the number of iterations. */
+ unsigned max_loop_count = 16;
+
for (;;) {
bool bit_el;
bool bit_s;
@@ -870,6 +879,13 @@ static void action_command(EEPRO100State *s)
#if 0
bool bit_sf = ((s->tx.command & COMMAND_SF) != 0);
#endif
+
+ if (max_loop_count-- == 0) {
+ /* Prevent an endless loop. */
+ logout("loop in %s:%u\n", __FILE__, __LINE__);
+ break;
+ }
+
s->cu_offset = s->tx.link;
TRACE(OTHER,
logout("val=(cu start), status=0x%04x, command=0x%04x, link=0x%08x\n",
diff --git a/qemu/hw/net/etraxfs_eth.c b/qemu/hw/net/etraxfs_eth.c
index d6002750f..05495ec40 100644
--- a/qemu/hw/net/etraxfs_eth.c
+++ b/qemu/hw/net/etraxfs_eth.c
@@ -22,10 +22,11 @@
* THE SOFTWARE.
*/
-#include <stdio.h>
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "net/net.h"
#include "hw/cris/etraxfs.h"
+#include "qemu/error-report.h"
#define D(x)
@@ -589,7 +590,8 @@ static int fs_eth_init(SysBusDevice *sbd)
ETRAXFSEthState *s = ETRAX_FS_ETH(dev);
if (!s->dma_out || !s->dma_in) {
- hw_error("Unconnected ETRAX-FS Ethernet MAC.\n");
+ error_report("Unconnected ETRAX-FS Ethernet MAC");
+ return -1;
}
s->dma_out->client.push = eth_tx_push;
diff --git a/qemu/hw/net/fsl_etsec/etsec.c b/qemu/hw/net/fsl_etsec/etsec.c
index 0f5cf4477..1e35f7f8c 100644
--- a/qemu/hw/net/fsl_etsec/etsec.c
+++ b/qemu/hw/net/fsl_etsec/etsec.c
@@ -26,6 +26,7 @@
* This implementation doesn't include ring priority, TCP/IP Off-Load, QoS.
*/
+#include "qemu/osdep.h"
#include "sysemu/sysemu.h"
#include "hw/sysbus.h"
#include "trace.h"
@@ -353,7 +354,7 @@ static ssize_t etsec_receive(NetClientState *nc,
etsec->need_flush = false;
ret = etsec_rx_ring_write(etsec, buf, size);
if (ret == 0) {
- /* The packet will be queued, let's flush it when buffer is avilable
+ /* The packet will be queued, let's flush it when buffer is available
* again. */
etsec->need_flush = true;
}
diff --git a/qemu/hw/net/fsl_etsec/miim.c b/qemu/hw/net/fsl_etsec/miim.c
index 1931b74e6..6bba01c82 100644
--- a/qemu/hw/net/fsl_etsec/miim.c
+++ b/qemu/hw/net/fsl_etsec/miim.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "etsec.h"
#include "registers.h"
diff --git a/qemu/hw/net/fsl_etsec/registers.c b/qemu/hw/net/fsl_etsec/registers.c
index a7bbfa113..46ce7a84b 100644
--- a/qemu/hw/net/fsl_etsec/registers.c
+++ b/qemu/hw/net/fsl_etsec/registers.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "registers.h"
const eTSEC_Register_Definition eTSEC_registers_def[] = {
diff --git a/qemu/hw/net/fsl_etsec/registers.h b/qemu/hw/net/fsl_etsec/registers.h
index 7ad768647..6fb96842b 100644
--- a/qemu/hw/net/fsl_etsec/registers.h
+++ b/qemu/hw/net/fsl_etsec/registers.h
@@ -24,7 +24,6 @@
#ifndef _ETSEC_REGISTERS_H_
#define _ETSEC_REGISTERS_H_
-#include <stdint.h>
enum eTSEC_Register_Access_Type {
ACC_RW = 1, /* Read/Write */
diff --git a/qemu/hw/net/fsl_etsec/rings.c b/qemu/hw/net/fsl_etsec/rings.c
index 68e7b6d16..ed1de7da9 100644
--- a/qemu/hw/net/fsl_etsec/rings.c
+++ b/qemu/hw/net/fsl_etsec/rings.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "net/checksum.h"
#include "etsec.h"
@@ -464,9 +465,7 @@ static void rx_init_frame(eTSEC *etsec, const uint8_t *buf, size_t size)
etsec->rx_fcb_size = 0;
}
- if (etsec->rx_buffer != NULL) {
- g_free(etsec->rx_buffer);
- }
+ g_free(etsec->rx_buffer);
/* Do not copy the frame for now */
etsec->rx_buffer = (uint8_t *)buf;
diff --git a/qemu/hw/net/imx_fec.c b/qemu/hw/net/imx_fec.c
new file mode 100644
index 000000000..e60e3380e
--- /dev/null
+++ b/qemu/hw/net/imx_fec.c
@@ -0,0 +1,711 @@
+/*
+ * i.MX Fast Ethernet Controller emulation.
+ *
+ * Copyright (c) 2013 Jean-Christophe Dubois. <jcd@tribudubois.net>
+ *
+ * Based on Coldfire Fast Ethernet Controller emulation.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/net/imx_fec.h"
+#include "sysemu/dma.h"
+
+/* For crc32 */
+#include <zlib.h>
+
+#ifndef DEBUG_IMX_FEC
+#define DEBUG_IMX_FEC 0
+#endif
+
+#define FEC_PRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX_FEC) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_FEC, \
+ __func__, ##args); \
+ } \
+ } while (0)
+
+#ifndef DEBUG_IMX_PHY
+#define DEBUG_IMX_PHY 0
+#endif
+
+#define PHY_PRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX_PHY) { \
+ fprintf(stderr, "[%s.phy]%s: " fmt , TYPE_IMX_FEC, \
+ __func__, ##args); \
+ } \
+ } while (0)
+
+static const VMStateDescription vmstate_imx_fec = {
+ .name = TYPE_IMX_FEC,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(irq_state, IMXFECState),
+ VMSTATE_UINT32(eir, IMXFECState),
+ VMSTATE_UINT32(eimr, IMXFECState),
+ VMSTATE_UINT32(rx_enabled, IMXFECState),
+ VMSTATE_UINT32(rx_descriptor, IMXFECState),
+ VMSTATE_UINT32(tx_descriptor, IMXFECState),
+ VMSTATE_UINT32(ecr, IMXFECState),
+ VMSTATE_UINT32(mmfr, IMXFECState),
+ VMSTATE_UINT32(mscr, IMXFECState),
+ VMSTATE_UINT32(mibc, IMXFECState),
+ VMSTATE_UINT32(rcr, IMXFECState),
+ VMSTATE_UINT32(tcr, IMXFECState),
+ VMSTATE_UINT32(tfwr, IMXFECState),
+ VMSTATE_UINT32(frsr, IMXFECState),
+ VMSTATE_UINT32(erdsr, IMXFECState),
+ VMSTATE_UINT32(etdsr, IMXFECState),
+ VMSTATE_UINT32(emrbr, IMXFECState),
+ VMSTATE_UINT32(miigsk_cfgr, IMXFECState),
+ VMSTATE_UINT32(miigsk_enr, IMXFECState),
+
+ VMSTATE_UINT32(phy_status, IMXFECState),
+ VMSTATE_UINT32(phy_control, IMXFECState),
+ VMSTATE_UINT32(phy_advertise, IMXFECState),
+ VMSTATE_UINT32(phy_int, IMXFECState),
+ VMSTATE_UINT32(phy_int_mask, IMXFECState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+#define PHY_INT_ENERGYON (1 << 7)
+#define PHY_INT_AUTONEG_COMPLETE (1 << 6)
+#define PHY_INT_FAULT (1 << 5)
+#define PHY_INT_DOWN (1 << 4)
+#define PHY_INT_AUTONEG_LP (1 << 3)
+#define PHY_INT_PARFAULT (1 << 2)
+#define PHY_INT_AUTONEG_PAGE (1 << 1)
+
+static void imx_fec_update(IMXFECState *s);
+
+/*
+ * The MII phy could raise a GPIO to the processor which in turn
+ * could be handled as an interrpt by the OS.
+ * For now we don't handle any GPIO/interrupt line, so the OS will
+ * have to poll for the PHY status.
+ */
+static void phy_update_irq(IMXFECState *s)
+{
+ imx_fec_update(s);
+}
+
+static void phy_update_link(IMXFECState *s)
+{
+ /* Autonegotiation status mirrors link status. */
+ if (qemu_get_queue(s->nic)->link_down) {
+ PHY_PRINTF("link is down\n");
+ s->phy_status &= ~0x0024;
+ s->phy_int |= PHY_INT_DOWN;
+ } else {
+ PHY_PRINTF("link is up\n");
+ s->phy_status |= 0x0024;
+ s->phy_int |= PHY_INT_ENERGYON;
+ s->phy_int |= PHY_INT_AUTONEG_COMPLETE;
+ }
+ phy_update_irq(s);
+}
+
+static void imx_fec_set_link(NetClientState *nc)
+{
+ phy_update_link(IMX_FEC(qemu_get_nic_opaque(nc)));
+}
+
+static void phy_reset(IMXFECState *s)
+{
+ s->phy_status = 0x7809;
+ s->phy_control = 0x3000;
+ s->phy_advertise = 0x01e1;
+ s->phy_int_mask = 0;
+ s->phy_int = 0;
+ phy_update_link(s);
+}
+
+static uint32_t do_phy_read(IMXFECState *s, int reg)
+{
+ uint32_t val;
+
+ if (reg > 31) {
+ /* we only advertise one phy */
+ return 0;
+ }
+
+ switch (reg) {
+ case 0: /* Basic Control */
+ val = s->phy_control;
+ break;
+ case 1: /* Basic Status */
+ val = s->phy_status;
+ break;
+ case 2: /* ID1 */
+ val = 0x0007;
+ break;
+ case 3: /* ID2 */
+ val = 0xc0d1;
+ break;
+ case 4: /* Auto-neg advertisement */
+ val = s->phy_advertise;
+ break;
+ case 5: /* Auto-neg Link Partner Ability */
+ val = 0x0f71;
+ break;
+ case 6: /* Auto-neg Expansion */
+ val = 1;
+ break;
+ case 29: /* Interrupt source. */
+ val = s->phy_int;
+ s->phy_int = 0;
+ phy_update_irq(s);
+ break;
+ case 30: /* Interrupt mask */
+ val = s->phy_int_mask;
+ break;
+ case 17:
+ case 18:
+ case 27:
+ case 31:
+ qemu_log_mask(LOG_UNIMP, "[%s.phy]%s: reg %d not implemented\n",
+ TYPE_IMX_FEC, __func__, reg);
+ val = 0;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad address at offset %d\n",
+ TYPE_IMX_FEC, __func__, reg);
+ val = 0;
+ break;
+ }
+
+ PHY_PRINTF("read 0x%04x @ %d\n", val, reg);
+
+ return val;
+}
+
+static void do_phy_write(IMXFECState *s, int reg, uint32_t val)
+{
+ PHY_PRINTF("write 0x%04x @ %d\n", val, reg);
+
+ if (reg > 31) {
+ /* we only advertise one phy */
+ return;
+ }
+
+ switch (reg) {
+ case 0: /* Basic Control */
+ if (val & 0x8000) {
+ phy_reset(s);
+ } else {
+ s->phy_control = val & 0x7980;
+ /* Complete autonegotiation immediately. */
+ if (val & 0x1000) {
+ s->phy_status |= 0x0020;
+ }
+ }
+ break;
+ case 4: /* Auto-neg advertisement */
+ s->phy_advertise = (val & 0x2d7f) | 0x80;
+ break;
+ case 30: /* Interrupt mask */
+ s->phy_int_mask = val & 0xff;
+ phy_update_irq(s);
+ break;
+ case 17:
+ case 18:
+ case 27:
+ case 31:
+ qemu_log_mask(LOG_UNIMP, "[%s.phy)%s: reg %d not implemented\n",
+ TYPE_IMX_FEC, __func__, reg);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad address at offset %d\n",
+ TYPE_IMX_FEC, __func__, reg);
+ break;
+ }
+}
+
+static void imx_fec_read_bd(IMXFECBufDesc *bd, dma_addr_t addr)
+{
+ dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd));
+}
+
+static void imx_fec_write_bd(IMXFECBufDesc *bd, dma_addr_t addr)
+{
+ dma_memory_write(&address_space_memory, addr, bd, sizeof(*bd));
+}
+
+static void imx_fec_update(IMXFECState *s)
+{
+ uint32_t active;
+ uint32_t changed;
+
+ active = s->eir & s->eimr;
+ changed = active ^ s->irq_state;
+ if (changed) {
+ qemu_set_irq(s->irq, active);
+ }
+ s->irq_state = active;
+}
+
+static void imx_fec_do_tx(IMXFECState *s)
+{
+ int frame_size = 0;
+ uint8_t frame[FEC_MAX_FRAME_SIZE];
+ uint8_t *ptr = frame;
+ uint32_t addr = s->tx_descriptor;
+
+ while (1) {
+ IMXFECBufDesc bd;
+ int len;
+
+ imx_fec_read_bd(&bd, addr);
+ FEC_PRINTF("tx_bd %x flags %04x len %d data %08x\n",
+ addr, bd.flags, bd.length, bd.data);
+ if ((bd.flags & FEC_BD_R) == 0) {
+ /* Run out of descriptors to transmit. */
+ break;
+ }
+ len = bd.length;
+ if (frame_size + len > FEC_MAX_FRAME_SIZE) {
+ len = FEC_MAX_FRAME_SIZE - frame_size;
+ s->eir |= FEC_INT_BABT;
+ }
+ dma_memory_read(&address_space_memory, bd.data, ptr, len);
+ ptr += len;
+ frame_size += len;
+ if (bd.flags & FEC_BD_L) {
+ /* Last buffer in frame. */
+ qemu_send_packet(qemu_get_queue(s->nic), frame, len);
+ ptr = frame;
+ frame_size = 0;
+ s->eir |= FEC_INT_TXF;
+ }
+ s->eir |= FEC_INT_TXB;
+ bd.flags &= ~FEC_BD_R;
+ /* Write back the modified descriptor. */
+ imx_fec_write_bd(&bd, addr);
+ /* Advance to the next descriptor. */
+ if ((bd.flags & FEC_BD_W) != 0) {
+ addr = s->etdsr;
+ } else {
+ addr += 8;
+ }
+ }
+
+ s->tx_descriptor = addr;
+
+ imx_fec_update(s);
+}
+
+static void imx_fec_enable_rx(IMXFECState *s)
+{
+ IMXFECBufDesc bd;
+ uint32_t tmp;
+
+ imx_fec_read_bd(&bd, s->rx_descriptor);
+
+ tmp = ((bd.flags & FEC_BD_E) != 0);
+
+ if (!tmp) {
+ FEC_PRINTF("RX buffer full\n");
+ } else if (!s->rx_enabled) {
+ qemu_flush_queued_packets(qemu_get_queue(s->nic));
+ }
+
+ s->rx_enabled = tmp;
+}
+
+static void imx_fec_reset(DeviceState *d)
+{
+ IMXFECState *s = IMX_FEC(d);
+
+ /* Reset the FEC */
+ s->eir = 0;
+ s->eimr = 0;
+ s->rx_enabled = 0;
+ s->ecr = 0;
+ s->mscr = 0;
+ s->mibc = 0xc0000000;
+ s->rcr = 0x05ee0001;
+ s->tcr = 0;
+ s->tfwr = 0;
+ s->frsr = 0x500;
+ s->miigsk_cfgr = 0;
+ s->miigsk_enr = 0x6;
+
+ /* We also reset the PHY */
+ phy_reset(s);
+}
+
+static uint64_t imx_fec_read(void *opaque, hwaddr addr, unsigned size)
+{
+ IMXFECState *s = IMX_FEC(opaque);
+
+ FEC_PRINTF("reading from @ 0x%" HWADDR_PRIx "\n", addr);
+
+ switch (addr & 0x3ff) {
+ case 0x004:
+ return s->eir;
+ case 0x008:
+ return s->eimr;
+ case 0x010:
+ return s->rx_enabled ? (1 << 24) : 0; /* RDAR */
+ case 0x014:
+ return 0; /* TDAR */
+ case 0x024:
+ return s->ecr;
+ case 0x040:
+ return s->mmfr;
+ case 0x044:
+ return s->mscr;
+ case 0x064:
+ return s->mibc; /* MIBC */
+ case 0x084:
+ return s->rcr;
+ case 0x0c4:
+ return s->tcr;
+ case 0x0e4: /* PALR */
+ return (s->conf.macaddr.a[0] << 24)
+ | (s->conf.macaddr.a[1] << 16)
+ | (s->conf.macaddr.a[2] << 8)
+ | s->conf.macaddr.a[3];
+ break;
+ case 0x0e8: /* PAUR */
+ return (s->conf.macaddr.a[4] << 24)
+ | (s->conf.macaddr.a[5] << 16)
+ | 0x8808;
+ case 0x0ec:
+ return 0x10000; /* OPD */
+ case 0x118:
+ return 0;
+ case 0x11c:
+ return 0;
+ case 0x120:
+ return 0;
+ case 0x124:
+ return 0;
+ case 0x144:
+ return s->tfwr;
+ case 0x14c:
+ return 0x600;
+ case 0x150:
+ return s->frsr;
+ case 0x180:
+ return s->erdsr;
+ case 0x184:
+ return s->etdsr;
+ case 0x188:
+ return s->emrbr;
+ case 0x300:
+ return s->miigsk_cfgr;
+ case 0x308:
+ return s->miigsk_enr;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_FEC, __func__, addr);
+ return 0;
+ }
+}
+
+static void imx_fec_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ IMXFECState *s = IMX_FEC(opaque);
+
+ FEC_PRINTF("writing 0x%08x @ 0x%" HWADDR_PRIx "\n", (int)value, addr);
+
+ switch (addr & 0x3ff) {
+ case 0x004: /* EIR */
+ s->eir &= ~value;
+ break;
+ case 0x008: /* EIMR */
+ s->eimr = value;
+ break;
+ case 0x010: /* RDAR */
+ if ((s->ecr & FEC_EN) && !s->rx_enabled) {
+ imx_fec_enable_rx(s);
+ }
+ break;
+ case 0x014: /* TDAR */
+ if (s->ecr & FEC_EN) {
+ imx_fec_do_tx(s);
+ }
+ break;
+ case 0x024: /* ECR */
+ s->ecr = value;
+ if (value & FEC_RESET) {
+ imx_fec_reset(DEVICE(s));
+ }
+ if ((s->ecr & FEC_EN) == 0) {
+ s->rx_enabled = 0;
+ }
+ break;
+ case 0x040: /* MMFR */
+ /* store the value */
+ s->mmfr = value;
+ if (extract32(value, 28, 1)) {
+ do_phy_write(s, extract32(value, 18, 9), extract32(value, 0, 16));
+ } else {
+ s->mmfr = do_phy_read(s, extract32(value, 18, 9));
+ }
+ /* raise the interrupt as the PHY operation is done */
+ s->eir |= FEC_INT_MII;
+ break;
+ case 0x044: /* MSCR */
+ s->mscr = value & 0xfe;
+ break;
+ case 0x064: /* MIBC */
+ /* TODO: Implement MIB. */
+ s->mibc = (value & 0x80000000) ? 0xc0000000 : 0;
+ break;
+ case 0x084: /* RCR */
+ s->rcr = value & 0x07ff003f;
+ /* TODO: Implement LOOP mode. */
+ break;
+ case 0x0c4: /* TCR */
+ /* We transmit immediately, so raise GRA immediately. */
+ s->tcr = value;
+ if (value & 1) {
+ s->eir |= FEC_INT_GRA;
+ }
+ break;
+ case 0x0e4: /* PALR */
+ s->conf.macaddr.a[0] = value >> 24;
+ s->conf.macaddr.a[1] = value >> 16;
+ s->conf.macaddr.a[2] = value >> 8;
+ s->conf.macaddr.a[3] = value;
+ break;
+ case 0x0e8: /* PAUR */
+ s->conf.macaddr.a[4] = value >> 24;
+ s->conf.macaddr.a[5] = value >> 16;
+ break;
+ case 0x0ec: /* OPDR */
+ break;
+ case 0x118: /* IAUR */
+ case 0x11c: /* IALR */
+ case 0x120: /* GAUR */
+ case 0x124: /* GALR */
+ /* TODO: implement MAC hash filtering. */
+ break;
+ case 0x144: /* TFWR */
+ s->tfwr = value & 3;
+ break;
+ case 0x14c: /* FRBR */
+ /* FRBR writes ignored. */
+ break;
+ case 0x150: /* FRSR */
+ s->frsr = (value & 0x3fc) | 0x400;
+ break;
+ case 0x180: /* ERDSR */
+ s->erdsr = value & ~3;
+ s->rx_descriptor = s->erdsr;
+ break;
+ case 0x184: /* ETDSR */
+ s->etdsr = value & ~3;
+ s->tx_descriptor = s->etdsr;
+ break;
+ case 0x188: /* EMRBR */
+ s->emrbr = value & 0x7f0;
+ break;
+ case 0x300: /* MIIGSK_CFGR */
+ s->miigsk_cfgr = value & 0x53;
+ break;
+ case 0x308: /* MIIGSK_ENR */
+ s->miigsk_enr = (value & 0x2) ? 0x6 : 0;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_FEC, __func__, addr);
+ break;
+ }
+
+ imx_fec_update(s);
+}
+
+static int imx_fec_can_receive(NetClientState *nc)
+{
+ IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
+
+ return s->rx_enabled;
+}
+
+static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf,
+ size_t len)
+{
+ IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
+ IMXFECBufDesc bd;
+ uint32_t flags = 0;
+ uint32_t addr;
+ uint32_t crc;
+ uint32_t buf_addr;
+ uint8_t *crc_ptr;
+ unsigned int buf_len;
+ size_t size = len;
+
+ FEC_PRINTF("len %d\n", (int)size);
+
+ if (!s->rx_enabled) {
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Unexpected packet\n",
+ TYPE_IMX_FEC, __func__);
+ return 0;
+ }
+
+ /* 4 bytes for the CRC. */
+ size += 4;
+ crc = cpu_to_be32(crc32(~0, buf, size));
+ crc_ptr = (uint8_t *) &crc;
+
+ /* Huge frames are truncted. */
+ if (size > FEC_MAX_FRAME_SIZE) {
+ size = FEC_MAX_FRAME_SIZE;
+ flags |= FEC_BD_TR | FEC_BD_LG;
+ }
+
+ /* Frames larger than the user limit just set error flags. */
+ if (size > (s->rcr >> 16)) {
+ flags |= FEC_BD_LG;
+ }
+
+ addr = s->rx_descriptor;
+ while (size > 0) {
+ imx_fec_read_bd(&bd, addr);
+ if ((bd.flags & FEC_BD_E) == 0) {
+ /* No descriptors available. Bail out. */
+ /*
+ * FIXME: This is wrong. We should probably either
+ * save the remainder for when more RX buffers are
+ * available, or flag an error.
+ */
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Lost end of frame\n",
+ TYPE_IMX_FEC, __func__);
+ break;
+ }
+ buf_len = (size <= s->emrbr) ? size : s->emrbr;
+ bd.length = buf_len;
+ size -= buf_len;
+
+ FEC_PRINTF("rx_bd 0x%x length %d\n", addr, bd.length);
+
+ /* The last 4 bytes are the CRC. */
+ if (size < 4) {
+ buf_len += size - 4;
+ }
+ buf_addr = bd.data;
+ dma_memory_write(&address_space_memory, buf_addr, buf, buf_len);
+ buf += buf_len;
+ if (size < 4) {
+ dma_memory_write(&address_space_memory, buf_addr + buf_len,
+ crc_ptr, 4 - size);
+ crc_ptr += 4 - size;
+ }
+ bd.flags &= ~FEC_BD_E;
+ if (size == 0) {
+ /* Last buffer in frame. */
+ bd.flags |= flags | FEC_BD_L;
+ FEC_PRINTF("rx frame flags %04x\n", bd.flags);
+ s->eir |= FEC_INT_RXF;
+ } else {
+ s->eir |= FEC_INT_RXB;
+ }
+ imx_fec_write_bd(&bd, addr);
+ /* Advance to the next descriptor. */
+ if ((bd.flags & FEC_BD_W) != 0) {
+ addr = s->erdsr;
+ } else {
+ addr += 8;
+ }
+ }
+ s->rx_descriptor = addr;
+ imx_fec_enable_rx(s);
+ imx_fec_update(s);
+ return len;
+}
+
+static const MemoryRegionOps imx_fec_ops = {
+ .read = imx_fec_read,
+ .write = imx_fec_write,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void imx_fec_cleanup(NetClientState *nc)
+{
+ IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
+
+ s->nic = NULL;
+}
+
+static NetClientInfo net_imx_fec_info = {
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .size = sizeof(NICState),
+ .can_receive = imx_fec_can_receive,
+ .receive = imx_fec_receive,
+ .cleanup = imx_fec_cleanup,
+ .link_status_changed = imx_fec_set_link,
+};
+
+
+static void imx_fec_realize(DeviceState *dev, Error **errp)
+{
+ IMXFECState *s = IMX_FEC(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+ memory_region_init_io(&s->iomem, OBJECT(dev), &imx_fec_ops, s,
+ TYPE_IMX_FEC, 0x400);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->irq);
+ qemu_macaddr_default_if_unset(&s->conf.macaddr);
+
+ s->conf.peers.ncs[0] = nd_table[0].netdev;
+
+ s->nic = qemu_new_nic(&net_imx_fec_info, &s->conf,
+ object_get_typename(OBJECT(dev)), DEVICE(dev)->id,
+ s);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+}
+
+static Property imx_fec_properties[] = {
+ DEFINE_NIC_PROPERTIES(IMXFECState, conf),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void imx_fec_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->vmsd = &vmstate_imx_fec;
+ dc->reset = imx_fec_reset;
+ dc->props = imx_fec_properties;
+ dc->realize = imx_fec_realize;
+ dc->desc = "i.MX FEC Ethernet Controller";
+}
+
+static const TypeInfo imx_fec_info = {
+ .name = TYPE_IMX_FEC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(IMXFECState),
+ .class_init = imx_fec_class_init,
+};
+
+static void imx_fec_register_types(void)
+{
+ type_register_static(&imx_fec_info);
+}
+
+type_init(imx_fec_register_types)
diff --git a/qemu/hw/net/lan9118.c b/qemu/hw/net/lan9118.c
index 4f0e840f0..08dc474d6 100644
--- a/qemu/hw/net/lan9118.c
+++ b/qemu/hw/net/lan9118.c
@@ -10,6 +10,7 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "net/net.h"
#include "hw/devices.h"
@@ -56,6 +57,8 @@ do { fprintf(stderr, "lan9118: error: " fmt , ## __VA_ARGS__);} while (0)
#define CSR_E2P_CMD 0xb0
#define CSR_E2P_DATA 0xb4
+#define E2P_CMD_MAC_ADDR_LOADED 0x100
+
/* IRQ_CFG */
#define IRQ_INT 0x00001000
#define IRQ_EN 0x00000100
@@ -352,14 +355,14 @@ static void lan9118_reload_eeprom(lan9118_state *s)
{
int i;
if (s->eeprom[0] != 0xa5) {
- s->e2p_cmd &= ~0x10;
+ s->e2p_cmd &= ~E2P_CMD_MAC_ADDR_LOADED;
DPRINTF("MACADDR load failed\n");
return;
}
for (i = 0; i < 6; i++) {
s->conf.macaddr.a[i] = s->eeprom[i + 1];
}
- s->e2p_cmd |= 0x10;
+ s->e2p_cmd |= E2P_CMD_MAC_ADDR_LOADED;
DPRINTF("MACADDR loaded from eeprom\n");
lan9118_mac_changed(s);
}
@@ -902,7 +905,8 @@ static void do_mac_write(lan9118_state *s, int reg, uint32_t val)
*/
break;
default:
- hw_error("lan9118: Unimplemented MAC register write: %d = 0x%x\n",
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "lan9118: Unimplemented MAC register write: %d = 0x%x\n",
s->mac_cmd & 0xf, val);
}
}
@@ -930,14 +934,16 @@ static uint32_t do_mac_read(lan9118_state *s, int reg)
case MAC_FLOW:
return s->mac_flow;
default:
- hw_error("lan9118: Unimplemented MAC register read: %d\n",
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "lan9118: Unimplemented MAC register read: %d\n",
s->mac_cmd & 0xf);
+ return 0;
}
}
static void lan9118_eeprom_cmd(lan9118_state *s, int cmd, int addr)
{
- s->e2p_cmd = (s->e2p_cmd & 0x10) | (cmd << 28) | addr;
+ s->e2p_cmd = (s->e2p_cmd & E2P_CMD_MAC_ADDR_LOADED) | (cmd << 28) | addr;
switch (cmd) {
case 0:
s->e2p_data = s->eeprom[addr];
@@ -1128,7 +1134,8 @@ static void lan9118_writel(void *opaque, hwaddr offset,
break;
default:
- hw_error("lan9118_write: Bad reg 0x%x = %x\n", (int)offset, (int)val);
+ qemu_log_mask(LOG_GUEST_ERROR, "lan9118_write: Bad reg 0x%x = %x\n",
+ (int)offset, (int)val);
break;
}
lan9118_update(s);
@@ -1246,7 +1253,7 @@ static uint64_t lan9118_readl(void *opaque, hwaddr offset,
case CSR_E2P_DATA:
return s->e2p_data;
}
- hw_error("lan9118_read: Bad reg 0x%x\n", (int)offset);
+ qemu_log_mask(LOG_GUEST_ERROR, "lan9118_read: Bad reg 0x%x\n", (int)offset);
return 0;
}
diff --git a/qemu/hw/net/lance.c b/qemu/hw/net/lance.c
index 780b39d65..6253d2103 100644
--- a/qemu/hw/net/lance.c
+++ b/qemu/hw/net/lance.c
@@ -35,6 +35,7 @@
* http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR92C990.txt
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "net/net.h"
#include "qemu/timer.h"
diff --git a/qemu/hw/net/mcf_fec.c b/qemu/hw/net/mcf_fec.c
index 21928f9f3..7c0398ed9 100644
--- a/qemu/hw/net/mcf_fec.c
+++ b/qemu/hw/net/mcf_fec.c
@@ -5,6 +5,7 @@
*
* This code is licensed under the GPL
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "net/net.h"
#include "hw/m68k/mcf.h"
diff --git a/qemu/hw/net/milkymist-minimac2.c b/qemu/hw/net/milkymist-minimac2.c
index 5d1cf0851..1e147c33c 100644
--- a/qemu/hw/net/milkymist-minimac2.c
+++ b/qemu/hw/net/milkymist-minimac2.c
@@ -22,6 +22,10 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h" /* FIXME: why does this use TARGET_PAGE_ALIGN? */
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "trace.h"
@@ -463,7 +467,7 @@ static int milkymist_minimac2_init(SysBusDevice *sbd)
/* register buffers memory */
memory_region_init_ram(&s->buffers, OBJECT(dev), "milkymist-minimac2.buffers",
- buffers_size, &error_abort);
+ buffers_size, &error_fatal);
vmstate_register_ram_global(&s->buffers);
s->rx0_buf = memory_region_get_ram_ptr(&s->buffers);
s->rx1_buf = s->rx0_buf + MINIMAC2_BUFFER_SIZE;
diff --git a/qemu/hw/net/mipsnet.c b/qemu/hw/net/mipsnet.c
index f261011a2..740cd98ff 100644
--- a/qemu/hw/net/mipsnet.c
+++ b/qemu/hw/net/mipsnet.c
@@ -1,3 +1,4 @@
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "net/net.h"
#include "trace.h"
diff --git a/qemu/hw/net/ne2000-isa.c b/qemu/hw/net/ne2000-isa.c
index 17e7199f7..a7f5a9464 100644
--- a/qemu/hw/net/ne2000-isa.c
+++ b/qemu/hw/net/ne2000-isa.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/isa/isa.h"
@@ -28,6 +29,7 @@
#include "net/net.h"
#include "ne2000.h"
#include "exec/address-spaces.h"
+#include "qapi/error.h"
#include "qapi/visitor.h"
#define TYPE_ISA_NE2000 "ne2k_isa"
@@ -44,7 +46,6 @@ typedef struct ISANE2000State {
static NetClientInfo net_ne2000_isa_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
- .can_receive = ne2000_can_receive,
.receive = ne2000_receive,
};
@@ -94,24 +95,26 @@ static void isa_ne2000_class_initfn(ObjectClass *klass, void *data)
set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
}
-static void isa_ne2000_get_bootindex(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void isa_ne2000_get_bootindex(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
{
ISANE2000State *isa = ISA_NE2000(obj);
NE2000State *s = &isa->ne2000;
- visit_type_int32(v, &s->c.bootindex, name, errp);
+ visit_type_int32(v, name, &s->c.bootindex, errp);
}
-static void isa_ne2000_set_bootindex(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void isa_ne2000_set_bootindex(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
{
ISANE2000State *isa = ISA_NE2000(obj);
NE2000State *s = &isa->ne2000;
int32_t boot_index;
Error *local_err = NULL;
- visit_type_int32(v, &boot_index, name, &local_err);
+ visit_type_int32(v, name, &boot_index, &local_err);
if (local_err) {
goto out;
}
diff --git a/qemu/hw/net/ne2000.c b/qemu/hw/net/ne2000.c
index 2bdb4c927..f0feaf96b 100644
--- a/qemu/hw/net/ne2000.c
+++ b/qemu/hw/net/ne2000.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "net/net.h"
@@ -154,6 +155,10 @@ static int ne2000_buffer_full(NE2000State *s)
{
int avail, index, boundary;
+ if (s->stop <= s->start) {
+ return 1;
+ }
+
index = s->curpag << 8;
boundary = s->boundary << 8;
if (index < boundary)
@@ -165,15 +170,6 @@ static int ne2000_buffer_full(NE2000State *s)
return 0;
}
-int ne2000_can_receive(NetClientState *nc)
-{
- NE2000State *s = qemu_get_nic_opaque(nc);
-
- if (s->cmd & E8390_STOP)
- return 1;
- return !ne2000_buffer_full(s);
-}
-
#define MIN_BUF_SIZE 60
ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_)
@@ -476,8 +472,9 @@ static inline void ne2000_mem_writel(NE2000State *s, uint32_t addr,
uint32_t val)
{
addr &= ~1; /* XXX: check exact behaviour if not even */
- if (addr < 32 ||
- (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+ if (addr < 32
+ || (addr >= NE2000_PMEM_START
+ && addr + sizeof(uint32_t) <= NE2000_MEM_SIZE)) {
stl_le_p(s->mem + addr, val);
}
}
@@ -506,8 +503,9 @@ static inline uint32_t ne2000_mem_readw(NE2000State *s, uint32_t addr)
static inline uint32_t ne2000_mem_readl(NE2000State *s, uint32_t addr)
{
addr &= ~1; /* XXX: check exact behaviour if not even */
- if (addr < 32 ||
- (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+ if (addr < 32
+ || (addr >= NE2000_PMEM_START
+ && addr + sizeof(uint32_t) <= NE2000_MEM_SIZE)) {
return ldl_le_p(s->mem + addr);
} else {
return 0xffffffff;
@@ -716,7 +714,6 @@ void ne2000_setup_io(NE2000State *s, DeviceState *dev, unsigned size)
static NetClientInfo net_ne2000_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
- .can_receive = ne2000_can_receive,
.receive = ne2000_receive,
};
diff --git a/qemu/hw/net/ne2000.h b/qemu/hw/net/ne2000.h
index e500306aa..d022b28fc 100644
--- a/qemu/hw/net/ne2000.h
+++ b/qemu/hw/net/ne2000.h
@@ -34,7 +34,6 @@ typedef struct NE2000State {
void ne2000_setup_io(NE2000State *s, DeviceState *dev, unsigned size);
extern const VMStateDescription vmstate_ne2000;
void ne2000_reset(NE2000State *s);
-int ne2000_can_receive(NetClientState *nc);
ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_);
#endif
diff --git a/qemu/hw/net/opencores_eth.c b/qemu/hw/net/opencores_eth.c
index 3642046ef..c6094fbb5 100644
--- a/qemu/hw/net/opencores_eth.c
+++ b/qemu/hw/net/opencores_eth.c
@@ -31,6 +31,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "net/net.h"
@@ -84,7 +85,7 @@ static void mii_reset(Mii *s)
{
memset(s->regs, 0, sizeof(s->regs));
s->regs[MII_BMCR] = 0x1000;
- s->regs[MII_BMSR] = 0x7848; /* no ext regs */
+ s->regs[MII_BMSR] = 0x7868; /* no ext regs */
s->regs[MII_PHYIDR1] = 0x2000;
s->regs[MII_PHYIDR2] = 0x5c90;
s->regs[MII_ANAR] = 0x01e1;
diff --git a/qemu/hw/net/pcnet-pci.c b/qemu/hw/net/pcnet-pci.c
index b4d60b812..595439a65 100644
--- a/qemu/hw/net/pcnet-pci.c
+++ b/qemu/hw/net/pcnet-pci.c
@@ -27,6 +27,7 @@
* AMD Publication# 19436 Rev:E Amendment/0 Issue Date: June 2000
*/
+#include "qemu/osdep.h"
#include "hw/pci/pci.h"
#include "net/net.h"
#include "hw/loader.h"
diff --git a/qemu/hw/net/pcnet.c b/qemu/hw/net/pcnet.c
index 34373767d..198a01f92 100644
--- a/qemu/hw/net/pcnet.c
+++ b/qemu/hw/net/pcnet.c
@@ -35,6 +35,7 @@
* http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR92C990.txt
*/
+#include "qemu/osdep.h"
#include "hw/qdev.h"
#include "net/net.h"
#include "qemu/timer.h"
@@ -670,8 +671,7 @@ static inline hwaddr pcnet_rdra_addr(PCNetState *s, int idx)
static inline int64_t pcnet_get_next_poll_time(PCNetState *s, int64_t current_time)
{
int64_t next_time = current_time +
- muldiv64(65536 - (CSR_SPND(s) ? 0 : CSR_POLL(s)),
- get_ticks_per_sec(), 33000000L);
+ (65536 - (CSR_SPND(s) ? 0 : CSR_POLL(s))) * 30;
if (next_time <= current_time)
next_time = current_time + 1;
return next_time;
@@ -1065,6 +1065,12 @@ ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_)
int pktcount = 0;
if (!s->looptest) {
+ if (size > 4092) {
+#ifdef PCNET_DEBUG_RMD
+ fprintf(stderr, "pcnet: truncates rx packet.\n");
+#endif
+ size = 4092;
+ }
memcpy(src, buf, size);
/* no need to compute the CRC */
src[size] = 0;
@@ -1085,7 +1091,7 @@ ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_)
uint32_t fcs = ~0;
uint8_t *p = src;
- while (p != &src[size-4])
+ while (p != &src[size])
CRC(fcs, *p++);
crc_err = (*(uint32_t *)p != htonl(fcs));
}
@@ -1234,8 +1240,10 @@ static void pcnet_transmit(PCNetState *s)
bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT);
/* if multi-tmd packet outsizes s->buffer then skip it silently.
- Note: this is not what real hw does */
- if (s->xmit_pos + bcnt > sizeof(s->buffer)) {
+ * Note: this is not what real hw does.
+ * Last four bytes of s->buffer are used to store CRC FCS code.
+ */
+ if (s->xmit_pos + bcnt > sizeof(s->buffer) - 4) {
s->xmit_pos = -1;
goto txdone;
}
diff --git a/qemu/hw/net/rocker/qmp-norocker.c b/qemu/hw/net/rocker/qmp-norocker.c
index 49b498b64..6acbcdb02 100644
--- a/qemu/hw/net/rocker/qmp-norocker.c
+++ b/qemu/hw/net/rocker/qmp-norocker.c
@@ -15,6 +15,7 @@
* GNU General Public License for more details.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qmp-commands.h"
#include "qapi/qmp/qerror.h"
diff --git a/qemu/hw/net/rocker/rocker.c b/qemu/hw/net/rocker/rocker.c
index 47d080fd3..30f2ce417 100644
--- a/qemu/hw/net/rocker/rocker.c
+++ b/qemu/hw/net/rocker/rocker.c
@@ -15,6 +15,7 @@
* GNU General Public License for more details.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "hw/pci/msix.h"
@@ -42,6 +43,7 @@ struct rocker {
/* switch configuration */
char *name; /* switch name */
+ char *world_name; /* world name */
uint32_t fp_ports; /* front-panel port count */
NICPeers *fp_ports_peers;
MACAddr fp_start_macaddr; /* front-panel port 0 mac addr */
@@ -101,8 +103,7 @@ RockerSwitch *qmp_query_rocker(const char *name, Error **errp)
r = rocker_find(name);
if (!r) {
- error_set(errp, ERROR_CLASS_GENERIC_ERROR,
- "rocker %s not found", name);
+ error_setg(errp, "rocker %s not found", name);
return NULL;
}
@@ -122,8 +123,7 @@ RockerPortList *qmp_query_rocker_ports(const char *name, Error **errp)
r = rocker_find(name);
if (!r) {
- error_set(errp, ERROR_CLASS_GENERIC_ERROR,
- "rocker %s not found", name);
+ error_setg(errp, "rocker %s not found", name);
return NULL;
}
@@ -234,6 +234,9 @@ static int tx_consume(Rocker *r, DescInfo *info)
frag_addr = rocker_tlv_get_le64(tlvs[ROCKER_TLV_TX_FRAG_ATTR_ADDR]);
frag_len = rocker_tlv_get_le16(tlvs[ROCKER_TLV_TX_FRAG_ATTR_LEN]);
+ if (iovcnt >= ROCKER_TX_FRAGS_MAX) {
+ goto err_too_many_frags;
+ }
iov[iovcnt].iov_len = frag_len;
iov[iovcnt].iov_base = g_malloc(frag_len);
if (!iov[iovcnt].iov_base) {
@@ -246,10 +249,7 @@ static int tx_consume(Rocker *r, DescInfo *info)
err = -ROCKER_ENXIO;
goto err_bad_io;
}
-
- if (++iovcnt > ROCKER_TX_FRAGS_MAX) {
- goto err_too_many_frags;
- }
+ iovcnt++;
}
if (iovcnt) {
@@ -265,9 +265,7 @@ err_bad_io:
err_no_mem:
err_bad_attr:
for (i = 0; i < ROCKER_TX_FRAGS_MAX; i++) {
- if (iov[i].iov_base) {
- g_free(iov[i].iov_base);
- }
+ g_free(iov[i].iov_base);
}
return err;
@@ -403,7 +401,13 @@ static int cmd_set_port_settings(Rocker *r,
if (tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MODE]) {
mode = rocker_tlv_get_u8(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MODE]);
- fp_port_set_world(fp_port, r->worlds[mode]);
+ if (mode >= ROCKER_WORLD_TYPE_MAX) {
+ return -ROCKER_EINVAL;
+ }
+ /* We don't support world change. */
+ if (!fp_port_check_world(fp_port, r->worlds[mode])) {
+ return -ROCKER_EINVAL;
+ }
}
if (tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING]) {
@@ -1283,6 +1287,18 @@ static void rocker_msix_uninit(Rocker *r)
rocker_msix_vectors_unuse(r, ROCKER_MSIX_VEC_COUNT(r->fp_ports));
}
+static World *rocker_world_type_by_name(Rocker *r, const char *name)
+{
+ int i;
+
+ for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) {
+ if (strcmp(name, world_name(r->worlds[i])) == 0) {
+ return r->worlds[i];
+ }
+ }
+ return NULL;
+}
+
static int pci_rocker_init(PCIDevice *dev)
{
Rocker *r = to_rocker(dev);
@@ -1294,14 +1310,27 @@ static int pci_rocker_init(PCIDevice *dev)
/* allocate worlds */
r->worlds[ROCKER_WORLD_TYPE_OF_DPA] = of_dpa_world_alloc(r);
- r->world_dflt = r->worlds[ROCKER_WORLD_TYPE_OF_DPA];
for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) {
if (!r->worlds[i]) {
+ err = -ENOMEM;
goto err_world_alloc;
}
}
+ if (!r->world_name) {
+ r->world_name = g_strdup(world_name(r->worlds[ROCKER_WORLD_TYPE_OF_DPA]));
+ }
+
+ r->world_dflt = rocker_world_type_by_name(r, r->world_name);
+ if (!r->world_dflt) {
+ fprintf(stderr,
+ "rocker: requested world \"%s\" does not exist\n",
+ r->world_name);
+ err = -EINVAL;
+ goto err_world_type_by_name;
+ }
+
/* set up memory-mapped region at BAR0 */
memory_region_init_io(&r->mmio, OBJECT(r), &rocker_mmio_ops, r,
@@ -1364,7 +1393,7 @@ static int pci_rocker_init(PCIDevice *dev)
r->fp_ports = ROCKER_FP_PORTS_MAX;
}
- r->rings = g_malloc(sizeof(DescRing *) * rocker_pci_ring_count(r));
+ r->rings = g_new(DescRing *, rocker_pci_ring_count(r));
if (!r->rings) {
goto err_rings_alloc;
}
@@ -1435,6 +1464,7 @@ err_duplicate:
err_msix_init:
object_unparent(OBJECT(&r->msix_bar));
object_unparent(OBJECT(&r->mmio));
+err_world_type_by_name:
err_world_alloc:
for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) {
if (r->worlds[i]) {
@@ -1506,6 +1536,7 @@ static void rocker_reset(DeviceState *dev)
static Property rocker_properties[] = {
DEFINE_PROP_STRING("name", Rocker, name),
+ DEFINE_PROP_STRING("world", Rocker, world_name),
DEFINE_PROP_MACADDR("fp_start_macaddr", Rocker,
fp_start_macaddr),
DEFINE_PROP_UINT64("switch_id", Rocker,
diff --git a/qemu/hw/net/rocker/rocker_desc.c b/qemu/hw/net/rocker/rocker_desc.c
index 9d896fe47..ac02797b7 100644
--- a/qemu/hw/net/rocker/rocker_desc.c
+++ b/qemu/hw/net/rocker/rocker_desc.c
@@ -14,6 +14,7 @@
* GNU General Public License for more details.
*/
+#include "qemu/osdep.h"
#include "net/net.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
@@ -136,15 +137,13 @@ bool desc_ring_set_size(DescRing *ring, uint32_t size)
}
for (i = 0; i < ring->size; i++) {
- if (ring->info[i].buf) {
- g_free(ring->info[i].buf);
- }
+ g_free(ring->info[i].buf);
}
ring->size = size;
ring->head = ring->tail = 0;
- ring->info = g_realloc(ring->info, size * sizeof(DescInfo));
+ ring->info = g_renew(DescInfo, ring->info, size);
if (!ring->info) {
return false;
}
@@ -347,7 +346,7 @@ DescRing *desc_ring_alloc(Rocker *r, int index)
{
DescRing *ring;
- ring = g_malloc0(sizeof(DescRing));
+ ring = g_new0(DescRing, 1);
if (!ring) {
return NULL;
}
@@ -360,9 +359,7 @@ DescRing *desc_ring_alloc(Rocker *r, int index)
void desc_ring_free(DescRing *ring)
{
- if (ring->info) {
- g_free(ring->info);
- }
+ g_free(ring->info);
g_free(ring);
}
diff --git a/qemu/hw/net/rocker/rocker_fp.c b/qemu/hw/net/rocker/rocker_fp.c
index c693ae508..0149899c6 100644
--- a/qemu/hw/net/rocker/rocker_fp.c
+++ b/qemu/hw/net/rocker/rocker_fp.c
@@ -14,6 +14,7 @@
* GNU General Public License for more details.
*/
+#include "qemu/osdep.h"
#include "net/clients.h"
#include "rocker.h"
@@ -185,6 +186,11 @@ void fp_port_set_world(FpPort *port, World *world)
port->world = world;
}
+bool fp_port_check_world(FpPort *port, World *world)
+{
+ return port->world == world;
+}
+
bool fp_port_enabled(FpPort *port)
{
return port->enabled;
@@ -218,7 +224,7 @@ FpPort *fp_port_alloc(Rocker *r, char *sw_name,
MACAddr *start_mac, unsigned int index,
NICPeers *peers)
{
- FpPort *port = g_malloc0(sizeof(FpPort));
+ FpPort *port = g_new0(FpPort, 1);
if (!port) {
return NULL;
diff --git a/qemu/hw/net/rocker/rocker_fp.h b/qemu/hw/net/rocker/rocker_fp.h
index ab80fd833..04592bbfd 100644
--- a/qemu/hw/net/rocker/rocker_fp.h
+++ b/qemu/hw/net/rocker/rocker_fp.h
@@ -40,6 +40,7 @@ int fp_port_set_settings(FpPort *port, uint32_t speed,
bool fp_port_from_pport(uint32_t pport, uint32_t *port);
World *fp_port_get_world(FpPort *port);
void fp_port_set_world(FpPort *port, World *world);
+bool fp_port_check_world(FpPort *port, World *world);
bool fp_port_enabled(FpPort *port);
void fp_port_enable(FpPort *port);
void fp_port_disable(FpPort *port);
diff --git a/qemu/hw/net/rocker/rocker_of_dpa.c b/qemu/hw/net/rocker/rocker_of_dpa.c
index 874fb01d6..0a134ebca 100644
--- a/qemu/hw/net/rocker/rocker_of_dpa.c
+++ b/qemu/hw/net/rocker/rocker_of_dpa.c
@@ -14,6 +14,7 @@
* GNU General Public License for more details.
*/
+#include "qemu/osdep.h"
#include "net/eth.h"
#include "qemu/iov.h"
#include "qemu/timer.h"
@@ -367,7 +368,7 @@ static OfDpaFlow *of_dpa_flow_alloc(uint64_t cookie)
OfDpaFlow *flow;
int64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) / 1000;
- flow = g_malloc0(sizeof(OfDpaFlow));
+ flow = g_new0(OfDpaFlow, 1);
if (!flow) {
return NULL;
}
@@ -811,7 +812,7 @@ static int of_dpa_group_get_stats(OfDpa *of_dpa, uint32_t id)
static OfDpaGroup *of_dpa_group_alloc(uint32_t id)
{
- OfDpaGroup *group = g_malloc0(sizeof(OfDpaGroup));
+ OfDpaGroup *group = g_new0(OfDpaGroup, 1);
if (!group) {
return NULL;
@@ -2039,15 +2040,14 @@ static int of_dpa_cmd_add_l2_flood(OfDpa *of_dpa, OfDpaGroup *group,
group->l2_flood.group_count =
rocker_tlv_get_le16(group_tlvs[ROCKER_TLV_OF_DPA_GROUP_COUNT]);
- tlvs = g_malloc0((group->l2_flood.group_count + 1) *
- sizeof(RockerTlv *));
+ tlvs = g_new0(RockerTlv *, group->l2_flood.group_count + 1);
if (!tlvs) {
return -ROCKER_ENOMEM;
}
g_free(group->l2_flood.group_ids);
group->l2_flood.group_ids =
- g_malloc0(group->l2_flood.group_count * sizeof(uint32_t));
+ g_new0(uint32_t, group->l2_flood.group_count);
if (!group->l2_flood.group_ids) {
err = -ROCKER_ENOMEM;
goto err_out;
@@ -2463,15 +2463,13 @@ RockerOfDpaFlowList *qmp_query_rocker_of_dpa_flows(const char *name,
r = rocker_find(name);
if (!r) {
- error_set(errp, ERROR_CLASS_GENERIC_ERROR,
- "rocker %s not found", name);
+ error_setg(errp, "rocker %s not found", name);
return NULL;
}
w = rocker_get_world(r, ROCKER_WORLD_TYPE_OF_DPA);
if (!w) {
- error_set(errp, ERROR_CLASS_GENERIC_ERROR,
- "rocker %s doesn't have OF-DPA world", name);
+ error_setg(errp, "rocker %s doesn't have OF-DPA world", name);
return NULL;
}
@@ -2598,15 +2596,13 @@ RockerOfDpaGroupList *qmp_query_rocker_of_dpa_groups(const char *name,
r = rocker_find(name);
if (!r) {
- error_set(errp, ERROR_CLASS_GENERIC_ERROR,
- "rocker %s not found", name);
+ error_setg(errp, "rocker %s not found", name);
return NULL;
}
w = rocker_get_world(r, ROCKER_WORLD_TYPE_OF_DPA);
if (!w) {
- error_set(errp, ERROR_CLASS_GENERIC_ERROR,
- "rocker %s doesn't have OF-DPA world", name);
+ error_setg(errp, "rocker %s doesn't have OF-DPA world", name);
return NULL;
}
@@ -2618,6 +2614,7 @@ RockerOfDpaGroupList *qmp_query_rocker_of_dpa_groups(const char *name,
}
static WorldOps of_dpa_ops = {
+ .name = "ofdpa",
.init = of_dpa_init,
.uninit = of_dpa_uninit,
.ig = of_dpa_ig,
diff --git a/qemu/hw/net/rocker/rocker_world.c b/qemu/hw/net/rocker/rocker_world.c
index a6b18f175..89777e968 100644
--- a/qemu/hw/net/rocker/rocker_world.c
+++ b/qemu/hw/net/rocker/rocker_world.c
@@ -14,6 +14,7 @@
* GNU General Public License for more details.
*/
+#include "qemu/osdep.h"
#include "qemu/iov.h"
#include "rocker.h"
@@ -97,10 +98,5 @@ enum rocker_world_type world_type(World *world)
const char *world_name(World *world)
{
- switch (world->type) {
- case ROCKER_WORLD_TYPE_OF_DPA:
- return "OF_DPA";
- default:
- return "unknown";
- }
+ return world->ops->name;
}
diff --git a/qemu/hw/net/rocker/rocker_world.h b/qemu/hw/net/rocker/rocker_world.h
index 18d277b92..58ade4733 100644
--- a/qemu/hw/net/rocker/rocker_world.h
+++ b/qemu/hw/net/rocker/rocker_world.h
@@ -33,6 +33,7 @@ typedef int (world_cmd)(World *world, DescInfo *info,
RockerTlv *cmd_info_tlv);
typedef struct world_ops {
+ const char *name;
world_init *init;
world_uninit *uninit;
world_ig *ig;
diff --git a/qemu/hw/net/rtl8139.c b/qemu/hw/net/rtl8139.c
index edbb61ccf..1e5ec149f 100644
--- a/qemu/hw/net/rtl8139.c
+++ b/qemu/hw/net/rtl8139.c
@@ -43,12 +43,13 @@
* Added rx/tx buffer reset when enabling rx/tx operation
*
* 2010-Feb-04 Frediano Ziglio: Rewrote timer support using QEMU timer only
- * when strictly needed (required for for
+ * when strictly needed (required for
* Darwin)
* 2011-Mar-22 Benjamin Poirier: Implemented VLAN offloading
*/
/* For crc32 */
+#include "qemu/osdep.h"
#include <zlib.h>
#include "hw/hw.h"
@@ -56,6 +57,7 @@
#include "sysemu/dma.h"
#include "qemu/timer.h"
#include "net/net.h"
+#include "net/eth.h"
#include "hw/loader.h"
#include "sysemu/sysemu.h"
#include "qemu/iov.h"
@@ -63,7 +65,7 @@
/* debug RTL8139 card */
//#define DEBUG_RTL8139 1
-#define PCI_FREQUENCY 33000000L
+#define PCI_PERIOD 30 /* 30 ns period = 33.333333 Mhz frequency */
#define SET_MASKED(input, mask, curr) \
( ( (input) & ~(mask) ) | ( (curr) & (mask) ) )
@@ -72,11 +74,7 @@
#define MOD2(input, size) \
( ( input ) & ( size - 1 ) )
-#define ETHER_ADDR_LEN 6
#define ETHER_TYPE_LEN 2
-#define ETH_HLEN (ETHER_ADDR_LEN * 2 + ETHER_TYPE_LEN)
-#define ETH_P_IP 0x0800 /* Internet Protocol packet */
-#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */
#define ETH_MTU 1500
#define VLAN_TCI_LEN 2
@@ -1016,8 +1014,8 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t
/* write VLAN info to descriptor variables. */
if (s->CpCmd & CPlusRxVLAN && be16_to_cpup((uint16_t *)
- &buf[ETHER_ADDR_LEN * 2]) == ETH_P_8021Q) {
- dot1q_buf = &buf[ETHER_ADDR_LEN * 2];
+ &buf[ETH_ALEN * 2]) == ETH_P_VLAN) {
+ dot1q_buf = &buf[ETH_ALEN * 2];
size -= VLAN_HLEN;
/* if too small buffer, use the tailroom added duing expansion */
if (size < MIN_BUF_SIZE) {
@@ -1058,10 +1056,10 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t
/* receive/copy to target memory */
if (dot1q_buf) {
- pci_dma_write(d, rx_addr, buf, 2 * ETHER_ADDR_LEN);
- pci_dma_write(d, rx_addr + 2 * ETHER_ADDR_LEN,
- buf + 2 * ETHER_ADDR_LEN + VLAN_HLEN,
- size - 2 * ETHER_ADDR_LEN);
+ pci_dma_write(d, rx_addr, buf, 2 * ETH_ALEN);
+ pci_dma_write(d, rx_addr + 2 * ETH_ALEN,
+ buf + 2 * ETH_ALEN + VLAN_HLEN,
+ size - 2 * ETH_ALEN);
} else {
pci_dma_write(d, rx_addr, buf, size);
}
@@ -1148,7 +1146,9 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t
/* if receiver buffer is empty then avail == 0 */
- if (avail != 0 && size + 8 >= avail)
+#define RX_ALIGN(x) (((x) + 3) & ~0x3)
+
+ if (avail != 0 && RX_ALIGN(size + 8) >= avail)
{
DPRINTF("rx overflow: rx buffer length %d head 0x%04x "
"read 0x%04x === available 0x%04x need 0x%04x\n",
@@ -1157,7 +1157,7 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t
s->IntrStatus |= RxOverflow;
++s->RxMissed;
rtl8139_update_irq(s);
- return size_;
+ return 0;
}
packet_header |= RxStatusOK;
@@ -1176,7 +1176,7 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t
rtl8139_write_buffer(s, (uint8_t *)&val, 4);
/* correct buffer write pointer */
- s->RxBufAddr = MOD2((s->RxBufAddr + 3) & ~0x3, s->RxBufferSize);
+ s->RxBufAddr = MOD2(RX_ALIGN(s->RxBufAddr), s->RxBufferSize);
/* now we can signal we have received something */
@@ -1783,12 +1783,12 @@ static void rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size,
return;
}
- if (dot1q_buf && size >= ETHER_ADDR_LEN * 2) {
+ if (dot1q_buf && size >= ETH_ALEN * 2) {
iov = (struct iovec[3]) {
- { .iov_base = buf, .iov_len = ETHER_ADDR_LEN * 2 },
+ { .iov_base = buf, .iov_len = ETH_ALEN * 2 },
{ .iov_base = (void *) dot1q_buf, .iov_len = VLAN_HLEN },
- { .iov_base = buf + ETHER_ADDR_LEN * 2,
- .iov_len = size - ETHER_ADDR_LEN * 2 },
+ { .iov_base = buf + ETH_ALEN * 2,
+ .iov_len = size - ETH_ALEN * 2 },
};
memcpy(vlan_iov, iov, sizeof(vlan_iov));
@@ -1868,64 +1868,12 @@ static int rtl8139_transmit_one(RTL8139State *s, int descriptor)
}
/* structures and macros for task offloading */
-typedef struct ip_header
-{
- uint8_t ip_ver_len; /* version and header length */
- uint8_t ip_tos; /* type of service */
- uint16_t ip_len; /* total length */
- uint16_t ip_id; /* identification */
- uint16_t ip_off; /* fragment offset field */
- uint8_t ip_ttl; /* time to live */
- uint8_t ip_p; /* protocol */
- uint16_t ip_sum; /* checksum */
- uint32_t ip_src,ip_dst; /* source and dest address */
-} ip_header;
-
-#define IP_HEADER_VERSION_4 4
-#define IP_HEADER_VERSION(ip) ((ip->ip_ver_len >> 4)&0xf)
-#define IP_HEADER_LENGTH(ip) (((ip->ip_ver_len)&0xf) << 2)
-
-typedef struct tcp_header
-{
- uint16_t th_sport; /* source port */
- uint16_t th_dport; /* destination port */
- uint32_t th_seq; /* sequence number */
- uint32_t th_ack; /* acknowledgement number */
- uint16_t th_offset_flags; /* data offset, reserved 6 bits, TCP protocol flags */
- uint16_t th_win; /* window */
- uint16_t th_sum; /* checksum */
- uint16_t th_urp; /* urgent pointer */
-} tcp_header;
-
-typedef struct udp_header
-{
- uint16_t uh_sport; /* source port */
- uint16_t uh_dport; /* destination port */
- uint16_t uh_ulen; /* udp length */
- uint16_t uh_sum; /* udp checksum */
-} udp_header;
-
-typedef struct ip_pseudo_header
-{
- uint32_t ip_src;
- uint32_t ip_dst;
- uint8_t zeros;
- uint8_t ip_proto;
- uint16_t ip_payload;
-} ip_pseudo_header;
-
-#define IP_PROTO_TCP 6
-#define IP_PROTO_UDP 17
-
#define TCP_HEADER_DATA_OFFSET(tcp) (((be16_to_cpu(tcp->th_offset_flags) >> 12)&0xf) << 2)
#define TCP_FLAGS_ONLY(flags) ((flags)&0x3f)
#define TCP_HEADER_FLAGS(tcp) TCP_FLAGS_ONLY(be16_to_cpu(tcp->th_offset_flags))
#define TCP_HEADER_CLEAR_FLAGS(tcp, off) ((tcp)->th_offset_flags &= cpu_to_be16(~TCP_FLAGS_ONLY(off)))
-#define TCP_FLAG_FIN 0x01
-#define TCP_FLAG_PUSH 0x08
-
/* produces ones' complement sum of data */
static uint16_t ones_complement_sum(uint8_t *data, size_t len)
{
@@ -2098,7 +2046,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
}
/* transfer ownership to target */
- txdw0 &= ~CP_RX_OWN;
+ txdw0 &= ~CP_TX_OWN;
/* reset error indicator bits */
txdw0 &= ~CP_TX_STATUS_UNF;
@@ -2134,7 +2082,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
bswap16(txdw1 & CP_TX_VLAN_TAG_MASK));
dot1q_buffer = (uint16_t *) dot1q_buffer_space;
- dot1q_buffer[0] = cpu_to_be16(ETH_P_8021Q);
+ dot1q_buffer[0] = cpu_to_be16(ETH_P_VLAN);
/* BE + le_to_cpu() + ~cpu_to_le()~ = BE */
dot1q_buffer[1] = cpu_to_le16(txdw1 & CP_TX_VLAN_TAG_MASK);
} else {
@@ -2151,12 +2099,12 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
DPRINTF("+++ C+ mode offloaded task checksum\n");
/* Large enough for Ethernet and IP headers? */
- if (saved_size < ETH_HLEN + sizeof(ip_header)) {
+ if (saved_size < ETH_HLEN + sizeof(struct ip_header)) {
goto skip_offload;
}
/* ip packet header */
- ip_header *ip = NULL;
+ struct ip_header *ip = NULL;
int hlen = 0;
uint8_t ip_protocol = 0;
uint16_t ip_data_len = 0;
@@ -2172,11 +2120,15 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
DPRINTF("+++ C+ mode has IP packet\n");
- /* not aligned */
+ /* Note on memory alignment: eth_payload_data is 16-bit aligned
+ * since saved_buffer is allocated with g_malloc() and ETH_HLEN is
+ * even. 32-bit accesses must use ldl/stl wrappers to avoid
+ * unaligned accesses.
+ */
eth_payload_data = saved_buffer + ETH_HLEN;
eth_payload_len = saved_size - ETH_HLEN;
- ip = (ip_header*)eth_payload_data;
+ ip = (struct ip_header*)eth_payload_data;
if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) {
DPRINTF("+++ C+ mode packet has bad IP version %d "
@@ -2185,8 +2137,8 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
goto skip_offload;
}
- hlen = IP_HEADER_LENGTH(ip);
- if (hlen < sizeof(ip_header) || hlen > eth_payload_len) {
+ hlen = IP_HDR_GET_LEN(ip);
+ if (hlen < sizeof(struct ip_header) || hlen > eth_payload_len) {
goto skip_offload;
}
@@ -2269,7 +2221,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
}
DPRINTF("+++ C+ mode TSO TCP seqno %08x\n",
- be32_to_cpu(p_tcp_hdr->th_seq));
+ ldl_be_p(&p_tcp_hdr->th_seq));
/* add 4 TCP pseudoheader fields */
/* copy IP source and destination fields */
@@ -2287,7 +2239,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
/* keep PUSH and FIN flags only for the last frame */
if (!is_last_frame)
{
- TCP_HEADER_CLEAR_FLAGS(p_tcp_hdr, TCP_FLAG_PUSH|TCP_FLAG_FIN);
+ TCP_HEADER_CLEAR_FLAGS(p_tcp_hdr, TH_PUSH | TH_FIN);
}
/* recalculate TCP checksum */
@@ -2325,7 +2277,8 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
0, (uint8_t *) dot1q_buffer);
/* add transferred count to TCP sequence number */
- p_tcp_hdr->th_seq = cpu_to_be32(chunk_size + be32_to_cpu(p_tcp_hdr->th_seq));
+ stl_be_p(&p_tcp_hdr->th_seq,
+ chunk_size + ldl_be_p(&p_tcp_hdr->th_seq));
++send_count;
}
@@ -2881,8 +2834,7 @@ static void rtl8139_io_writew(void *opaque, uint8_t addr, uint32_t val)
static void rtl8139_set_next_tctr_time(RTL8139State *s)
{
- const uint64_t ns_per_period =
- muldiv64(0x100000000LL, get_ticks_per_sec(), PCI_FREQUENCY);
+ const uint64_t ns_per_period = (uint64_t)PCI_PERIOD << 32;
DPRINTF("entered rtl8139_set_next_tctr_time\n");
@@ -2900,7 +2852,7 @@ static void rtl8139_set_next_tctr_time(RTL8139State *s)
if (!s->TimerInt) {
timer_del(s->timer);
} else {
- uint64_t delta = muldiv64(s->TimerInt, get_ticks_per_sec(), PCI_FREQUENCY);
+ uint64_t delta = (uint64_t)s->TimerInt * PCI_PERIOD;
if (s->TCTR_base + delta <= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) {
delta += ns_per_period;
}
@@ -3174,8 +3126,8 @@ static uint32_t rtl8139_io_readl(void *opaque, uint8_t addr)
break;
case Timer:
- ret = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->TCTR_base,
- PCI_FREQUENCY, get_ticks_per_sec());
+ ret = (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->TCTR_base) /
+ PCI_PERIOD;
DPRINTF("TCTR Timer read val=0x%08x\n", ret);
break;
@@ -3269,8 +3221,7 @@ static void rtl8139_pre_save(void *opaque)
int64_t current_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
/* for migration to older versions */
- s->TCTR = muldiv64(current_time - s->TCTR_base, PCI_FREQUENCY,
- get_ticks_per_sec());
+ s->TCTR = (current_time - s->TCTR_base) / PCI_PERIOD;
s->rtl8139_mmio_io_addr_dummy = 0;
}
@@ -3438,10 +3389,8 @@ static void pci_rtl8139_uninit(PCIDevice *dev)
{
RTL8139State *s = RTL8139(dev);
- if (s->cplus_txbuffer) {
- g_free(s->cplus_txbuffer);
- s->cplus_txbuffer = NULL;
- }
+ g_free(s->cplus_txbuffer);
+ s->cplus_txbuffer = NULL;
timer_del(s->timer);
timer_free(s->timer);
qemu_del_nic(s->nic);
diff --git a/qemu/hw/net/smc91c111.c b/qemu/hw/net/smc91c111.c
index 74e06e6c7..21c1b8f54 100644
--- a/qemu/hw/net/smc91c111.c
+++ b/qemu/hw/net/smc91c111.c
@@ -7,6 +7,7 @@
* This code is licensed under the GPL
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "net/net.h"
#include "hw/devices.h"
@@ -124,6 +125,25 @@ static void smc91c111_update(smc91c111_state *s)
qemu_set_irq(s->irq, level);
}
+static int smc91c111_can_receive(smc91c111_state *s)
+{
+ if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST)) {
+ return 1;
+ }
+ if (s->allocated == (1 << NUM_PACKETS) - 1 ||
+ s->rx_fifo_len == NUM_PACKETS) {
+ return 0;
+ }
+ return 1;
+}
+
+static inline void smc91c111_flush_queued_packets(smc91c111_state *s)
+{
+ if (smc91c111_can_receive(s)) {
+ qemu_flush_queued_packets(qemu_get_queue(s->nic));
+ }
+}
+
/* Try to allocate a packet. Returns 0x80 on failure. */
static int smc91c111_allocate_packet(smc91c111_state *s)
{
@@ -164,6 +184,7 @@ static void smc91c111_pop_rx_fifo(smc91c111_state *s)
} else {
s->int_level &= ~INT_RCV;
}
+ smc91c111_flush_queued_packets(s);
smc91c111_update(s);
}
@@ -185,7 +206,7 @@ static void smc91c111_release_packet(smc91c111_state *s, int packet)
s->allocated &= ~(1 << packet);
if (s->tx_alloc == 0x80)
smc91c111_tx_alloc(s);
- qemu_flush_queued_packets(qemu_get_queue(s->nic));
+ smc91c111_flush_queued_packets(s);
}
/* Flush the TX FIFO. */
@@ -311,6 +332,7 @@ static void smc91c111_writeb(void *opaque, hwaddr offset,
if (s->rcr & RCR_SOFT_RST) {
smc91c111_reset(DEVICE(s));
}
+ smc91c111_flush_queued_packets(s);
return;
case 10: case 11: /* RPCR */
/* Ignored */
@@ -636,15 +658,11 @@ static uint32_t smc91c111_readl(void *opaque, hwaddr offset)
return val;
}
-static int smc91c111_can_receive(NetClientState *nc)
+static int smc91c111_can_receive_nc(NetClientState *nc)
{
smc91c111_state *s = qemu_get_nic_opaque(nc);
- if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
- return 1;
- if (s->allocated == (1 << NUM_PACKETS) - 1)
- return 0;
- return 1;
+ return smc91c111_can_receive(s);
}
static ssize_t smc91c111_receive(NetClientState *nc, const uint8_t *buf, size_t size)
@@ -739,7 +757,7 @@ static const MemoryRegionOps smc91c111_mem_ops = {
static NetClientInfo net_smc91c111_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
- .can_receive = smc91c111_can_receive,
+ .can_receive = smc91c111_can_receive_nc,
.receive = smc91c111_receive,
};
diff --git a/qemu/hw/net/spapr_llan.c b/qemu/hw/net/spapr_llan.c
index 1ca5e9ce6..a647f25d9 100644
--- a/qemu/hw/net/spapr_llan.c
+++ b/qemu/hw/net/spapr_llan.c
@@ -24,6 +24,9 @@
* THE SOFTWARE.
*
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "net/net.h"
#include "hw/qdev.h"
@@ -44,6 +47,10 @@
#define DPRINTF(fmt...)
#endif
+/* Compatibility flags for migration */
+#define SPAPRVLAN_FLAG_RX_BUF_POOLS_BIT 0
+#define SPAPRVLAN_FLAG_RX_BUF_POOLS (1 << SPAPRVLAN_FLAG_RX_BUF_POOLS_BIT)
+
/*
* Virtual LAN device
*/
@@ -85,6 +92,15 @@ typedef uint64_t vlan_bd_t;
#define VIO_SPAPR_VLAN_DEVICE(obj) \
OBJECT_CHECK(VIOsPAPRVLANDevice, (obj), TYPE_VIO_SPAPR_VLAN_DEVICE)
+#define RX_POOL_MAX_BDS 4096
+#define RX_MAX_POOLS 5
+
+typedef struct {
+ int32_t bufsize;
+ int32_t count;
+ vlan_bd_t bds[RX_POOL_MAX_BDS];
+} RxBufPool;
+
typedef struct VIOsPAPRVLANDevice {
VIOsPAPRDevice sdev;
NICConf nicconf;
@@ -93,6 +109,8 @@ typedef struct VIOsPAPRVLANDevice {
target_ulong buf_list;
uint32_t add_buf_ptr, use_buf_ptr, rx_bufs;
target_ulong rxq_ptr;
+ uint32_t compat_flags; /* Compatability flags for migration */
+ RxBufPool *rx_pool[RX_MAX_POOLS]; /* Receive buffer descriptor pools */
} VIOsPAPRVLANDevice;
static int spapr_vlan_can_receive(NetClientState *nc)
@@ -102,6 +120,73 @@ static int spapr_vlan_can_receive(NetClientState *nc)
return (dev->isopen && dev->rx_bufs > 0);
}
+/**
+ * Get buffer descriptor from one of our receive buffer pools
+ */
+static vlan_bd_t spapr_vlan_get_rx_bd_from_pool(VIOsPAPRVLANDevice *dev,
+ size_t size)
+{
+ vlan_bd_t bd;
+ int pool;
+
+ for (pool = 0; pool < RX_MAX_POOLS; pool++) {
+ if (dev->rx_pool[pool]->count > 0 &&
+ dev->rx_pool[pool]->bufsize >= size + 8) {
+ break;
+ }
+ }
+ if (pool == RX_MAX_POOLS) {
+ /* Failed to find a suitable buffer */
+ return 0;
+ }
+
+ DPRINTF("Found buffer: pool=%d count=%d rxbufs=%d\n", pool,
+ dev->rx_pool[pool]->count, dev->rx_bufs);
+
+ /* Remove the buffer from the pool */
+ dev->rx_pool[pool]->count--;
+ bd = dev->rx_pool[pool]->bds[dev->rx_pool[pool]->count];
+ dev->rx_pool[pool]->bds[dev->rx_pool[pool]->count] = 0;
+
+ return bd;
+}
+
+/**
+ * Get buffer descriptor from the receive buffer list page that has been
+ * supplied by the guest with the H_REGISTER_LOGICAL_LAN call
+ */
+static vlan_bd_t spapr_vlan_get_rx_bd_from_page(VIOsPAPRVLANDevice *dev,
+ size_t size)
+{
+ int buf_ptr = dev->use_buf_ptr;
+ vlan_bd_t bd;
+
+ do {
+ buf_ptr += 8;
+ if (buf_ptr >= VLAN_RX_BDS_LEN + VLAN_RX_BDS_OFF) {
+ buf_ptr = VLAN_RX_BDS_OFF;
+ }
+
+ bd = vio_ldq(&dev->sdev, dev->buf_list + buf_ptr);
+ DPRINTF("use_buf_ptr=%d bd=0x%016llx\n",
+ buf_ptr, (unsigned long long)bd);
+ } while ((!(bd & VLAN_BD_VALID) || VLAN_BD_LEN(bd) < size + 8)
+ && buf_ptr != dev->use_buf_ptr);
+
+ if (!(bd & VLAN_BD_VALID) || VLAN_BD_LEN(bd) < size + 8) {
+ /* Failed to find a suitable buffer */
+ return 0;
+ }
+
+ /* Remove the buffer from the pool */
+ dev->use_buf_ptr = buf_ptr;
+ vio_stq(&dev->sdev, dev->buf_list + dev->use_buf_ptr, 0);
+
+ DPRINTF("Found buffer: ptr=%d rxbufs=%d\n", dev->use_buf_ptr, dev->rx_bufs);
+
+ return bd;
+}
+
static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf,
size_t size)
{
@@ -109,7 +194,6 @@ static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf,
VIOsPAPRDevice *sdev = VIO_SPAPR_DEVICE(dev);
vlan_bd_t rxq_bd = vio_ldq(sdev, dev->buf_list + VLAN_RXQ_BD_OFF);
vlan_bd_t bd;
- int buf_ptr = dev->use_buf_ptr;
uint64_t handle;
uint8_t control;
@@ -124,29 +208,16 @@ static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf,
return -1;
}
- do {
- buf_ptr += 8;
- if (buf_ptr >= (VLAN_RX_BDS_LEN + VLAN_RX_BDS_OFF)) {
- buf_ptr = VLAN_RX_BDS_OFF;
- }
-
- bd = vio_ldq(sdev, dev->buf_list + buf_ptr);
- DPRINTF("use_buf_ptr=%d bd=0x%016llx\n",
- buf_ptr, (unsigned long long)bd);
- } while ((!(bd & VLAN_BD_VALID) || (VLAN_BD_LEN(bd) < (size + 8)))
- && (buf_ptr != dev->use_buf_ptr));
-
- if (!(bd & VLAN_BD_VALID) || (VLAN_BD_LEN(bd) < (size + 8))) {
- /* Failed to find a suitable buffer */
+ if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) {
+ bd = spapr_vlan_get_rx_bd_from_pool(dev, size);
+ } else {
+ bd = spapr_vlan_get_rx_bd_from_page(dev, size);
+ }
+ if (!bd) {
return -1;
}
- /* Remove the buffer from the pool */
dev->rx_bufs--;
- dev->use_buf_ptr = buf_ptr;
- vio_stq(sdev, dev->buf_list + dev->use_buf_ptr, 0);
-
- DPRINTF("Found buffer: ptr=%d num=%d\n", dev->use_buf_ptr, dev->rx_bufs);
/* Transfer the packet data */
if (spapr_vio_dma_write(sdev, VLAN_BD_ADDR(bd) + 8, buf, size) < 0) {
@@ -194,13 +265,31 @@ static NetClientInfo net_spapr_vlan_info = {
.receive = spapr_vlan_receive,
};
+static void spapr_vlan_reset_rx_pool(RxBufPool *rxp)
+{
+ /*
+ * Use INT_MAX as bufsize so that unused buffers are moved to the end
+ * of the list during the qsort in spapr_vlan_add_rxbuf_to_pool() later.
+ */
+ rxp->bufsize = INT_MAX;
+ rxp->count = 0;
+ memset(rxp->bds, 0, sizeof(rxp->bds));
+}
+
static void spapr_vlan_reset(VIOsPAPRDevice *sdev)
{
VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev);
+ int i;
dev->buf_list = 0;
dev->rx_bufs = 0;
dev->isopen = 0;
+
+ if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) {
+ for (i = 0; i < RX_MAX_POOLS; i++) {
+ spapr_vlan_reset_rx_pool(dev->rx_pool[i]);
+ }
+ }
}
static void spapr_vlan_realize(VIOsPAPRDevice *sdev, Error **errp)
@@ -217,10 +306,31 @@ static void spapr_vlan_realize(VIOsPAPRDevice *sdev, Error **errp)
static void spapr_vlan_instance_init(Object *obj)
{
VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(obj);
+ int i;
device_add_bootindex_property(obj, &dev->nicconf.bootindex,
"bootindex", "",
DEVICE(dev), NULL);
+
+ if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) {
+ for (i = 0; i < RX_MAX_POOLS; i++) {
+ dev->rx_pool[i] = g_new(RxBufPool, 1);
+ spapr_vlan_reset_rx_pool(dev->rx_pool[i]);
+ }
+ }
+}
+
+static void spapr_vlan_instance_finalize(Object *obj)
+{
+ VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(obj);
+ int i;
+
+ if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) {
+ for (i = 0; i < RX_MAX_POOLS; i++) {
+ g_free(dev->rx_pool[i]);
+ dev->rx_pool[i] = NULL;
+ }
+ }
}
void spapr_vlan_create(VIOsPAPRBus *bus, NICInfo *nd)
@@ -371,6 +481,113 @@ static target_ulong h_free_logical_lan(PowerPCCPU *cpu,
return H_SUCCESS;
}
+/**
+ * Used for qsort, this function compares two RxBufPools by size.
+ */
+static int rx_pool_size_compare(const void *p1, const void *p2)
+{
+ const RxBufPool *pool1 = *(RxBufPool **)p1;
+ const RxBufPool *pool2 = *(RxBufPool **)p2;
+
+ if (pool1->bufsize < pool2->bufsize) {
+ return -1;
+ }
+ return pool1->bufsize > pool2->bufsize;
+}
+
+/**
+ * Search for a matching buffer pool with exact matching size,
+ * or return -1 if no matching pool has been found.
+ */
+static int spapr_vlan_get_rx_pool_id(VIOsPAPRVLANDevice *dev, int size)
+{
+ int pool;
+
+ for (pool = 0; pool < RX_MAX_POOLS; pool++) {
+ if (dev->rx_pool[pool]->bufsize == size) {
+ return pool;
+ }
+ }
+
+ return -1;
+}
+
+/**
+ * Enqueuing receive buffer by adding it to one of our receive buffer pools
+ */
+static target_long spapr_vlan_add_rxbuf_to_pool(VIOsPAPRVLANDevice *dev,
+ target_ulong buf)
+{
+ int size = VLAN_BD_LEN(buf);
+ int pool;
+
+ pool = spapr_vlan_get_rx_pool_id(dev, size);
+ if (pool < 0) {
+ /*
+ * No matching pool found? Try to use a new one. If the guest used all
+ * pools before, but changed the size of one pool inbetween, we might
+ * need to recycle that pool here (if it's empty already). Thus scan
+ * all buffer pools now, starting with the last (likely empty) one.
+ */
+ for (pool = RX_MAX_POOLS - 1; pool >= 0 ; pool--) {
+ if (dev->rx_pool[pool]->count == 0) {
+ dev->rx_pool[pool]->bufsize = size;
+ /*
+ * Sort pools by size so that spapr_vlan_receive()
+ * can later find the smallest buffer pool easily.
+ */
+ qsort(dev->rx_pool, RX_MAX_POOLS, sizeof(dev->rx_pool[0]),
+ rx_pool_size_compare);
+ pool = spapr_vlan_get_rx_pool_id(dev, size);
+ DPRINTF("created RX pool %d for size %lld\n", pool,
+ VLAN_BD_LEN(buf));
+ break;
+ }
+ }
+ }
+ /* Still no usable pool? Give up */
+ if (pool < 0 || dev->rx_pool[pool]->count >= RX_POOL_MAX_BDS) {
+ return H_RESOURCE;
+ }
+
+ DPRINTF("h_add_llan_buf(): Add buf using pool %i (size %lli, count=%i)\n",
+ pool, VLAN_BD_LEN(buf), dev->rx_pool[pool]->count);
+
+ dev->rx_pool[pool]->bds[dev->rx_pool[pool]->count++] = buf;
+
+ return 0;
+}
+
+/**
+ * This is the old way of enqueuing receive buffers: Add it to the rx queue
+ * page that has been supplied by the guest (which is quite limited in size).
+ */
+static target_long spapr_vlan_add_rxbuf_to_page(VIOsPAPRVLANDevice *dev,
+ target_ulong buf)
+{
+ vlan_bd_t bd;
+
+ if (dev->rx_bufs >= VLAN_MAX_BUFS) {
+ return H_RESOURCE;
+ }
+
+ do {
+ dev->add_buf_ptr += 8;
+ if (dev->add_buf_ptr >= VLAN_RX_BDS_LEN + VLAN_RX_BDS_OFF) {
+ dev->add_buf_ptr = VLAN_RX_BDS_OFF;
+ }
+
+ bd = vio_ldq(&dev->sdev, dev->buf_list + dev->add_buf_ptr);
+ } while (bd & VLAN_BD_VALID);
+
+ vio_stq(&dev->sdev, dev->buf_list + dev->add_buf_ptr, buf);
+
+ DPRINTF("h_add_llan_buf(): Added buf ptr=%d rx_bufs=%d bd=0x%016llx\n",
+ dev->add_buf_ptr, dev->rx_bufs, (unsigned long long)buf);
+
+ return 0;
+}
+
static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu,
sPAPRMachineState *spapr,
target_ulong opcode,
@@ -380,7 +597,7 @@ static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu,
target_ulong buf = args[1];
VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev);
- vlan_bd_t bd;
+ target_long ret;
DPRINTF("H_ADD_LOGICAL_LAN_BUFFER(0x" TARGET_FMT_lx
", 0x" TARGET_FMT_lx ")\n", reg, buf);
@@ -396,29 +613,23 @@ static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu,
return H_PARAMETER;
}
- if (!dev->isopen || dev->rx_bufs >= VLAN_MAX_BUFS) {
+ if (!dev->isopen) {
return H_RESOURCE;
}
- do {
- dev->add_buf_ptr += 8;
- if (dev->add_buf_ptr >= (VLAN_RX_BDS_LEN + VLAN_RX_BDS_OFF)) {
- dev->add_buf_ptr = VLAN_RX_BDS_OFF;
- }
-
- bd = vio_ldq(sdev, dev->buf_list + dev->add_buf_ptr);
- } while (bd & VLAN_BD_VALID);
-
- vio_stq(sdev, dev->buf_list + dev->add_buf_ptr, buf);
+ if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) {
+ ret = spapr_vlan_add_rxbuf_to_pool(dev, buf);
+ } else {
+ ret = spapr_vlan_add_rxbuf_to_page(dev, buf);
+ }
+ if (ret) {
+ return ret;
+ }
dev->rx_bufs++;
qemu_flush_queued_packets(qemu_get_queue(dev->nic));
- DPRINTF("h_add_logical_lan_buffer(): Added buf ptr=%d rx_bufs=%d"
- " bd=0x%016llx\n", dev->add_buf_ptr, dev->rx_bufs,
- (unsigned long long)buf);
-
return H_SUCCESS;
}
@@ -508,9 +719,44 @@ static target_ulong h_multicast_ctrl(PowerPCCPU *cpu, sPAPRMachineState *spapr,
static Property spapr_vlan_properties[] = {
DEFINE_SPAPR_PROPERTIES(VIOsPAPRVLANDevice, sdev),
DEFINE_NIC_PROPERTIES(VIOsPAPRVLANDevice, nicconf),
+ DEFINE_PROP_BIT("use-rx-buffer-pools", VIOsPAPRVLANDevice,
+ compat_flags, SPAPRVLAN_FLAG_RX_BUF_POOLS_BIT, true),
DEFINE_PROP_END_OF_LIST(),
};
+static bool spapr_vlan_rx_buffer_pools_needed(void *opaque)
+{
+ VIOsPAPRVLANDevice *dev = opaque;
+
+ return (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) != 0;
+}
+
+static const VMStateDescription vmstate_rx_buffer_pool = {
+ .name = "spapr_llan/rx_buffer_pool",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = spapr_vlan_rx_buffer_pools_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32(bufsize, RxBufPool),
+ VMSTATE_INT32(count, RxBufPool),
+ VMSTATE_UINT64_ARRAY(bds, RxBufPool, RX_POOL_MAX_BDS),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_rx_pools = {
+ .name = "spapr_llan/rx_pools",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = spapr_vlan_rx_buffer_pools_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(rx_pool, VIOsPAPRVLANDevice,
+ RX_MAX_POOLS, 1,
+ vmstate_rx_buffer_pool, RxBufPool),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_spapr_llan = {
.name = "spapr_llan",
.version_id = 1,
@@ -527,6 +773,10 @@ static const VMStateDescription vmstate_spapr_llan = {
VMSTATE_END_OF_LIST()
},
+ .subsections = (const VMStateDescription * []) {
+ &vmstate_rx_pools,
+ NULL
+ }
};
static void spapr_vlan_class_init(ObjectClass *klass, void *data)
@@ -553,6 +803,7 @@ static const TypeInfo spapr_vlan_info = {
.instance_size = sizeof(VIOsPAPRVLANDevice),
.class_init = spapr_vlan_class_init,
.instance_init = spapr_vlan_instance_init,
+ .instance_finalize = spapr_vlan_instance_finalize,
};
static void spapr_vlan_register_types(void)
diff --git a/qemu/hw/net/stellaris_enet.c b/qemu/hw/net/stellaris_enet.c
index 21a47735d..688089494 100644
--- a/qemu/hw/net/stellaris_enet.c
+++ b/qemu/hw/net/stellaris_enet.c
@@ -6,6 +6,7 @@
*
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "net/net.h"
#include <zlib.h>
@@ -235,8 +236,18 @@ static ssize_t stellaris_enet_receive(NetClientState *nc, const uint8_t *buf, si
n = s->next_packet + s->np;
if (n >= 31)
n -= 31;
- s->np++;
+ if (size >= sizeof(s->rx[n].data) - 6) {
+ /* If the packet won't fit into the
+ * emulated 2K RAM, this is reported
+ * as a FIFO overrun error.
+ */
+ s->ris |= SE_INT_FOV;
+ stellaris_enet_update(s);
+ return -1;
+ }
+
+ s->np++;
s->rx[n].len = size + 6;
p = s->rx[n].data;
*(p++) = (size + 6);
diff --git a/qemu/hw/net/vhost_net.c b/qemu/hw/net/vhost_net.c
index 5c1d11f51..6e1032fc1 100644
--- a/qemu/hw/net/vhost_net.c
+++ b/qemu/hw/net/vhost_net.c
@@ -13,6 +13,7 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "net/net.h"
#include "net/tap.h"
#include "net/vhost-user.h"
@@ -21,24 +22,20 @@
#include "net/vhost_net.h"
#include "qemu/error-report.h"
-#include "config.h"
#ifdef CONFIG_VHOST_NET
#include <linux/vhost.h>
#include <sys/socket.h>
#include <linux/kvm.h>
-#include <fcntl.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <netinet/in.h>
-#include <stdio.h>
#include "standard-headers/linux/virtio_ring.h"
#include "hw/virtio/vhost.h"
#include "hw/virtio/virtio-bus.h"
-#include "hw/virtio/virtio-access.h"
struct vhost_net {
struct vhost_dev dev;
@@ -77,13 +74,9 @@ static const int user_feature_bits[] = {
VIRTIO_NET_F_HOST_ECN,
VIRTIO_NET_F_HOST_UFO,
VIRTIO_NET_F_MRG_RXBUF,
- VIRTIO_NET_F_STATUS,
- VIRTIO_NET_F_CTRL_VQ,
- VIRTIO_NET_F_CTRL_RX,
- VIRTIO_NET_F_CTRL_VLAN,
- VIRTIO_NET_F_CTRL_RX_EXTRA,
- VIRTIO_NET_F_CTRL_MAC_ADDR,
- VIRTIO_NET_F_CTRL_GUEST_OFFLOADS,
+
+ /* This bit implies RARP isn't sent by QEMU out of band */
+ VIRTIO_NET_F_GUEST_ANNOUNCE,
VIRTIO_NET_F_MQ,
@@ -122,6 +115,11 @@ void vhost_net_ack_features(struct vhost_net *net, uint64_t features)
vhost_ack_features(&net->dev, vhost_net_get_feature_bits(net), features);
}
+uint64_t vhost_net_get_max_queues(VHostNetState *net)
+{
+ return net->dev.max_queues;
+}
+
static int vhost_net_get_fd(NetClientState *backend)
{
switch (backend->info->type) {
@@ -143,6 +141,11 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options)
fprintf(stderr, "vhost-net requires net backend to be setup\n");
goto fail;
}
+ net->nc = options->net_backend;
+
+ net->dev.max_queues = 1;
+ net->dev.nvqs = 2;
+ net->dev.vqs = net->vqs;
if (backend_kernel) {
r = vhost_net_get_fd(options->net_backend);
@@ -152,14 +155,15 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options)
net->dev.backend_features = qemu_has_vnet_hdr(options->net_backend)
? 0 : (1ULL << VHOST_NET_F_VIRTIO_NET_HDR);
net->backend = r;
+ net->dev.protocol_features = 0;
} else {
net->dev.backend_features = 0;
+ net->dev.protocol_features = 0;
net->backend = -1;
- }
- net->nc = options->net_backend;
- net->dev.nvqs = 2;
- net->dev.vqs = net->vqs;
+ /* vhost-user needs vq_index to initiate a specific queue pair */
+ net->dev.vq_index = net->nc->queue_index * net->dev.nvqs;
+ }
r = vhost_dev_init(&net->dev, options->opaque,
options->backend_type);
@@ -192,27 +196,6 @@ static void vhost_net_set_vq_index(struct vhost_net *net, int vq_index)
net->dev.vq_index = vq_index;
}
-static int vhost_net_set_vnet_endian(VirtIODevice *dev, NetClientState *peer,
- bool set)
-{
- int r = 0;
-
- if (virtio_has_feature(dev, VIRTIO_F_VERSION_1) ||
- (virtio_legacy_is_cross_endian(dev) && !virtio_is_big_endian(dev))) {
- r = qemu_set_vnet_le(peer, set);
- if (r) {
- error_report("backend does not support LE vnet headers");
- }
- } else if (virtio_legacy_is_cross_endian(dev)) {
- r = qemu_set_vnet_be(peer, set);
- if (r) {
- error_report("backend does not support BE vnet headers");
- }
- }
-
- return r;
-}
-
static int vhost_net_start_one(struct vhost_net *net,
VirtIODevice *dev)
{
@@ -241,8 +224,7 @@ static int vhost_net_start_one(struct vhost_net *net,
file.fd = net->backend;
for (file.index = 0; file.index < net->dev.nvqs; ++file.index) {
const VhostOps *vhost_ops = net->dev.vhost_ops;
- r = vhost_ops->vhost_call(&net->dev, VHOST_NET_SET_BACKEND,
- &file);
+ r = vhost_ops->vhost_net_set_backend(&net->dev, &file);
if (r < 0) {
r = -errno;
goto fail;
@@ -255,8 +237,7 @@ fail:
if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) {
while (file.index-- > 0) {
const VhostOps *vhost_ops = net->dev.vhost_ops;
- int r = vhost_ops->vhost_call(&net->dev, VHOST_NET_SET_BACKEND,
- &file);
+ int r = vhost_ops->vhost_net_set_backend(&net->dev, &file);
assert(r >= 0);
}
}
@@ -278,15 +259,7 @@ static void vhost_net_stop_one(struct vhost_net *net,
if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) {
for (file.index = 0; file.index < net->dev.nvqs; ++file.index) {
const VhostOps *vhost_ops = net->dev.vhost_ops;
- int r = vhost_ops->vhost_call(&net->dev, VHOST_NET_SET_BACKEND,
- &file);
- assert(r >= 0);
- }
- } else if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) {
- for (file.index = 0; file.index < net->dev.nvqs; ++file.index) {
- const VhostOps *vhost_ops = net->dev.vhost_ops;
- int r = vhost_ops->vhost_call(&net->dev, VHOST_RESET_OWNER,
- NULL);
+ int r = vhost_ops->vhost_net_set_backend(&net->dev, &file);
assert(r >= 0);
}
}
@@ -307,23 +280,28 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs,
if (!k->set_guest_notifiers) {
error_report("binding does not support guest notifiers");
- r = -ENOSYS;
- goto err;
- }
-
- r = vhost_net_set_vnet_endian(dev, ncs[0].peer, true);
- if (r < 0) {
- goto err;
+ return -ENOSYS;
}
for (i = 0; i < total_queues; i++) {
- vhost_net_set_vq_index(get_vhost_net(ncs[i].peer), i * 2);
- }
+ struct vhost_net *net;
+
+ net = get_vhost_net(ncs[i].peer);
+ vhost_net_set_vq_index(net, i * 2);
+
+ /* Suppress the masking guest notifiers on vhost user
+ * because vhost user doesn't interrupt masking/unmasking
+ * properly.
+ */
+ if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) {
+ dev->use_guest_notifier_mask = false;
+ }
+ }
r = k->set_guest_notifiers(qbus->parent, total_queues * 2, true);
if (r < 0) {
error_report("Error binding guest notifier: %d", -r);
- goto err_endian;
+ goto err;
}
for (i = 0; i < total_queues; i++) {
@@ -345,8 +323,6 @@ err_start:
fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", e);
fflush(stderr);
}
-err_endian:
- vhost_net_set_vnet_endian(dev, ncs[0].peer, false);
err:
return r;
}
@@ -369,8 +345,6 @@ void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs,
fflush(stderr);
}
assert(r >= 0);
-
- assert(vhost_net_set_vnet_endian(dev, ncs[0].peer, false) >= 0);
}
void vhost_net_cleanup(struct vhost_net *net)
@@ -379,6 +353,18 @@ void vhost_net_cleanup(struct vhost_net *net)
g_free(net);
}
+int vhost_net_notify_migration_done(struct vhost_net *net, char* mac_addr)
+{
+ const VhostOps *vhost_ops = net->dev.vhost_ops;
+ int r = -1;
+
+ if (vhost_ops->vhost_migration_done) {
+ r = vhost_ops->vhost_migration_done(&net->dev, mac_addr);
+ }
+
+ return r;
+}
+
bool vhost_net_virtqueue_pending(VHostNetState *net, int idx)
{
return vhost_virtqueue_pending(&net->dev, idx);
@@ -411,7 +397,25 @@ VHostNetState *get_vhost_net(NetClientState *nc)
return vhost_net;
}
+
+int vhost_set_vring_enable(NetClientState *nc, int enable)
+{
+ VHostNetState *net = get_vhost_net(nc);
+ const VhostOps *vhost_ops = net->dev.vhost_ops;
+
+ if (vhost_ops->vhost_set_vring_enable) {
+ return vhost_ops->vhost_set_vring_enable(&net->dev, enable);
+ }
+
+ return 0;
+}
+
#else
+uint64_t vhost_net_get_max_queues(VHostNetState *net)
+{
+ return 1;
+}
+
struct vhost_net *vhost_net_init(VhostNetOptions *options)
{
error_report("vhost-net support is not compiled in");
@@ -452,8 +456,18 @@ void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev,
{
}
+int vhost_net_notify_migration_done(struct vhost_net *net, char* mac_addr)
+{
+ return -1;
+}
+
VHostNetState *get_vhost_net(NetClientState *nc)
{
return 0;
}
+
+int vhost_set_vring_enable(NetClientState *nc, int enable)
+{
+ return 0;
+}
#endif
diff --git a/qemu/hw/net/virtio-net.c b/qemu/hw/net/virtio-net.c
index 151083954..5798f87d8 100644
--- a/qemu/hw/net/virtio-net.c
+++ b/qemu/hw/net/virtio-net.c
@@ -11,6 +11,7 @@
*
*/
+#include "qemu/osdep.h"
#include "qemu/iov.h"
#include "hw/virtio/virtio.h"
#include "net/net.h"
@@ -86,8 +87,8 @@ static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config)
memcpy(&netcfg, config, n->config_size);
- if (!virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_MAC_ADDR) &&
- !virtio_has_feature(vdev, VIRTIO_F_VERSION_1) &&
+ if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_MAC_ADDR) &&
+ !virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1) &&
memcmp(netcfg.mac, n->mac, ETH_ALEN)) {
memcpy(n->mac, netcfg.mac, ETH_ALEN);
qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
@@ -128,6 +129,13 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status)
if (!n->vhost_started) {
int r, i;
+ if (n->needs_vnet_hdr_swap) {
+ error_report("backend does not support %s vnet headers; "
+ "falling back on userspace virtio",
+ virtio_is_big_endian(vdev) ? "BE" : "LE");
+ return;
+ }
+
/* Any packets outstanding? Purge them to avoid touching rings
* when vhost is running.
*/
@@ -152,6 +160,59 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status)
}
}
+static int virtio_net_set_vnet_endian_one(VirtIODevice *vdev,
+ NetClientState *peer,
+ bool enable)
+{
+ if (virtio_is_big_endian(vdev)) {
+ return qemu_set_vnet_be(peer, enable);
+ } else {
+ return qemu_set_vnet_le(peer, enable);
+ }
+}
+
+static bool virtio_net_set_vnet_endian(VirtIODevice *vdev, NetClientState *ncs,
+ int queues, bool enable)
+{
+ int i;
+
+ for (i = 0; i < queues; i++) {
+ if (virtio_net_set_vnet_endian_one(vdev, ncs[i].peer, enable) < 0 &&
+ enable) {
+ while (--i >= 0) {
+ virtio_net_set_vnet_endian_one(vdev, ncs[i].peer, false);
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void virtio_net_vnet_endian_status(VirtIONet *n, uint8_t status)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(n);
+ int queues = n->multiqueue ? n->max_queues : 1;
+
+ if (virtio_net_started(n, status)) {
+ /* Before using the device, we tell the network backend about the
+ * endianness to use when parsing vnet headers. If the backend
+ * can't do it, we fallback onto fixing the headers in the core
+ * virtio-net code.
+ */
+ n->needs_vnet_hdr_swap = virtio_net_set_vnet_endian(vdev, n->nic->ncs,
+ queues, true);
+ } else if (virtio_net_started(n, vdev->status)) {
+ /* After using the device, we need to reset the network backend to
+ * the default (guest native endianness), otherwise the guest may
+ * lose network connectivity if it is rebooted into a different
+ * endianness.
+ */
+ virtio_net_set_vnet_endian(vdev, n->nic->ncs, queues, false);
+ }
+}
+
static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status)
{
VirtIONet *n = VIRTIO_NET(vdev);
@@ -159,6 +220,7 @@ static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status)
int i;
uint8_t queue_status;
+ virtio_net_vnet_endian_status(n, status);
virtio_net_vhost_status(n, status);
for (i = 0; i < n->max_queues; i++) {
@@ -304,7 +366,7 @@ static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc)
info->multicast_table = str_list;
info->vlan_table = get_vlan_table(n);
- if (!virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_VLAN)) {
+ if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VLAN)) {
info->vlan = RX_STATE_ALL;
} else if (!info->vlan_table) {
info->vlan = RX_STATE_NONE;
@@ -406,6 +468,10 @@ static int peer_attach(VirtIONet *n, int index)
return 0;
}
+ if (nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) {
+ vhost_set_vring_enable(nc->peer, 1);
+ }
+
if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
return 0;
}
@@ -421,6 +487,10 @@ static int peer_detach(VirtIONet *n, int index)
return 0;
}
+ if (nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) {
+ vhost_set_vring_enable(nc->peer, 0);
+ }
+
if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
return 0;
}
@@ -529,13 +599,13 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features)
int i;
virtio_net_set_multiqueue(n,
- __virtio_has_feature(features, VIRTIO_NET_F_MQ));
+ virtio_has_feature(features, VIRTIO_NET_F_MQ));
virtio_net_set_mrg_rx_bufs(n,
- __virtio_has_feature(features,
- VIRTIO_NET_F_MRG_RXBUF),
- __virtio_has_feature(features,
- VIRTIO_F_VERSION_1));
+ virtio_has_feature(features,
+ VIRTIO_NET_F_MRG_RXBUF),
+ virtio_has_feature(features,
+ VIRTIO_F_VERSION_1));
if (n->has_vnet_hdr) {
n->curr_guest_offloads =
@@ -552,7 +622,7 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features)
vhost_net_ack_features(get_vhost_net(nc->peer), features);
}
- if (__virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN)) {
+ if (virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN)) {
memset(n->vlans, 0, MAX_VLAN >> 3);
} else {
memset(n->vlans, 0xff, MAX_VLAN >> 3);
@@ -599,7 +669,7 @@ static int virtio_net_handle_offloads(VirtIONet *n, uint8_t cmd,
uint64_t offloads;
size_t s;
- if (!virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
+ if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
return VIRTIO_NET_ERR;
}
@@ -810,20 +880,24 @@ static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
VirtIONet *n = VIRTIO_NET(vdev);
struct virtio_net_ctrl_hdr ctrl;
virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
- VirtQueueElement elem;
+ VirtQueueElement *elem;
size_t s;
struct iovec *iov, *iov2;
unsigned int iov_cnt;
- while (virtqueue_pop(vq, &elem)) {
- if (iov_size(elem.in_sg, elem.in_num) < sizeof(status) ||
- iov_size(elem.out_sg, elem.out_num) < sizeof(ctrl)) {
+ for (;;) {
+ elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
+ if (!elem) {
+ break;
+ }
+ if (iov_size(elem->in_sg, elem->in_num) < sizeof(status) ||
+ iov_size(elem->out_sg, elem->out_num) < sizeof(ctrl)) {
error_report("virtio-net ctrl missing headers");
exit(1);
}
- iov_cnt = elem.out_num;
- iov2 = iov = g_memdup(elem.out_sg, sizeof(struct iovec) * elem.out_num);
+ iov_cnt = elem->out_num;
+ iov2 = iov = g_memdup(elem->out_sg, sizeof(struct iovec) * elem->out_num);
s = iov_to_buf(iov, iov_cnt, 0, &ctrl, sizeof(ctrl));
iov_discard_front(&iov, &iov_cnt, sizeof(ctrl));
if (s != sizeof(ctrl)) {
@@ -842,12 +916,13 @@ static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
status = virtio_net_handle_offloads(n, ctrl.cmd, iov, iov_cnt);
}
- s = iov_from_buf(elem.in_sg, elem.in_num, 0, &status, sizeof(status));
+ s = iov_from_buf(elem->in_sg, elem->in_num, 0, &status, sizeof(status));
assert(s == sizeof(status));
- virtqueue_push(vq, &elem, sizeof(status));
+ virtqueue_push(vq, elem, sizeof(status));
virtio_notify(vdev, vq);
g_free(iov2);
+ g_free(elem);
}
}
@@ -949,7 +1024,10 @@ static void receive_header(VirtIONet *n, const struct iovec *iov, int iov_cnt,
void *wbuf = (void *)buf;
work_around_broken_dhclient(wbuf, wbuf + n->host_hdr_len,
size - n->host_hdr_len);
- virtio_net_hdr_swap(VIRTIO_DEVICE(n), wbuf);
+
+ if (n->needs_vnet_hdr_swap) {
+ virtio_net_hdr_swap(VIRTIO_DEVICE(n), wbuf);
+ }
iov_from_buf(iov, iov_cnt, 0, buf, sizeof(struct virtio_net_hdr));
} else {
struct virtio_net_hdr hdr = {
@@ -1036,13 +1114,14 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t
offset = i = 0;
while (offset < size) {
- VirtQueueElement elem;
+ VirtQueueElement *elem;
int len, total;
- const struct iovec *sg = elem.in_sg;
+ const struct iovec *sg;
total = 0;
- if (virtqueue_pop(q->rx_vq, &elem) == 0) {
+ elem = virtqueue_pop(q->rx_vq, sizeof(VirtQueueElement));
+ if (!elem) {
if (i == 0)
return -1;
error_report("virtio-net unexpected empty queue: "
@@ -1055,21 +1134,22 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t
exit(1);
}
- if (elem.in_num < 1) {
+ if (elem->in_num < 1) {
error_report("virtio-net receive queue contains no in buffers");
exit(1);
}
+ sg = elem->in_sg;
if (i == 0) {
assert(offset == 0);
if (n->mergeable_rx_bufs) {
mhdr_cnt = iov_copy(mhdr_sg, ARRAY_SIZE(mhdr_sg),
- sg, elem.in_num,
+ sg, elem->in_num,
offsetof(typeof(mhdr), num_buffers),
sizeof(mhdr.num_buffers));
}
- receive_header(n, sg, elem.in_num, buf, size);
+ receive_header(n, sg, elem->in_num, buf, size);
offset = n->host_hdr_len;
total += n->guest_hdr_len;
guest_offset = n->guest_hdr_len;
@@ -1078,7 +1158,7 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t
}
/* copy in packet. ugh */
- len = iov_from_buf(sg, elem.in_num, guest_offset,
+ len = iov_from_buf(sg, elem->in_num, guest_offset,
buf + offset, size - offset);
total += len;
offset += len;
@@ -1086,18 +1166,14 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t
* must have consumed the complete packet.
* Otherwise, drop it. */
if (!n->mergeable_rx_bufs && offset < size) {
-#if 0
- error_report("virtio-net truncated non-mergeable packet: "
- "i %zd mergeable %d offset %zd, size %zd, "
- "guest hdr len %zd, host hdr len %zd",
- i, n->mergeable_rx_bufs,
- offset, size, n->guest_hdr_len, n->host_hdr_len);
-#endif
+ virtqueue_discard(q->rx_vq, elem, total);
+ g_free(elem);
return size;
}
/* signal other side */
- virtqueue_fill(q->rx_vq, &elem, total, i++);
+ virtqueue_fill(q->rx_vq, elem, total, i++);
+ g_free(elem);
}
if (mhdr_cnt) {
@@ -1121,10 +1197,11 @@ static void virtio_net_tx_complete(NetClientState *nc, ssize_t len)
VirtIONetQueue *q = virtio_net_get_subqueue(nc);
VirtIODevice *vdev = VIRTIO_DEVICE(n);
- virtqueue_push(q->tx_vq, &q->async_tx.elem, 0);
+ virtqueue_push(q->tx_vq, q->async_tx.elem, 0);
virtio_notify(vdev, q->tx_vq);
- q->async_tx.elem.out_num = q->async_tx.len = 0;
+ g_free(q->async_tx.elem);
+ q->async_tx.elem = NULL;
virtio_queue_set_notification(q->tx_vq, 1);
virtio_net_flush_tx(q);
@@ -1135,25 +1212,31 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
{
VirtIONet *n = q->n;
VirtIODevice *vdev = VIRTIO_DEVICE(n);
- VirtQueueElement elem;
+ VirtQueueElement *elem;
int32_t num_packets = 0;
int queue_index = vq2q(virtio_get_queue_index(q->tx_vq));
if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
return num_packets;
}
- if (q->async_tx.elem.out_num) {
+ if (q->async_tx.elem) {
virtio_queue_set_notification(q->tx_vq, 0);
return num_packets;
}
- while (virtqueue_pop(q->tx_vq, &elem)) {
- ssize_t ret, len;
- unsigned int out_num = elem.out_num;
- struct iovec *out_sg = &elem.out_sg[0];
- struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1];
+ for (;;) {
+ ssize_t ret;
+ unsigned int out_num;
+ struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1], *out_sg;
struct virtio_net_hdr_mrg_rxbuf mhdr;
+ elem = virtqueue_pop(q->tx_vq, sizeof(VirtQueueElement));
+ if (!elem) {
+ break;
+ }
+
+ out_num = elem->out_num;
+ out_sg = elem->out_sg;
if (out_num < 1) {
error_report("virtio-net header not in first element");
exit(1);
@@ -1165,7 +1248,7 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
error_report("virtio-net header incorrect");
exit(1);
}
- if (virtio_needs_swap(vdev)) {
+ if (n->needs_vnet_hdr_swap) {
virtio_net_hdr_swap(vdev, (void *) &mhdr);
sg2[0].iov_base = &mhdr;
sg2[0].iov_len = n->guest_hdr_len;
@@ -1196,21 +1279,18 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
out_sg = sg;
}
- len = n->guest_hdr_len;
-
ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index),
out_sg, out_num, virtio_net_tx_complete);
if (ret == 0) {
virtio_queue_set_notification(q->tx_vq, 0);
q->async_tx.elem = elem;
- q->async_tx.len = len;
return -EBUSY;
}
- len += ret;
drop:
- virtqueue_push(q->tx_vq, &elem, 0);
+ virtqueue_push(q->tx_vq, elem, 0);
virtio_notify(vdev, q->tx_vq);
+ g_free(elem);
if (++num_packets >= n->tx_burst) {
break;
@@ -1453,7 +1533,7 @@ static void virtio_net_save_device(VirtIODevice *vdev, QEMUFile *f)
}
}
- if (virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
+ if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
qemu_put_be64(f, n->curr_guest_offloads);
}
}
@@ -1462,11 +1542,33 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
{
VirtIONet *n = opaque;
VirtIODevice *vdev = VIRTIO_DEVICE(n);
+ int ret;
if (version_id < 2 || version_id > VIRTIO_NET_VM_VERSION)
return -EINVAL;
- return virtio_load(vdev, f, version_id);
+ ret = virtio_load(vdev, f, version_id);
+ if (ret) {
+ return ret;
+ }
+
+ if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
+ n->curr_guest_offloads = qemu_get_be64(f);
+ } else {
+ n->curr_guest_offloads = virtio_net_supported_guest_offloads(n);
+ }
+
+ if (peer_has_vnet_hdr(n)) {
+ virtio_net_apply_guest_offloads(n);
+ }
+
+ if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) &&
+ virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) {
+ n->announce_counter = SELF_ANNOUNCE_ROUNDS;
+ timer_mod(n->announce_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL));
+ }
+
+ return 0;
}
static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f,
@@ -1479,7 +1581,8 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f,
n->vqs[0].tx_waiting = qemu_get_be32(f);
virtio_net_set_mrg_rx_bufs(n, qemu_get_be32(f),
- virtio_has_feature(vdev, VIRTIO_F_VERSION_1));
+ virtio_vdev_has_feature(vdev,
+ VIRTIO_F_VERSION_1));
if (version_id >= 3)
n->status = qemu_get_be16(f);
@@ -1562,16 +1665,6 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f,
}
}
- if (virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
- n->curr_guest_offloads = qemu_get_be64(f);
- } else {
- n->curr_guest_offloads = virtio_net_supported_guest_offloads(n);
- }
-
- if (peer_has_vnet_hdr(n)) {
- virtio_net_apply_guest_offloads(n);
- }
-
virtio_net_set_queues(n);
/* Find the first multicast entry in the saved MAC filter */
@@ -1589,12 +1682,6 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f,
qemu_get_subqueue(n->nic, i)->link_down = link_down;
}
- if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) &&
- virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) {
- n->announce_counter = SELF_ANNOUNCE_ROUNDS;
- timer_mod(n->announce_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL));
- }
-
return 0;
}
diff --git a/qemu/hw/net/vmware_utils.h b/qemu/hw/net/vmware_utils.h
index 1099df669..c0dbb2ff4 100644
--- a/qemu/hw/net/vmware_utils.h
+++ b/qemu/hw/net/vmware_utils.h
@@ -18,10 +18,7 @@
#define VMWARE_UTILS_H
#include "qemu/range.h"
-
-#ifndef VMW_SHPRN
-#define VMW_SHPRN(fmt, ...) do {} while (0)
-#endif
+#include "vmxnet_debug.h"
/*
* Shared memory access functions with byte swap support
@@ -52,7 +49,7 @@ vmw_shmem_rw(hwaddr addr, void *buf, int len, int is_write)
}
static inline void
-vmw_shmem_set(hwaddr addr, uint8 val, int len)
+vmw_shmem_set(hwaddr addr, uint8_t val, int len)
{
int i;
VMW_SHPRN("SHMEM set: %" PRIx64 ", len: %d (value 0x%X)", addr, len, val);
diff --git a/qemu/hw/net/vmxnet3.c b/qemu/hw/net/vmxnet3.c
index 59b06b841..093a71e12 100644
--- a/qemu/hw/net/vmxnet3.c
+++ b/qemu/hw/net/vmxnet3.c
@@ -15,6 +15,7 @@
*
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "net/net.h"
@@ -36,12 +37,28 @@
#define VMXNET3_MSIX_BAR_SIZE 0x2000
#define MIN_BUF_SIZE 60
+/* Compatability flags for migration */
+#define VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS_BIT 0
+#define VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS \
+ (1 << VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS_BIT)
+#define VMXNET3_COMPAT_FLAG_DISABLE_PCIE_BIT 1
+#define VMXNET3_COMPAT_FLAG_DISABLE_PCIE \
+ (1 << VMXNET3_COMPAT_FLAG_DISABLE_PCIE_BIT)
+
+#define VMXNET3_EXP_EP_OFFSET (0x48)
+#define VMXNET3_MSI_OFFSET(s) \
+ ((s)->compat_flags & VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS ? 0x50 : 0x84)
+#define VMXNET3_MSIX_OFFSET(s) \
+ ((s)->compat_flags & VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS ? 0 : 0x9c)
+#define VMXNET3_DSN_OFFSET (0x100)
+
#define VMXNET3_BAR0_IDX (0)
#define VMXNET3_BAR1_IDX (1)
#define VMXNET3_MSIX_BAR_IDX (2)
#define VMXNET3_OFF_MSIX_TABLE (0x000)
-#define VMXNET3_OFF_MSIX_PBA (0x800)
+#define VMXNET3_OFF_MSIX_PBA(s) \
+ ((s)->compat_flags & VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS ? 0x800 : 0x1000)
/* Link speed in Mbps should be shifted by 16 */
#define VMXNET3_LINK_SPEED (1000 << 16)
@@ -50,7 +67,7 @@
#define VMXNET3_LINK_STATUS_UP 0x1
/* Least significant bit should be set for revision and version */
-#define VMXNET3_DEVICE_VERSION 0x1
+#define VMXNET3_UPT_REVISION 0x1
#define VMXNET3_DEVICE_REVISION 0x1
/* Number of interrupt vectors for non-MSIx modes */
@@ -108,9 +125,19 @@
#define VMXNET_FLAG_IS_SET(field, flag) (((field) & (flag)) == (flag))
+typedef struct VMXNET3Class {
+ PCIDeviceClass parent_class;
+ DeviceRealize parent_dc_realize;
+} VMXNET3Class;
+
#define TYPE_VMXNET3 "vmxnet3"
#define VMXNET3(obj) OBJECT_CHECK(VMXNET3State, (obj), TYPE_VMXNET3)
+#define VMXNET3_DEVICE_CLASS(klass) \
+ OBJECT_CLASS_CHECK(VMXNET3Class, (klass), TYPE_VMXNET3)
+#define VMXNET3_DEVICE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(VMXNET3Class, (obj), TYPE_VMXNET3)
+
/* Cyclic ring abstraction */
typedef struct {
hwaddr pa;
@@ -138,7 +165,7 @@ static inline void vmxnet3_ring_init(Vmxnet3Ring *ring,
}
#define VMXNET3_RING_DUMP(macro, ring_name, ridx, r) \
- macro("%s#%d: base %" PRIx64 " size %lu cell_size %lu gen %d next %lu", \
+ macro("%s#%d: base %" PRIx64 " size %zu cell_size %zu gen %d next %zu", \
(ring_name), (ridx), \
(r)->pa, (r)->size, (r)->cell_size, (r)->gen, (r)->next)
@@ -313,6 +340,9 @@ typedef struct {
MACAddr *mcast_list;
uint32_t mcast_list_len;
uint32_t mcast_list_buff_size; /* needed for live migration. */
+
+ /* Compatability flags for migration */
+ uint32_t compat_flags;
} VMXNET3State;
/* Interrupt management */
@@ -493,7 +523,7 @@ vmxnet3_dec_rx_completion_counter(VMXNET3State *s, int qidx)
vmxnet3_ring_dec(&s->rxq_descr[qidx].comp_ring);
}
-static void vmxnet3_complete_packet(VMXNET3State *s, int qidx, uint32 tx_ridx)
+static void vmxnet3_complete_packet(VMXNET3State *s, int qidx, uint32_t tx_ridx)
{
struct Vmxnet3_TxCompDesc txcq_descr;
@@ -729,9 +759,7 @@ static void vmxnet3_process_tx_queue(VMXNET3State *s, int qidx)
}
if (txd.eop) {
- if (!s->skip_current_tx_pkt) {
- vmxnet_tx_pkt_parse(s->tx_pkt);
-
+ if (!s->skip_current_tx_pkt && vmxnet_tx_pkt_parse(s->tx_pkt)) {
if (s->needs_vlan) {
vmxnet_tx_pkt_setup_vlan_header(s->tx_pkt, s->tci);
}
@@ -927,9 +955,9 @@ static void vmxnet3_rx_need_csum_calculate(struct VmxnetRxPkt *pkt,
/* Validate packet len: csum_start + scum_offset + length of csum field */
if (pkt_len < (vhdr->csum_start + vhdr->csum_offset + 2)) {
- VMW_PKPRN("packet len:%d < csum_start(%d) + csum_offset(%d) + 2, "
+ VMW_PKPRN("packet len:%zu < csum_start(%d) + csum_offset(%d) + 2, "
"cannot calculate checksum",
- len, vhdr->csum_start, vhdr->csum_offset);
+ pkt_len, vhdr->csum_start, vhdr->csum_offset);
return;
}
@@ -1165,9 +1193,13 @@ vmxnet3_io_bar0_write(void *opaque, hwaddr addr,
static uint64_t
vmxnet3_io_bar0_read(void *opaque, hwaddr addr, unsigned size)
{
+ VMXNET3State *s = opaque;
+
if (VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_IMR,
VMXNET3_MAX_INTRS, VMXNET3_REG_ALIGN)) {
- g_assert_not_reached();
+ int l = VMW_MULTIREG_IDX_BY_ADDR(addr, VMXNET3_REG_IMR,
+ VMXNET3_REG_ALIGN);
+ return s->interrupt_states[l].is_masked;
}
VMW_CBPRN("BAR0 unknown read [%" PRIx64 "], size %d", addr, size);
@@ -1192,8 +1224,13 @@ static void vmxnet3_reset_mac(VMXNET3State *s)
static void vmxnet3_deactivate_device(VMXNET3State *s)
{
- VMW_CBPRN("Deactivating vmxnet3...");
- s->device_active = false;
+ if (s->device_active) {
+ VMW_CBPRN("Deactivating vmxnet3...");
+ vmxnet_tx_pkt_reset(s->tx_pkt);
+ vmxnet_tx_pkt_uninit(s->tx_pkt);
+ vmxnet_rx_pkt_uninit(s->rx_pkt);
+ s->device_active = false;
+ }
}
static void vmxnet3_reset(VMXNET3State *s)
@@ -1202,7 +1239,6 @@ static void vmxnet3_reset(VMXNET3State *s)
vmxnet3_deactivate_device(s);
vmxnet3_reset_interrupt_states(s);
- vmxnet_tx_pkt_reset(s->tx_pkt);
s->drv_shmem = 0;
s->tx_sop = true;
s->skip_current_tx_pkt = false;
@@ -1287,6 +1323,10 @@ static uint32_t vmxnet3_get_interrupt_config(VMXNET3State *s)
static void vmxnet3_fill_stats(VMXNET3State *s)
{
int i;
+
+ if (!s->device_active)
+ return;
+
for (i = 0; i < s->txq_num; i++) {
cpu_physical_memory_write(s->txq_descr[i].tx_stats_pa,
&s->txq_descr[i].txq_stats,
@@ -1425,6 +1465,12 @@ static void vmxnet3_activate_device(VMXNET3State *s)
return;
}
+ /* Verify if device is active */
+ if (s->device_active) {
+ VMW_CFPRN("Vmxnet3 device is active");
+ return;
+ }
+
vmxnet3_adjust_by_guest_type(s);
vmxnet3_update_features(s);
vmxnet3_update_pm_state(s);
@@ -1621,7 +1667,7 @@ static void vmxnet3_handle_command(VMXNET3State *s, uint64_t cmd)
break;
case VMXNET3_CMD_QUIESCE_DEV:
- VMW_CBPRN("Set: VMXNET3_CMD_QUIESCE_DEV - pause the device");
+ VMW_CBPRN("Set: VMXNET3_CMD_QUIESCE_DEV - deactivate the device");
vmxnet3_deactivate_device(s);
break;
@@ -1629,6 +1675,23 @@ static void vmxnet3_handle_command(VMXNET3State *s, uint64_t cmd)
VMW_CBPRN("Set: VMXNET3_CMD_GET_CONF_INTR - interrupt configuration");
break;
+ case VMXNET3_CMD_GET_ADAPTIVE_RING_INFO:
+ VMW_CBPRN("Set: VMXNET3_CMD_GET_ADAPTIVE_RING_INFO - "
+ "adaptive ring info flags");
+ break;
+
+ case VMXNET3_CMD_GET_DID_LO:
+ VMW_CBPRN("Set: Get lower part of device ID");
+ break;
+
+ case VMXNET3_CMD_GET_DID_HI:
+ VMW_CBPRN("Set: Get upper part of device ID");
+ break;
+
+ case VMXNET3_CMD_GET_DEV_EXTRA_INFO:
+ VMW_CBPRN("Set: Get device extra info");
+ break;
+
default:
VMW_CBPRN("Received unknown command: %" PRIx64, cmd);
break;
@@ -1641,13 +1704,14 @@ static uint64_t vmxnet3_get_command_status(VMXNET3State *s)
switch (s->last_command) {
case VMXNET3_CMD_ACTIVATE_DEV:
- ret = (s->device_active) ? 0 : -1;
+ ret = (s->device_active) ? 0 : 1;
VMW_CFPRN("Device active: %" PRIx64, ret);
break;
case VMXNET3_CMD_RESET_DEV:
case VMXNET3_CMD_QUIESCE_DEV:
case VMXNET3_CMD_GET_QUEUE_STATUS:
+ case VMXNET3_CMD_GET_DEV_EXTRA_INFO:
ret = 0;
break;
@@ -1668,9 +1732,21 @@ static uint64_t vmxnet3_get_command_status(VMXNET3State *s)
ret = vmxnet3_get_interrupt_config(s);
break;
+ case VMXNET3_CMD_GET_ADAPTIVE_RING_INFO:
+ ret = VMXNET3_DISABLE_ADAPTIVE_RING;
+ break;
+
+ case VMXNET3_CMD_GET_DID_LO:
+ ret = PCI_DEVICE_ID_VMWARE_VMXNET3;
+ break;
+
+ case VMXNET3_CMD_GET_DID_HI:
+ ret = VMXNET3_DEVICE_REVISION;
+ break;
+
default:
VMW_WRPRN("Received request for unknown command: %x", s->last_command);
- ret = -1;
+ ret = 0;
break;
}
@@ -1726,7 +1802,7 @@ vmxnet3_io_bar1_write(void *opaque,
* shared address only after we get the high part
*/
if (val == 0) {
- s->device_active = false;
+ vmxnet3_deactivate_device(s);
}
s->temp_shared_guest_driver_memory = val;
s->drv_shmem = 0;
@@ -1801,7 +1877,7 @@ vmxnet3_io_bar1_read(void *opaque, hwaddr addr, unsigned size)
/* UPT Version Report Selection */
case VMXNET3_REG_UVRS:
VMW_CBPRN("Read BAR1 [VMXNET3_REG_UVRS], size %d", size);
- ret = VMXNET3_DEVICE_VERSION;
+ ret = VMXNET3_UPT_REVISION;
break;
/* Command */
@@ -1959,7 +2035,7 @@ vmxnet3_receive(NetClientState *nc, const uint8_t *buf, size_t size)
vmxnet_rx_pkt_attach_data(s->rx_pkt, buf, size, s->rx_vlan_stripping);
bytes_indicated = vmxnet3_indicate_packet(s) ? size : -1;
if (bytes_indicated < size) {
- VMW_PKPRN("RX: %lu of %lu bytes indicated", bytes_indicated, size);
+ VMW_PKPRN("RX: %zu of %zu bytes indicated", bytes_indicated, size);
}
} else {
VMW_PKPRN("Packet dropped by RX filter");
@@ -1988,7 +2064,6 @@ static void vmxnet3_set_link_status(NetClientState *nc)
static NetClientInfo net_vmxnet3_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
- .can_receive = vmxnet3_can_receive,
.receive = vmxnet3_receive,
.link_status_changed = vmxnet3_set_link_status,
};
@@ -2001,16 +2076,13 @@ static bool vmxnet3_peer_has_vnet_hdr(VMXNET3State *s)
return true;
}
- VMW_WRPRN("Peer has no virtio extension. Task offloads will be emulated.");
return false;
}
static void vmxnet3_net_uninit(VMXNET3State *s)
{
g_free(s->mcast_list);
- vmxnet_tx_pkt_reset(s->tx_pkt);
- vmxnet_tx_pkt_uninit(s->tx_pkt);
- vmxnet_rx_pkt_uninit(s->rx_pkt);
+ vmxnet3_deactivate_device(s);
qemu_del_nic(s->nic);
}
@@ -2030,7 +2102,7 @@ static void vmxnet3_net_init(VMXNET3State *s)
s->link_status_and_speed = VMXNET3_LINK_SPEED | VMXNET3_LINK_STATUS_UP;
- VMW_CFPRN("Permanent MAC: " MAC_FMT, MAC_ARG(s->perm_mac.a));
+ VMW_CFPRN("Permanent MAC: " VMXNET_MF, VMXNET_MA(s->perm_mac.a));
s->nic = qemu_new_nic(&net_vmxnet3_info, &s->conf,
object_get_typename(OBJECT(s)),
@@ -2088,8 +2160,8 @@ vmxnet3_init_msix(VMXNET3State *s)
&s->msix_bar,
VMXNET3_MSIX_BAR_IDX, VMXNET3_OFF_MSIX_TABLE,
&s->msix_bar,
- VMXNET3_MSIX_BAR_IDX, VMXNET3_OFF_MSIX_PBA,
- 0);
+ VMXNET3_MSIX_BAR_IDX, VMXNET3_OFF_MSIX_PBA(s),
+ VMXNET3_MSIX_OFFSET(s));
if (0 > res) {
VMW_WRPRN("Failed to initialize MSI-X, error %d", res);
@@ -2117,7 +2189,6 @@ vmxnet3_cleanup_msix(VMXNET3State *s)
}
}
-#define VMXNET3_MSI_OFFSET (0x50)
#define VMXNET3_USE_64BIT (true)
#define VMXNET3_PER_VECTOR_MASK (false)
@@ -2127,7 +2198,7 @@ vmxnet3_init_msi(VMXNET3State *s)
PCIDevice *d = PCI_DEVICE(s);
int res;
- res = msi_init(d, VMXNET3_MSI_OFFSET, VMXNET3_MAX_NMSIX_INTRS,
+ res = msi_init(d, VMXNET3_MSI_OFFSET(s), VMXNET3_MAX_NMSIX_INTRS,
VMXNET3_USE_64BIT, VMXNET3_PER_VECTOR_MASK);
if (0 > res) {
VMW_WRPRN("Failed to initialize MSI, error %d", res);
@@ -2184,6 +2255,22 @@ static const MemoryRegionOps b1_ops = {
},
};
+static uint8_t *vmxnet3_device_serial_num(VMXNET3State *s)
+{
+ static uint64_t dsn_payload;
+ uint8_t *dsnp = (uint8_t *)&dsn_payload;
+
+ dsnp[0] = 0xfe;
+ dsnp[1] = s->conf.macaddr.a[3];
+ dsnp[2] = s->conf.macaddr.a[4];
+ dsnp[3] = s->conf.macaddr.a[5];
+ dsnp[4] = s->conf.macaddr.a[0];
+ dsnp[5] = s->conf.macaddr.a[1];
+ dsnp[6] = s->conf.macaddr.a[2];
+ dsnp[7] = 0xff;
+ return dsnp;
+}
+
static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp)
{
DeviceState *dev = DEVICE(pci_dev);
@@ -2221,6 +2308,17 @@ static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp)
vmxnet3_net_init(s);
+ if (pci_is_express(pci_dev)) {
+ if (pci_bus_is_express(pci_dev->bus)) {
+ pcie_endpoint_cap_init(pci_dev, VMXNET3_EXP_EP_OFFSET);
+ }
+
+ pcie_add_capability(pci_dev, PCI_EXT_CAP_ID_DSN, 0x1,
+ VMXNET3_DSN_OFFSET, PCI_EXT_CAP_DSN_SIZEOF);
+ memcpy(pci_dev->config + VMXNET3_DSN_OFFSET + 4,
+ vmxnet3_device_serial_num(s), sizeof(uint64_t));
+ }
+
register_savevm(dev, "vmxnet3-msix", -1, 1,
vmxnet3_msix_save, vmxnet3_msix_load, s);
}
@@ -2490,6 +2588,29 @@ static const VMStateInfo int_state_info = {
.put = vmxnet3_put_int_state
};
+static bool vmxnet3_vmstate_need_pcie_device(void *opaque)
+{
+ VMXNET3State *s = VMXNET3(opaque);
+
+ return !(s->compat_flags & VMXNET3_COMPAT_FLAG_DISABLE_PCIE);
+}
+
+static bool vmxnet3_vmstate_test_pci_device(void *opaque, int version_id)
+{
+ return !vmxnet3_vmstate_need_pcie_device(opaque);
+}
+
+static const VMStateDescription vmstate_vmxnet3_pcie_device = {
+ .name = "vmxnet3/pcie",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = vmxnet3_vmstate_need_pcie_device,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCIE_DEVICE(parent_obj, VMXNET3State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_vmxnet3 = {
.name = "vmxnet3",
.version_id = 1,
@@ -2497,7 +2618,9 @@ static const VMStateDescription vmstate_vmxnet3 = {
.pre_save = vmxnet3_pre_save,
.post_load = vmxnet3_post_load,
.fields = (VMStateField[]) {
- VMSTATE_PCI_DEVICE(parent_obj, VMXNET3State),
+ VMSTATE_STRUCT_TEST(parent_obj, VMXNET3State,
+ vmxnet3_vmstate_test_pci_device, 0,
+ vmstate_pci_device, PCIDevice),
VMSTATE_BOOL(rx_packets_compound, VMXNET3State),
VMSTATE_BOOL(rx_vlan_stripping, VMXNET3State),
VMSTATE_BOOL(lro_supported, VMXNET3State),
@@ -2532,19 +2655,38 @@ static const VMStateDescription vmstate_vmxnet3 = {
},
.subsections = (const VMStateDescription*[]) {
&vmxstate_vmxnet3_mcast_list,
+ &vmstate_vmxnet3_pcie_device,
NULL
}
};
static Property vmxnet3_properties[] = {
DEFINE_NIC_PROPERTIES(VMXNET3State, conf),
+ DEFINE_PROP_BIT("x-old-msi-offsets", VMXNET3State, compat_flags,
+ VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS_BIT, false),
+ DEFINE_PROP_BIT("x-disable-pcie", VMXNET3State, compat_flags,
+ VMXNET3_COMPAT_FLAG_DISABLE_PCIE_BIT, false),
DEFINE_PROP_END_OF_LIST(),
};
+static void vmxnet3_realize(DeviceState *qdev, Error **errp)
+{
+ VMXNET3Class *vc = VMXNET3_DEVICE_GET_CLASS(qdev);
+ PCIDevice *pci_dev = PCI_DEVICE(qdev);
+ VMXNET3State *s = VMXNET3(qdev);
+
+ if (!(s->compat_flags & VMXNET3_COMPAT_FLAG_DISABLE_PCIE)) {
+ pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
+ }
+
+ vc->parent_dc_realize(qdev, errp);
+}
+
static void vmxnet3_class_init(ObjectClass *class, void *data)
{
DeviceClass *dc = DEVICE_CLASS(class);
PCIDeviceClass *c = PCI_DEVICE_CLASS(class);
+ VMXNET3Class *vc = VMXNET3_DEVICE_CLASS(class);
c->realize = vmxnet3_pci_realize;
c->exit = vmxnet3_pci_uninit;
@@ -2554,6 +2696,8 @@ static void vmxnet3_class_init(ObjectClass *class, void *data)
c->class_id = PCI_CLASS_NETWORK_ETHERNET;
c->subsystem_vendor_id = PCI_VENDOR_ID_VMWARE;
c->subsystem_id = PCI_DEVICE_ID_VMWARE_VMXNET3;
+ vc->parent_dc_realize = dc->realize;
+ dc->realize = vmxnet3_realize;
dc->desc = "VMWare Paravirtualized Ethernet v3";
dc->reset = vmxnet3_qdev_reset;
dc->vmsd = &vmstate_vmxnet3;
@@ -2564,6 +2708,7 @@ static void vmxnet3_class_init(ObjectClass *class, void *data)
static const TypeInfo vmxnet3_info = {
.name = TYPE_VMXNET3,
.parent = TYPE_PCI_DEVICE,
+ .class_size = sizeof(VMXNET3Class),
.instance_size = sizeof(VMXNET3State),
.class_init = vmxnet3_class_init,
.instance_init = vmxnet3_instance_init,
diff --git a/qemu/hw/net/vmxnet3.h b/qemu/hw/net/vmxnet3.h
index f987d7126..f7006afe9 100644
--- a/qemu/hw/net/vmxnet3.h
+++ b/qemu/hw/net/vmxnet3.h
@@ -198,9 +198,13 @@ enum {
VMXNET3_CMD_GET_DID_LO, /* 0xF00D0005 */
VMXNET3_CMD_GET_DID_HI, /* 0xF00D0006 */
VMXNET3_CMD_GET_DEV_EXTRA_INFO, /* 0xF00D0007 */
- VMXNET3_CMD_GET_CONF_INTR /* 0xF00D0008 */
+ VMXNET3_CMD_GET_CONF_INTR, /* 0xF00D0008 */
+ VMXNET3_CMD_GET_ADAPTIVE_RING_INFO /* 0xF00D0009 */
};
+/* Adaptive Ring Info Flags */
+#define VMXNET3_DISABLE_ADAPTIVE_RING 1
+
/*
* Little Endian layout of bitfields -
* Byte 0 : 7.....len.....0
diff --git a/qemu/hw/net/vmxnet_debug.h b/qemu/hw/net/vmxnet_debug.h
index 96dae0f91..96495dbb1 100644
--- a/qemu/hw/net/vmxnet_debug.h
+++ b/qemu/hw/net/vmxnet_debug.h
@@ -20,94 +20,127 @@
#define VMXNET_DEVICE_NAME "vmxnet3"
-/* #define VMXNET_DEBUG_CB */
#define VMXNET_DEBUG_WARNINGS
#define VMXNET_DEBUG_ERRORS
-/* #define VMXNET_DEBUG_INTERRUPTS */
-/* #define VMXNET_DEBUG_CONFIG */
-/* #define VMXNET_DEBUG_RINGS */
-/* #define VMXNET_DEBUG_PACKETS */
-/* #define VMXNET_DEBUG_SHMEM_ACCESS */
+
+#undef VMXNET_DEBUG_CB
+#undef VMXNET_DEBUG_INTERRUPTS
+#undef VMXNET_DEBUG_CONFIG
+#undef VMXNET_DEBUG_RINGS
+#undef VMXNET_DEBUG_PACKETS
+#undef VMXNET_DEBUG_SHMEM_ACCESS
+
+#ifdef VMXNET_DEBUG_CB
+# define VMXNET_DEBUG_CB_ENABLED 1
+#else
+# define VMXNET_DEBUG_CB_ENABLED 0
+#endif
+
+#ifdef VMXNET_DEBUG_WARNINGS
+# define VMXNET_DEBUG_WARNINGS_ENABLED 1
+#else
+# define VMXNET_DEBUG_WARNINGS_ENABLED 0
+#endif
+
+#ifdef VMXNET_DEBUG_ERRORS
+# define VMXNET_DEBUG_ERRORS_ENABLED 1
+#else
+# define VMXNET_DEBUG_ERRORS_ENABLED 0
+#endif
+
+#ifdef VMXNET_DEBUG_CONFIG
+# define VMXNET_DEBUG_CONFIG_ENABLED 1
+#else
+# define VMXNET_DEBUG_CONFIG_ENABLED 0
+#endif
+
+#ifdef VMXNET_DEBUG_RINGS
+# define VMXNET_DEBUG_RINGS_ENABLED 1
+#else
+# define VMXNET_DEBUG_RINGS_ENABLED 0
+#endif
+
+#ifdef VMXNET_DEBUG_PACKETS
+# define VMXNET_DEBUG_PACKETS_ENABLED 1
+#else
+# define VMXNET_DEBUG_PACKETS_ENABLED 0
+#endif
+
+#ifdef VMXNET_DEBUG_INTERRUPTS
+# define VMXNET_DEBUG_INTERRUPTS_ENABLED 1
+#else
+# define VMXNET_DEBUG_INTERRUPTS_ENABLED 0
+#endif
#ifdef VMXNET_DEBUG_SHMEM_ACCESS
+# define VMXNET_DEBUG_SHMEM_ACCESS_ENABLED 1
+#else
+# define VMXNET_DEBUG_SHMEM_ACCESS_ENABLED 0
+#endif
+
#define VMW_SHPRN(fmt, ...) \
do { \
- printf("[%s][SH][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \
- ## __VA_ARGS__); \
+ if (VMXNET_DEBUG_SHMEM_ACCESS_ENABLED) { \
+ printf("[%s][SH][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \
+ ## __VA_ARGS__); \
+ } \
} while (0)
-#else
-#define VMW_SHPRN(fmt, ...) do {} while (0)
-#endif
-#ifdef VMXNET_DEBUG_CB
#define VMW_CBPRN(fmt, ...) \
do { \
- printf("[%s][CB][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \
- ## __VA_ARGS__); \
+ if (VMXNET_DEBUG_CB_ENABLED) { \
+ printf("[%s][CB][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \
+ ## __VA_ARGS__); \
+ } \
} while (0)
-#else
-#define VMW_CBPRN(fmt, ...) do {} while (0)
-#endif
-#ifdef VMXNET_DEBUG_PACKETS
#define VMW_PKPRN(fmt, ...) \
do { \
- printf("[%s][PK][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \
- ## __VA_ARGS__); \
+ if (VMXNET_DEBUG_PACKETS_ENABLED) { \
+ printf("[%s][PK][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \
+ ## __VA_ARGS__); \
+ } \
} while (0)
-#else
-#define VMW_PKPRN(fmt, ...) do {} while (0)
-#endif
-#ifdef VMXNET_DEBUG_WARNINGS
#define VMW_WRPRN(fmt, ...) \
do { \
- printf("[%s][WR][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \
- ## __VA_ARGS__); \
+ if (VMXNET_DEBUG_WARNINGS_ENABLED) { \
+ printf("[%s][WR][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \
+ ## __VA_ARGS__); \
+ } \
} while (0)
-#else
-#define VMW_WRPRN(fmt, ...) do {} while (0)
-#endif
-#ifdef VMXNET_DEBUG_ERRORS
#define VMW_ERPRN(fmt, ...) \
do { \
- printf("[%s][ER][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \
- ## __VA_ARGS__); \
+ if (VMXNET_DEBUG_ERRORS_ENABLED) { \
+ printf("[%s][ER][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \
+ ## __VA_ARGS__); \
+ } \
} while (0)
-#else
-#define VMW_ERPRN(fmt, ...) do {} while (0)
-#endif
-#ifdef VMXNET_DEBUG_INTERRUPTS
#define VMW_IRPRN(fmt, ...) \
do { \
- printf("[%s][IR][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \
- ## __VA_ARGS__); \
+ if (VMXNET_DEBUG_INTERRUPTS_ENABLED) { \
+ printf("[%s][IR][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \
+ ## __VA_ARGS__); \
+ } \
} while (0)
-#else
-#define VMW_IRPRN(fmt, ...) do {} while (0)
-#endif
-#ifdef VMXNET_DEBUG_CONFIG
#define VMW_CFPRN(fmt, ...) \
do { \
- printf("[%s][CF][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \
- ## __VA_ARGS__); \
+ if (VMXNET_DEBUG_CONFIG_ENABLED) { \
+ printf("[%s][CF][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \
+ ## __VA_ARGS__); \
+ } \
} while (0)
-#else
-#define VMW_CFPRN(fmt, ...) do {} while (0)
-#endif
-#ifdef VMXNET_DEBUG_RINGS
#define VMW_RIPRN(fmt, ...) \
do { \
- printf("[%s][RI][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \
- ## __VA_ARGS__); \
+ if (VMXNET_DEBUG_RINGS_ENABLED) { \
+ printf("[%s][RI][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \
+ ## __VA_ARGS__); \
+ } \
} while (0)
-#else
-#define VMW_RIPRN(fmt, ...) do {} while (0)
-#endif
#define VMXNET_MF "%02X:%02X:%02X:%02X:%02X:%02X"
#define VMXNET_MA(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
diff --git a/qemu/hw/net/vmxnet_rx_pkt.c b/qemu/hw/net/vmxnet_rx_pkt.c
index aa5462931..21bb46e68 100644
--- a/qemu/hw/net/vmxnet_rx_pkt.c
+++ b/qemu/hw/net/vmxnet_rx_pkt.c
@@ -15,6 +15,7 @@
*
*/
+#include "qemu/osdep.h"
#include "vmxnet_rx_pkt.h"
#include "net/eth.h"
#include "qemu-common.h"
diff --git a/qemu/hw/net/vmxnet_rx_pkt.h b/qemu/hw/net/vmxnet_rx_pkt.h
index a425846b5..0a45c1ba0 100644
--- a/qemu/hw/net/vmxnet_rx_pkt.h
+++ b/qemu/hw/net/vmxnet_rx_pkt.h
@@ -18,8 +18,6 @@
#ifndef VMXNET_RX_PKT_H
#define VMXNET_RX_PKT_H
-#include "stdint.h"
-#include "stdbool.h"
#include "net/eth.h"
/* defines to enable packet dump functions */
diff --git a/qemu/hw/net/vmxnet_tx_pkt.c b/qemu/hw/net/vmxnet_tx_pkt.c
index f7344c4cb..91e1e08fd 100644
--- a/qemu/hw/net/vmxnet_tx_pkt.c
+++ b/qemu/hw/net/vmxnet_tx_pkt.c
@@ -15,6 +15,7 @@
*
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "vmxnet_tx_pkt.h"
#include "net/eth.h"
@@ -142,11 +143,24 @@ static bool vmxnet_tx_pkt_parse_headers(struct VmxnetTxPkt *pkt)
bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, 0, l2_hdr->iov_base,
ETH_MAX_L2_HDR_LEN);
- if (bytes_read < ETH_MAX_L2_HDR_LEN) {
+ if (bytes_read < sizeof(struct eth_header)) {
+ l2_hdr->iov_len = 0;
+ return false;
+ }
+
+ l2_hdr->iov_len = sizeof(struct eth_header);
+ switch (be16_to_cpu(PKT_GET_ETH_HDR(l2_hdr->iov_base)->h_proto)) {
+ case ETH_P_VLAN:
+ l2_hdr->iov_len += sizeof(struct vlan_header);
+ break;
+ case ETH_P_DVLAN:
+ l2_hdr->iov_len += 2 * sizeof(struct vlan_header);
+ break;
+ }
+
+ if (bytes_read < l2_hdr->iov_len) {
l2_hdr->iov_len = 0;
return false;
- } else {
- l2_hdr->iov_len = eth_get_l2_hdr_length(l2_hdr->iov_base);
}
l3_proto = eth_get_l3_proto(l2_hdr->iov_base, l2_hdr->iov_len);
diff --git a/qemu/hw/net/vmxnet_tx_pkt.h b/qemu/hw/net/vmxnet_tx_pkt.h
index 57121a6fe..f51e98ad9 100644
--- a/qemu/hw/net/vmxnet_tx_pkt.h
+++ b/qemu/hw/net/vmxnet_tx_pkt.h
@@ -18,8 +18,6 @@
#ifndef VMXNET_TX_PKT_H
#define VMXNET_TX_PKT_H
-#include "stdint.h"
-#include "stdbool.h"
#include "net/eth.h"
#include "exec/hwaddr.h"
diff --git a/qemu/hw/net/xen_nic.c b/qemu/hw/net/xen_nic.c
index d7cbfc103..7281730d9 100644
--- a/qemu/hw/net/xen_nic.c
+++ b/qemu/hw/net/xen_nic.c
@@ -19,19 +19,9 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <unistd.h>
-#include <signal.h>
-#include <inttypes.h>
-#include <fcntl.h>
-#include <errno.h>
+#include "qemu/osdep.h"
#include <sys/socket.h>
#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/wait.h>
@@ -169,7 +159,7 @@ static void net_tx_packets(struct XenNetDev *netdev)
(txreq.flags & NETTXF_more_data) ? " more_data" : "",
(txreq.flags & NETTXF_extra_info) ? " extra_info" : "");
- page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev,
+ page = xengnttab_map_grant_ref(netdev->xendev.gnttabdev,
netdev->xendev.dom,
txreq.gref, PROT_READ);
if (page == NULL) {
@@ -191,7 +181,7 @@ static void net_tx_packets(struct XenNetDev *netdev)
qemu_send_packet(qemu_get_queue(netdev->nic),
page + txreq.offset, txreq.size);
}
- xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1);
+ xengnttab_unmap(netdev->xendev.gnttabdev, page, 1);
net_tx_response(netdev, &txreq, NETIF_RSP_OKAY);
}
if (!netdev->tx_work) {
@@ -261,7 +251,7 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size
memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq));
netdev->rx_ring.req_cons = ++rc;
- page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev,
+ page = xengnttab_map_grant_ref(netdev->xendev.gnttabdev,
netdev->xendev.dom,
rxreq.gref, PROT_WRITE);
if (page == NULL) {
@@ -271,7 +261,7 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size
return -1;
}
memcpy(page + NET_IP_ALIGN, buf, size);
- xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1);
+ xengnttab_unmap(netdev->xendev.gnttabdev, page, 1);
net_rx_response(netdev, &rxreq, NETIF_RSP_OKAY, NET_IP_ALIGN, size, 0);
return size;
@@ -343,19 +333,19 @@ static int net_connect(struct XenDevice *xendev)
return -1;
}
- netdev->txs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev,
+ netdev->txs = xengnttab_map_grant_ref(netdev->xendev.gnttabdev,
netdev->xendev.dom,
netdev->tx_ring_ref,
PROT_READ | PROT_WRITE);
if (!netdev->txs) {
return -1;
}
- netdev->rxs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev,
+ netdev->rxs = xengnttab_map_grant_ref(netdev->xendev.gnttabdev,
netdev->xendev.dom,
netdev->rx_ring_ref,
PROT_READ | PROT_WRITE);
if (!netdev->rxs) {
- xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->txs, 1);
+ xengnttab_unmap(netdev->xendev.gnttabdev, netdev->txs, 1);
netdev->txs = NULL;
return -1;
}
@@ -380,11 +370,11 @@ static void net_disconnect(struct XenDevice *xendev)
xen_be_unbind_evtchn(&netdev->xendev);
if (netdev->txs) {
- xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->txs, 1);
+ xengnttab_unmap(netdev->xendev.gnttabdev, netdev->txs, 1);
netdev->txs = NULL;
}
if (netdev->rxs) {
- xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->rxs, 1);
+ xengnttab_unmap(netdev->xendev.gnttabdev, netdev->rxs, 1);
netdev->rxs = NULL;
}
}
diff --git a/qemu/hw/net/xgmac.c b/qemu/hw/net/xgmac.c
index 15fb68194..0c5f793bd 100644
--- a/qemu/hw/net/xgmac.c
+++ b/qemu/hw/net/xgmac.c
@@ -24,6 +24,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "sysemu/char.h"
#include "qemu/log.h"
diff --git a/qemu/hw/net/xilinx_axienet.c b/qemu/hw/net/xilinx_axienet.c
index d63c42324..de23ab5dc 100644
--- a/qemu/hw/net/xilinx_axienet.c
+++ b/qemu/hw/net/xilinx_axienet.c
@@ -22,7 +22,9 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
+#include "qapi/error.h"
#include "qemu/log.h"
#include "net/net.h"
#include "net/checksum.h"
diff --git a/qemu/hw/net/xilinx_ethlite.c b/qemu/hw/net/xilinx_ethlite.c
index ad6b55306..bc846e709 100644
--- a/qemu/hw/net/xilinx_ethlite.c
+++ b/qemu/hw/net/xilinx_ethlite.c
@@ -22,6 +22,9 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h" /* FIXME should not use tswap* */
#include "hw/sysbus.h"
#include "hw/hw.h"
#include "net/net.h"
diff --git a/qemu/hw/nvram/ds1225y.c b/qemu/hw/nvram/ds1225y.c
index 332598b25..57d5ab215 100644
--- a/qemu/hw/nvram/ds1225y.c
+++ b/qemu/hw/nvram/ds1225y.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "trace.h"
diff --git a/qemu/hw/nvram/eeprom93xx.c b/qemu/hw/nvram/eeprom93xx.c
index 0af4d6707..2c16fc23d 100644
--- a/qemu/hw/nvram/eeprom93xx.c
+++ b/qemu/hw/nvram/eeprom93xx.c
@@ -35,6 +35,7 @@
* - No emulation of EEPROM timings.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/nvram/eeprom93xx.h"
diff --git a/qemu/hw/nvram/fw_cfg.c b/qemu/hw/nvram/fw_cfg.c
index 88481b78c..999f48028 100644
--- a/qemu/hw/nvram/fw_cfg.c
+++ b/qemu/hw/nvram/fw_cfg.c
@@ -21,16 +21,19 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "sysemu/sysemu.h"
+#include "sysemu/dma.h"
#include "hw/isa/isa.h"
#include "hw/nvram/fw_cfg.h"
#include "hw/sysbus.h"
+#include "hw/boards.h"
#include "trace.h"
#include "qemu/error-report.h"
#include "qemu/config-file.h"
+#include "qemu/cutils.h"
-#define FW_CFG_SIZE 2
#define FW_CFG_NAME "fw_cfg"
#define FW_CFG_PATH "/machine/" FW_CFG_NAME
@@ -42,6 +45,18 @@
#define FW_CFG_IO(obj) OBJECT_CHECK(FWCfgIoState, (obj), TYPE_FW_CFG_IO)
#define FW_CFG_MEM(obj) OBJECT_CHECK(FWCfgMemState, (obj), TYPE_FW_CFG_MEM)
+/* FW_CFG_VERSION bits */
+#define FW_CFG_VERSION 0x01
+#define FW_CFG_VERSION_DMA 0x02
+
+/* FW_CFG_DMA_CONTROL bits */
+#define FW_CFG_DMA_CTL_ERROR 0x01
+#define FW_CFG_DMA_CTL_READ 0x02
+#define FW_CFG_DMA_CTL_SKIP 0x04
+#define FW_CFG_DMA_CTL_SELECT 0x08
+
+#define FW_CFG_DMA_SIGNATURE 0x51454d5520434647ULL /* "QEMU CFG" */
+
typedef struct FWCfgEntry {
uint32_t len;
uint8_t *data;
@@ -55,10 +70,18 @@ struct FWCfgState {
/*< public >*/
FWCfgEntry entries[2][FW_CFG_MAX_ENTRY];
+ int entry_order[FW_CFG_MAX_ENTRY];
FWCfgFiles *files;
uint16_t cur_entry;
uint32_t cur_offset;
Notifier machine_ready;
+
+ int fw_cfg_order_override;
+
+ bool dma_enabled;
+ dma_addr_t dma_addr;
+ AddressSpace *dma_as;
+ MemoryRegion dma_iomem;
};
struct FWCfgIoState {
@@ -67,7 +90,7 @@ struct FWCfgIoState {
/*< public >*/
MemoryRegion comb_iomem;
- uint32_t iobase;
+ uint32_t iobase, dma_iobase;
};
struct FWCfgMemState {
@@ -187,9 +210,7 @@ static void fw_cfg_bootsplash(FWCfgState *s)
g_free(filename);
return;
}
- if (boot_splash_filedata != NULL) {
- g_free(boot_splash_filedata);
- }
+ g_free(boot_splash_filedata);
boot_splash_filedata = (uint8_t *)file_data;
boot_splash_filedata_size = file_size;
@@ -236,7 +257,8 @@ static void fw_cfg_write(FWCfgState *s, uint8_t value)
static int fw_cfg_select(FWCfgState *s, uint16_t key)
{
- int ret;
+ int arch, ret;
+ FWCfgEntry *e;
s->cur_offset = 0;
if ((key & FW_CFG_ENTRY_MASK) >= FW_CFG_MAX_ENTRY) {
@@ -245,41 +267,45 @@ static int fw_cfg_select(FWCfgState *s, uint16_t key)
} else {
s->cur_entry = key;
ret = 1;
- }
-
- trace_fw_cfg_select(s, key, ret);
- return ret;
-}
-
-static uint8_t fw_cfg_read(FWCfgState *s)
-{
- int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
- FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
- uint8_t ret;
-
- if (s->cur_entry == FW_CFG_INVALID || !e->data || s->cur_offset >= e->len)
- ret = 0;
- else {
+ /* entry successfully selected, now run callback if present */
+ arch = !!(key & FW_CFG_ARCH_LOCAL);
+ e = &s->entries[arch][key & FW_CFG_ENTRY_MASK];
if (e->read_callback) {
- e->read_callback(e->callback_opaque, s->cur_offset);
+ e->read_callback(e->callback_opaque);
}
- ret = e->data[s->cur_offset++];
}
- trace_fw_cfg_read(s, ret);
+ trace_fw_cfg_select(s, key, ret);
return ret;
}
-static uint64_t fw_cfg_data_mem_read(void *opaque, hwaddr addr,
- unsigned size)
+static uint64_t fw_cfg_data_read(void *opaque, hwaddr addr, unsigned size)
{
FWCfgState *s = opaque;
+ int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
+ FWCfgEntry *e = (s->cur_entry == FW_CFG_INVALID) ? NULL :
+ &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
uint64_t value = 0;
- unsigned i;
- for (i = 0; i < size; ++i) {
- value = (value << 8) | fw_cfg_read(s);
+ assert(size > 0 && size <= sizeof(value));
+ if (s->cur_entry != FW_CFG_INVALID && e->data && s->cur_offset < e->len) {
+ /* The least significant 'size' bytes of the return value are
+ * expected to contain a string preserving portion of the item
+ * data, padded with zeros on the right in case we run out early.
+ * In technical terms, we're composing the host-endian representation
+ * of the big endian interpretation of the fw_cfg string.
+ */
+ do {
+ value = (value << 8) | e->data[s->cur_offset++];
+ } while (--size && s->cur_offset < e->len);
+ /* If size is still not zero, we *did* run out early, so continue
+ * left-shifting, to add the appropriate number of padding zeros
+ * on the right.
+ */
+ value <<= 8 * size;
}
+
+ trace_fw_cfg_read(s, value);
return value;
}
@@ -294,6 +320,126 @@ static void fw_cfg_data_mem_write(void *opaque, hwaddr addr,
} while (i);
}
+static void fw_cfg_dma_transfer(FWCfgState *s)
+{
+ dma_addr_t len;
+ FWCfgDmaAccess dma;
+ int arch;
+ FWCfgEntry *e;
+ int read;
+ dma_addr_t dma_addr;
+
+ /* Reset the address before the next access */
+ dma_addr = s->dma_addr;
+ s->dma_addr = 0;
+
+ if (dma_memory_read(s->dma_as, dma_addr, &dma, sizeof(dma))) {
+ stl_be_dma(s->dma_as, dma_addr + offsetof(FWCfgDmaAccess, control),
+ FW_CFG_DMA_CTL_ERROR);
+ return;
+ }
+
+ dma.address = be64_to_cpu(dma.address);
+ dma.length = be32_to_cpu(dma.length);
+ dma.control = be32_to_cpu(dma.control);
+
+ if (dma.control & FW_CFG_DMA_CTL_SELECT) {
+ fw_cfg_select(s, dma.control >> 16);
+ }
+
+ arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
+ e = (s->cur_entry == FW_CFG_INVALID) ? NULL :
+ &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
+
+ if (dma.control & FW_CFG_DMA_CTL_READ) {
+ read = 1;
+ } else if (dma.control & FW_CFG_DMA_CTL_SKIP) {
+ read = 0;
+ } else {
+ dma.length = 0;
+ }
+
+ dma.control = 0;
+
+ while (dma.length > 0 && !(dma.control & FW_CFG_DMA_CTL_ERROR)) {
+ if (s->cur_entry == FW_CFG_INVALID || !e->data ||
+ s->cur_offset >= e->len) {
+ len = dma.length;
+
+ /* If the access is not a read access, it will be a skip access,
+ * tested before.
+ */
+ if (read) {
+ if (dma_memory_set(s->dma_as, dma.address, 0, len)) {
+ dma.control |= FW_CFG_DMA_CTL_ERROR;
+ }
+ }
+
+ } else {
+ if (dma.length <= (e->len - s->cur_offset)) {
+ len = dma.length;
+ } else {
+ len = (e->len - s->cur_offset);
+ }
+
+ /* If the access is not a read access, it will be a skip access,
+ * tested before.
+ */
+ if (read) {
+ if (dma_memory_write(s->dma_as, dma.address,
+ &e->data[s->cur_offset], len)) {
+ dma.control |= FW_CFG_DMA_CTL_ERROR;
+ }
+ }
+
+ s->cur_offset += len;
+ }
+
+ dma.address += len;
+ dma.length -= len;
+
+ }
+
+ stl_be_dma(s->dma_as, dma_addr + offsetof(FWCfgDmaAccess, control),
+ dma.control);
+
+ trace_fw_cfg_read(s, 0);
+}
+
+static uint64_t fw_cfg_dma_mem_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ /* Return a signature value (and handle various read sizes) */
+ return extract64(FW_CFG_DMA_SIGNATURE, (8 - addr - size) * 8, size * 8);
+}
+
+static void fw_cfg_dma_mem_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ FWCfgState *s = opaque;
+
+ if (size == 4) {
+ if (addr == 0) {
+ /* FWCfgDmaAccess high address */
+ s->dma_addr = value << 32;
+ } else if (addr == 4) {
+ /* FWCfgDmaAccess low address */
+ s->dma_addr |= value;
+ fw_cfg_dma_transfer(s);
+ }
+ } else if (size == 8 && addr == 0) {
+ s->dma_addr = value;
+ fw_cfg_dma_transfer(s);
+ }
+}
+
+static bool fw_cfg_dma_mem_valid(void *opaque, hwaddr addr,
+ unsigned size, bool is_write)
+{
+ return !is_write || ((size == 4 && (addr == 0 || addr == 4)) ||
+ (size == 8 && addr == 0));
+}
+
static bool fw_cfg_data_mem_valid(void *opaque, hwaddr addr,
unsigned size, bool is_write)
{
@@ -312,12 +458,6 @@ static bool fw_cfg_ctl_mem_valid(void *opaque, hwaddr addr,
return is_write && size == 2;
}
-static uint64_t fw_cfg_comb_read(void *opaque, hwaddr addr,
- unsigned size)
-{
- return fw_cfg_read(opaque);
-}
-
static void fw_cfg_comb_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size)
{
@@ -344,7 +484,7 @@ static const MemoryRegionOps fw_cfg_ctl_mem_ops = {
};
static const MemoryRegionOps fw_cfg_data_mem_ops = {
- .read = fw_cfg_data_mem_read,
+ .read = fw_cfg_data_read,
.write = fw_cfg_data_mem_write,
.endianness = DEVICE_BIG_ENDIAN,
.valid = {
@@ -355,17 +495,27 @@ static const MemoryRegionOps fw_cfg_data_mem_ops = {
};
static const MemoryRegionOps fw_cfg_comb_mem_ops = {
- .read = fw_cfg_comb_read,
+ .read = fw_cfg_data_read,
.write = fw_cfg_comb_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid.accepts = fw_cfg_comb_valid,
};
+static const MemoryRegionOps fw_cfg_dma_mem_ops = {
+ .read = fw_cfg_dma_mem_read,
+ .write = fw_cfg_dma_mem_write,
+ .endianness = DEVICE_BIG_ENDIAN,
+ .valid.accepts = fw_cfg_dma_mem_valid,
+ .valid.max_access_size = 8,
+ .impl.max_access_size = 8,
+};
+
static void fw_cfg_reset(DeviceState *d)
{
FWCfgState *s = FW_CFG(d);
- fw_cfg_select(s, 0);
+ /* we never register a read callback for FW_CFG_SIGNATURE */
+ fw_cfg_select(s, FW_CFG_SIGNATURE);
}
/* Save restore 32 bit int as uint16_t
@@ -401,6 +551,22 @@ static bool is_version_1(void *opaque, int version_id)
return version_id == 1;
}
+static bool fw_cfg_dma_enabled(void *opaque)
+{
+ FWCfgState *s = opaque;
+
+ return s->dma_enabled;
+}
+
+static const VMStateDescription vmstate_fw_cfg_dma = {
+ .name = "fw_cfg/dma",
+ .needed = fw_cfg_dma_enabled,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(dma_addr, FWCfgState),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
static const VMStateDescription vmstate_fw_cfg = {
.name = "fw_cfg",
.version_id = 2,
@@ -410,6 +576,10 @@ static const VMStateDescription vmstate_fw_cfg = {
VMSTATE_UINT16_HACK(cur_offset, FWCfgState, is_version_1),
VMSTATE_UINT32_V(cur_offset, FWCfgState, 2),
VMSTATE_END_OF_LIST()
+ },
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_fw_cfg_dma,
+ NULL,
}
};
@@ -499,12 +669,87 @@ void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value)
fw_cfg_add_bytes(s, key, copy, sizeof(value));
}
+void fw_cfg_set_order_override(FWCfgState *s, int order)
+{
+ assert(s->fw_cfg_order_override == 0);
+ s->fw_cfg_order_override = order;
+}
+
+void fw_cfg_reset_order_override(FWCfgState *s)
+{
+ assert(s->fw_cfg_order_override != 0);
+ s->fw_cfg_order_override = 0;
+}
+
+/*
+ * This is the legacy order list. For legacy systems, files are in
+ * the fw_cfg in the order defined below, by the "order" value. Note
+ * that some entries (VGA ROMs, NIC option ROMS, etc.) go into a
+ * specific area, but there may be more than one and they occur in the
+ * order that the user specifies them on the command line. Those are
+ * handled in a special manner, using the order override above.
+ *
+ * For non-legacy, the files are sorted by filename to avoid this kind
+ * of complexity in the future.
+ *
+ * This is only for x86, other arches don't implement versioning so
+ * they won't set legacy mode.
+ */
+static struct {
+ const char *name;
+ int order;
+} fw_cfg_order[] = {
+ { "etc/boot-menu-wait", 10 },
+ { "bootsplash.jpg", 11 },
+ { "bootsplash.bmp", 12 },
+ { "etc/boot-fail-wait", 15 },
+ { "etc/smbios/smbios-tables", 20 },
+ { "etc/smbios/smbios-anchor", 30 },
+ { "etc/e820", 40 },
+ { "etc/reserved-memory-end", 50 },
+ { "genroms/kvmvapic.bin", 55 },
+ { "genroms/linuxboot.bin", 60 },
+ { }, /* VGA ROMs from pc_vga_init come here, 70. */
+ { }, /* NIC option ROMs from pc_nic_init come here, 80. */
+ { "etc/system-states", 90 },
+ { }, /* User ROMs come here, 100. */
+ { }, /* Device FW comes here, 110. */
+ { "etc/extra-pci-roots", 120 },
+ { "etc/acpi/tables", 130 },
+ { "etc/table-loader", 140 },
+ { "etc/tpm/log", 150 },
+ { "etc/acpi/rsdp", 160 },
+ { "bootorder", 170 },
+
+#define FW_CFG_ORDER_OVERRIDE_LAST 200
+};
+
+static int get_fw_cfg_order(FWCfgState *s, const char *name)
+{
+ int i;
+
+ if (s->fw_cfg_order_override > 0)
+ return s->fw_cfg_order_override;
+
+ for (i = 0; i < ARRAY_SIZE(fw_cfg_order); i++) {
+ if (fw_cfg_order[i].name == NULL)
+ continue;
+ if (strcmp(name, fw_cfg_order[i].name) == 0)
+ return fw_cfg_order[i].order;
+ }
+ /* Stick unknown stuff at the end. */
+ error_report("warning: Unknown firmware file in legacy mode: %s\n", name);
+ return FW_CFG_ORDER_OVERRIDE_LAST;
+}
+
void fw_cfg_add_file_callback(FWCfgState *s, const char *filename,
FWCfgReadCallback callback, void *callback_opaque,
void *data, size_t len)
{
- int i, index;
+ int i, index, count;
size_t dsize;
+ MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
+ int order = 0;
if (!s->files) {
dsize = sizeof(uint32_t) + sizeof(FWCfgFile) * FW_CFG_FILE_SLOTS;
@@ -512,13 +757,48 @@ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename,
fw_cfg_add_bytes(s, FW_CFG_FILE_DIR, s->files, dsize);
}
- index = be32_to_cpu(s->files->count);
- assert(index < FW_CFG_FILE_SLOTS);
+ count = be32_to_cpu(s->files->count);
+ assert(count < FW_CFG_FILE_SLOTS);
+
+ /* Find the insertion point. */
+ if (mc->legacy_fw_cfg_order) {
+ /*
+ * Sort by order. For files with the same order, we keep them
+ * in the sequence in which they were added.
+ */
+ order = get_fw_cfg_order(s, filename);
+ for (index = count;
+ index > 0 && order < s->entry_order[index - 1];
+ index--);
+ } else {
+ /* Sort by file name. */
+ for (index = count;
+ index > 0 && strcmp(filename, s->files->f[index - 1].name) < 0;
+ index--);
+ }
- pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name),
- filename);
- for (i = 0; i < index; i++) {
- if (strcmp(s->files->f[index].name, s->files->f[i].name) == 0) {
+ /*
+ * Move all the entries from the index point and after down one
+ * to create a slot for the new entry. Because calculations are
+ * being done with the index, make it so that "i" is the current
+ * index and "i - 1" is the one being copied from, thus the
+ * unusual start and end in the for statement.
+ */
+ for (i = count + 1; i > index; i--) {
+ s->files->f[i] = s->files->f[i - 1];
+ s->files->f[i].select = cpu_to_be16(FW_CFG_FILE_FIRST + i);
+ s->entries[0][FW_CFG_FILE_FIRST + i] =
+ s->entries[0][FW_CFG_FILE_FIRST + i - 1];
+ s->entry_order[i] = s->entry_order[i - 1];
+ }
+
+ memset(&s->files->f[index], 0, sizeof(FWCfgFile));
+ memset(&s->entries[0][FW_CFG_FILE_FIRST + index], 0, sizeof(FWCfgEntry));
+
+ pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name), filename);
+ for (i = 0; i <= count; i++) {
+ if (i != index &&
+ strcmp(s->files->f[index].name, s->files->f[i].name) == 0) {
error_report("duplicate fw_cfg file name: %s",
s->files->f[index].name);
exit(1);
@@ -530,9 +810,10 @@ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename,
s->files->f[index].size = cpu_to_be32(len);
s->files->f[index].select = cpu_to_be16(FW_CFG_FILE_FIRST + index);
+ s->entry_order[index] = order;
trace_fw_cfg_add_file(s, index, s->files->f[index].name, len);
- s->files->count = cpu_to_be32(index+1);
+ s->files->count = cpu_to_be32(count+1);
}
void fw_cfg_add_file(FWCfgState *s, const char *filename,
@@ -595,7 +876,6 @@ static void fw_cfg_init1(DeviceState *dev)
qdev_init_nofail(dev);
fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (char *)"QEMU", 4);
- fw_cfg_add_i32(s, FW_CFG_ID, 1);
fw_cfg_add_bytes(s, FW_CFG_UUID, qemu_uuid, 16);
fw_cfg_add_i16(s, FW_CFG_NOGRAPHIC, (uint16_t)(display_type == DT_NOGRAPHIC));
fw_cfg_add_i16(s, FW_CFG_NB_CPUS, (uint16_t)smp_cpus);
@@ -607,25 +887,57 @@ static void fw_cfg_init1(DeviceState *dev)
qemu_add_machine_init_done_notifier(&s->machine_ready);
}
-FWCfgState *fw_cfg_init_io(uint32_t iobase)
+FWCfgState *fw_cfg_init_io_dma(uint32_t iobase, uint32_t dma_iobase,
+ AddressSpace *dma_as)
{
DeviceState *dev;
+ FWCfgState *s;
+ uint32_t version = FW_CFG_VERSION;
+ bool dma_requested = dma_iobase && dma_as;
dev = qdev_create(NULL, TYPE_FW_CFG_IO);
qdev_prop_set_uint32(dev, "iobase", iobase);
+ qdev_prop_set_uint32(dev, "dma_iobase", dma_iobase);
+ if (!dma_requested) {
+ qdev_prop_set_bit(dev, "dma_enabled", false);
+ }
+
fw_cfg_init1(dev);
+ s = FW_CFG(dev);
+
+ if (s->dma_enabled) {
+ /* 64 bits for the address field */
+ s->dma_as = dma_as;
+ s->dma_addr = 0;
+
+ version |= FW_CFG_VERSION_DMA;
+ }
+
+ fw_cfg_add_i32(s, FW_CFG_ID, version);
+
+ return s;
+}
- return FW_CFG(dev);
+FWCfgState *fw_cfg_init_io(uint32_t iobase)
+{
+ return fw_cfg_init_io_dma(iobase, 0, NULL);
}
-FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, hwaddr data_addr,
- uint32_t data_width)
+FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr,
+ hwaddr data_addr, uint32_t data_width,
+ hwaddr dma_addr, AddressSpace *dma_as)
{
DeviceState *dev;
SysBusDevice *sbd;
+ FWCfgState *s;
+ uint32_t version = FW_CFG_VERSION;
+ bool dma_requested = dma_addr && dma_as;
dev = qdev_create(NULL, TYPE_FW_CFG_MEM);
qdev_prop_set_uint32(dev, "data_width", data_width);
+ if (!dma_requested) {
+ qdev_prop_set_bit(dev, "dma_enabled", false);
+ }
fw_cfg_init1(dev);
@@ -633,13 +945,25 @@ FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, hwaddr data_addr,
sysbus_mmio_map(sbd, 0, ctl_addr);
sysbus_mmio_map(sbd, 1, data_addr);
- return FW_CFG(dev);
+ s = FW_CFG(dev);
+
+ if (s->dma_enabled) {
+ s->dma_as = dma_as;
+ s->dma_addr = 0;
+ sysbus_mmio_map(sbd, 2, dma_addr);
+ version |= FW_CFG_VERSION_DMA;
+ }
+
+ fw_cfg_add_i32(s, FW_CFG_ID, version);
+
+ return s;
}
FWCfgState *fw_cfg_init_mem(hwaddr ctl_addr, hwaddr data_addr)
{
return fw_cfg_init_mem_wide(ctl_addr, data_addr,
- fw_cfg_data_mem_ops.valid.max_access_size);
+ fw_cfg_data_mem_ops.valid.max_access_size,
+ 0, NULL);
}
@@ -666,6 +990,9 @@ static const TypeInfo fw_cfg_info = {
static Property fw_cfg_io_properties[] = {
DEFINE_PROP_UINT32("iobase", FWCfgIoState, iobase, -1),
+ DEFINE_PROP_UINT32("dma_iobase", FWCfgIoState, dma_iobase, -1),
+ DEFINE_PROP_BOOL("dma_enabled", FWCfgIoState, parent_obj.dma_enabled,
+ true),
DEFINE_PROP_END_OF_LIST(),
};
@@ -674,9 +1001,19 @@ static void fw_cfg_io_realize(DeviceState *dev, Error **errp)
FWCfgIoState *s = FW_CFG_IO(dev);
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ /* when using port i/o, the 8-bit data register ALWAYS overlaps
+ * with half of the 16-bit control register. Hence, the total size
+ * of the i/o region used is FW_CFG_CTL_SIZE */
memory_region_init_io(&s->comb_iomem, OBJECT(s), &fw_cfg_comb_mem_ops,
- FW_CFG(s), "fwcfg", FW_CFG_SIZE);
+ FW_CFG(s), "fwcfg", FW_CFG_CTL_SIZE);
sysbus_add_io(sbd, s->iobase, &s->comb_iomem);
+
+ if (FW_CFG(s)->dma_enabled) {
+ memory_region_init_io(&FW_CFG(s)->dma_iomem, OBJECT(s),
+ &fw_cfg_dma_mem_ops, FW_CFG(s), "fwcfg.dma",
+ sizeof(dma_addr_t));
+ sysbus_add_io(sbd, s->dma_iobase, &FW_CFG(s)->dma_iomem);
+ }
}
static void fw_cfg_io_class_init(ObjectClass *klass, void *data)
@@ -697,6 +1034,8 @@ static const TypeInfo fw_cfg_io_info = {
static Property fw_cfg_mem_properties[] = {
DEFINE_PROP_UINT32("data_width", FWCfgMemState, data_width, -1),
+ DEFINE_PROP_BOOL("dma_enabled", FWCfgMemState, parent_obj.dma_enabled,
+ true),
DEFINE_PROP_END_OF_LIST(),
};
@@ -707,7 +1046,7 @@ static void fw_cfg_mem_realize(DeviceState *dev, Error **errp)
const MemoryRegionOps *data_ops = &fw_cfg_data_mem_ops;
memory_region_init_io(&s->ctl_iomem, OBJECT(s), &fw_cfg_ctl_mem_ops,
- FW_CFG(s), "fwcfg.ctl", FW_CFG_SIZE);
+ FW_CFG(s), "fwcfg.ctl", FW_CFG_CTL_SIZE);
sysbus_init_mmio(sbd, &s->ctl_iomem);
if (s->data_width > data_ops->valid.max_access_size) {
@@ -725,6 +1064,13 @@ static void fw_cfg_mem_realize(DeviceState *dev, Error **errp)
memory_region_init_io(&s->data_iomem, OBJECT(s), data_ops, FW_CFG(s),
"fwcfg.data", data_ops->valid.max_access_size);
sysbus_init_mmio(sbd, &s->data_iomem);
+
+ if (FW_CFG(s)->dma_enabled) {
+ memory_region_init_io(&FW_CFG(s)->dma_iomem, OBJECT(s),
+ &fw_cfg_dma_mem_ops, FW_CFG(s), "fwcfg.dma",
+ sizeof(dma_addr_t));
+ sysbus_init_mmio(sbd, &FW_CFG(s)->dma_iomem);
+ }
}
static void fw_cfg_mem_class_init(ObjectClass *klass, void *data)
diff --git a/qemu/hw/nvram/mac_nvram.c b/qemu/hw/nvram/mac_nvram.c
index d35f8a312..24f61212b 100644
--- a/qemu/hw/nvram/mac_nvram.c
+++ b/qemu/hw/nvram/mac_nvram.c
@@ -22,10 +22,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/nvram/openbios_firmware_abi.h"
#include "sysemu/sysemu.h"
#include "hw/ppc/mac.h"
+#include "qemu/cutils.h"
#include <zlib.h>
/* debug NVR */
@@ -48,7 +50,8 @@ static void macio_nvram_writeb(void *opaque, hwaddr addr,
addr = (addr >> s->it_shift) & (s->size - 1);
s->data[addr] = value;
- NVR_DPRINTF("writeb addr %04" PHYS_PRIx " val %" PRIx64 "\n", addr, value);
+ NVR_DPRINTF("writeb addr %04" HWADDR_PRIx " val %" PRIx64 "\n",
+ addr, value);
}
static uint64_t macio_nvram_readb(void *opaque, hwaddr addr,
@@ -59,7 +62,8 @@ static uint64_t macio_nvram_readb(void *opaque, hwaddr addr,
addr = (addr >> s->it_shift) & (s->size - 1);
value = s->data[addr];
- NVR_DPRINTF("readb addr %04x val %x\n", (int)addr, value);
+ NVR_DPRINTF("readb addr %04" HWADDR_PRIx " val %" PRIx32 "\n",
+ addr, value);
return value;
}
@@ -123,6 +127,7 @@ static void macio_nvram_class_init(ObjectClass *oc, void *data)
dc->reset = macio_nvram_reset;
dc->vmsd = &vmstate_macio_nvram;
dc->props = macio_nvram_properties;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
}
static const TypeInfo macio_nvram_type_info = {
diff --git a/qemu/hw/nvram/spapr_nvram.c b/qemu/hw/nvram/spapr_nvram.c
index fcaa77dd9..802636ef3 100644
--- a/qemu/hw/nvram/spapr_nvram.c
+++ b/qemu/hw/nvram/spapr_nvram.c
@@ -22,6 +22,10 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include <libfdt.h>
#include "sysemu/block-backend.h"
diff --git a/qemu/hw/openrisc/cputimer.c b/qemu/hw/openrisc/cputimer.c
index 9c5494510..a98c799de 100644
--- a/qemu/hw/openrisc/cputimer.c
+++ b/qemu/hw/openrisc/cputimer.c
@@ -18,11 +18,12 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "cpu.h"
#include "hw/hw.h"
#include "qemu/timer.h"
-#define TIMER_FREQ (20 * 1000 * 1000) /* 20MHz */
+#define TIMER_PERIOD 50 /* 50 ns period for 20 MHz timer */
/* The time when TTCR changes */
static uint64_t last_clk;
@@ -36,8 +37,7 @@ void cpu_openrisc_count_update(OpenRISCCPU *cpu)
return;
}
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- cpu->env.ttcr += (uint32_t)muldiv64(now - last_clk, TIMER_FREQ,
- get_ticks_per_sec());
+ cpu->env.ttcr += (uint32_t)((now - last_clk) / TIMER_PERIOD);
last_clk = now;
}
@@ -59,7 +59,7 @@ void cpu_openrisc_timer_update(OpenRISCCPU *cpu)
} else {
wait = (cpu->env.ttmr & TTMR_TP) - (cpu->env.ttcr & TTMR_TP);
}
- next = now + muldiv64(wait, get_ticks_per_sec(), TIMER_FREQ);
+ next = now + (uint64_t)wait * TIMER_PERIOD;
timer_mod(cpu->env.timer, next);
}
diff --git a/qemu/hw/openrisc/openrisc_sim.c b/qemu/hw/openrisc/openrisc_sim.c
index 1da0657dd..6d06d5be0 100644
--- a/qemu/hw/openrisc/openrisc_sim.c
+++ b/qemu/hw/openrisc/openrisc_sim.c
@@ -18,6 +18,10 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "hw/boards.h"
#include "elf.h"
@@ -68,7 +72,8 @@ static void cpu_openrisc_load_kernel(ram_addr_t ram_size,
if (kernel_filename && !qtest_enabled()) {
kernel_size = load_elf(kernel_filename, NULL, NULL,
- &elf_entry, NULL, NULL, 1, ELF_MACHINE, 1);
+ &elf_entry, NULL, NULL, 1, EM_OPENRISC,
+ 1, 0);
entry = elf_entry;
if (kernel_size < 0) {
kernel_size = load_uimage(kernel_filename,
@@ -114,7 +119,7 @@ static void openrisc_sim_init(MachineState *machine)
}
ram = g_malloc(sizeof(*ram));
- memory_region_init_ram(ram, NULL, "openrisc.ram", ram_size, &error_abort);
+ memory_region_init_ram(ram, NULL, "openrisc.ram", ram_size, &error_fatal);
vmstate_register_ram_global(ram);
memory_region_add_subregion(get_system_memory(), 0, ram);
@@ -132,17 +137,12 @@ static void openrisc_sim_init(MachineState *machine)
cpu_openrisc_load_kernel(ram_size, kernel_filename, cpu);
}
-static QEMUMachine openrisc_sim_machine = {
- .name = "or32-sim",
- .desc = "or32 simulation",
- .init = openrisc_sim_init,
- .max_cpus = 1,
- .is_default = 1,
-};
-
-static void openrisc_sim_machine_init(void)
+static void openrisc_sim_machine_init(MachineClass *mc)
{
- qemu_register_machine(&openrisc_sim_machine);
+ mc->desc = "or32 simulation";
+ mc->init = openrisc_sim_init;
+ mc->max_cpus = 1;
+ mc->is_default = 1;
}
-machine_init(openrisc_sim_machine_init);
+DEFINE_MACHINE("or32-sim", openrisc_sim_machine_init)
diff --git a/qemu/hw/openrisc/pic_cpu.c b/qemu/hw/openrisc/pic_cpu.c
index 2af1d6013..569b443f5 100644
--- a/qemu/hw/openrisc/pic_cpu.c
+++ b/qemu/hw/openrisc/pic_cpu.c
@@ -18,6 +18,7 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "cpu.h"
diff --git a/qemu/hw/pci-bridge/dec.c b/qemu/hw/pci-bridge/dec.c
index 28d0ff9c8..840c96198 100644
--- a/qemu/hw/pci-bridge/dec.c
+++ b/qemu/hw/pci-bridge/dec.c
@@ -23,6 +23,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "dec.h"
#include "hw/sysbus.h"
#include "hw/pci/pci.h"
@@ -51,9 +52,9 @@ static int dec_map_irq(PCIDevice *pci_dev, int irq_num)
return irq_num;
}
-static int dec_pci_bridge_initfn(PCIDevice *pci_dev)
+static void dec_pci_bridge_realize(PCIDevice *pci_dev, Error **errp)
{
- return pci_bridge_initfn(pci_dev, TYPE_PCI_BUS);
+ pci_bridge_initfn(pci_dev, TYPE_PCI_BUS);
}
static void dec_21154_pci_bridge_class_init(ObjectClass *klass, void *data)
@@ -61,7 +62,7 @@ static void dec_21154_pci_bridge_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
- k->init = dec_pci_bridge_initfn;
+ k->realize = dec_pci_bridge_realize;
k->exit = pci_bridge_exitfn;
k->vendor_id = PCI_VENDOR_ID_DEC;
k->device_id = PCI_DEVICE_ID_DEC_21154;
diff --git a/qemu/hw/pci-bridge/i82801b11.c b/qemu/hw/pci-bridge/i82801b11.c
index 7e79bc01e..2404e7eba 100644
--- a/qemu/hw/pci-bridge/i82801b11.c
+++ b/qemu/hw/pci-bridge/i82801b11.c
@@ -41,6 +41,7 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>
*/
+#include "qemu/osdep.h"
#include "hw/pci/pci.h"
#include "hw/i386/ich9.h"
@@ -61,10 +62,7 @@ static int i82801b11_bridge_initfn(PCIDevice *d)
{
int rc;
- rc = pci_bridge_initfn(d, TYPE_PCI_BUS);
- if (rc < 0) {
- return rc;
- }
+ pci_bridge_initfn(d, TYPE_PCI_BUS);
rc = pci_bridge_ssvid_init(d, I82801ba_SSVID_OFFSET,
I82801ba_SSVID_SVID, I82801ba_SSVID_SSID);
@@ -80,6 +78,14 @@ err_bridge:
return rc;
}
+static const VMStateDescription i82801b11_bridge_dev_vmstate = {
+ .name = "i82801b11_bridge",
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(parent_obj, PCIBridge),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static void i82801b11_bridge_class_init(ObjectClass *klass, void *data)
{
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
@@ -91,6 +97,7 @@ static void i82801b11_bridge_class_init(ObjectClass *klass, void *data)
k->revision = ICH9_D2P_A2_REVISION;
k->init = i82801b11_bridge_initfn;
k->config_write = pci_bridge_write_config;
+ dc->vmsd = &i82801b11_bridge_dev_vmstate;
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
}
diff --git a/qemu/hw/pci-bridge/ioh3420.c b/qemu/hw/pci-bridge/ioh3420.c
index cce2fdd8e..0937fa34b 100644
--- a/qemu/hw/pci-bridge/ioh3420.c
+++ b/qemu/hw/pci-bridge/ioh3420.c
@@ -20,6 +20,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/pci/pci_ids.h"
#include "hw/pci/msi.h"
#include "hw/pci/pcie.h"
@@ -97,11 +98,7 @@ static int ioh3420_initfn(PCIDevice *d)
PCIESlot *s = PCIE_SLOT(d);
int rc;
- rc = pci_bridge_initfn(d, TYPE_PCIE_BUS);
- if (rc < 0) {
- return rc;
- }
-
+ pci_bridge_initfn(d, TYPE_PCIE_BUS);
pcie_port_init_reg(d);
rc = pci_bridge_ssvid_init(d, IOH_EP_SSVID_OFFSET,
@@ -129,7 +126,7 @@ static int ioh3420_initfn(PCIDevice *d)
goto err_pcie_cap;
}
pcie_cap_root_init(d);
- rc = pcie_aer_init(d, IOH_EP_AER_OFFSET);
+ rc = pcie_aer_init(d, IOH_EP_AER_OFFSET, PCI_ERR_SIZEOF);
if (rc < 0) {
goto err;
}
diff --git a/qemu/hw/pci-bridge/pci_bridge_dev.c b/qemu/hw/pci-bridge/pci_bridge_dev.c
index 26aded9f0..7b582e96a 100644
--- a/qemu/hw/pci-bridge/pci_bridge_dev.c
+++ b/qemu/hw/pci-bridge/pci_bridge_dev.c
@@ -19,6 +19,8 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/pci/pci_bridge.h"
#include "hw/pci/pci_ids.h"
#include "hw/pci/msi.h"
@@ -52,10 +54,8 @@ static int pci_bridge_dev_initfn(PCIDevice *dev)
PCIBridgeDev *bridge_dev = PCI_BRIDGE_DEV(dev);
int err;
- err = pci_bridge_initfn(dev, TYPE_PCI_BUS);
- if (err) {
- goto bridge_error;
- }
+ pci_bridge_initfn(dev, TYPE_PCI_BUS);
+
if (bridge_dev->flags & (1 << PCI_BRIDGE_DEV_F_SHPC_REQ)) {
dev->config[PCI_INTERRUPT_PIN] = 0x1;
memory_region_init(&bridge_dev->bar, OBJECT(dev), "shpc-bar",
@@ -73,7 +73,7 @@ static int pci_bridge_dev_initfn(PCIDevice *dev)
goto slotid_error;
}
if ((bridge_dev->flags & (1 << PCI_BRIDGE_DEV_F_MSI_REQ)) &&
- msi_supported) {
+ msi_nonbroken) {
err = msi_init(dev, 0, 1, true, true);
if (err < 0) {
goto msi_error;
@@ -94,7 +94,7 @@ slotid_error:
}
shpc_error:
pci_bridge_exitfn(dev);
-bridge_error:
+
return err;
}
diff --git a/qemu/hw/pci-bridge/pci_expander_bridge.c b/qemu/hw/pci-bridge/pci_expander_bridge.c
index 57f8a3762..ba320bd85 100644
--- a/qemu/hw/pci-bridge/pci_expander_bridge.c
+++ b/qemu/hw/pci-bridge/pci_expander_bridge.c
@@ -10,6 +10,7 @@
* See the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_bus.h"
#include "hw/pci/pci_host.h"
@@ -23,6 +24,9 @@
#define TYPE_PXB_BUS "pxb-bus"
#define PXB_BUS(obj) OBJECT_CHECK(PXBBus, (obj), TYPE_PXB_BUS)
+#define TYPE_PXB_PCIE_BUS "pxb-pcie-bus"
+#define PXB_PCIE_BUS(obj) OBJECT_CHECK(PXBBus, (obj), TYPE_PXB_PCIE_BUS)
+
typedef struct PXBBus {
/*< private >*/
PCIBus parent_obj;
@@ -34,6 +38,9 @@ typedef struct PXBBus {
#define TYPE_PXB_DEVICE "pxb"
#define PXB_DEV(obj) OBJECT_CHECK(PXBDev, (obj), TYPE_PXB_DEVICE)
+#define TYPE_PXB_PCIE_DEVICE "pxb-pcie"
+#define PXB_PCIE_DEV(obj) OBJECT_CHECK(PXBDev, (obj), TYPE_PXB_PCIE_DEVICE)
+
typedef struct PXBDev {
/*< private >*/
PCIDevice parent_obj;
@@ -43,13 +50,18 @@ typedef struct PXBDev {
uint16_t numa_node;
} PXBDev;
+static PXBDev *convert_to_pxb(PCIDevice *dev)
+{
+ return pci_bus_is_express(dev->bus) ? PXB_PCIE_DEV(dev) : PXB_DEV(dev);
+}
+
static GList *pxb_dev_list;
#define TYPE_PXB_HOST "pxb-host"
static int pxb_bus_num(PCIBus *bus)
{
- PXBDev *pxb = PXB_DEV(bus->parent_dev);
+ PXBDev *pxb = convert_to_pxb(bus->parent_dev);
return pxb->bus_nr;
}
@@ -61,7 +73,7 @@ static bool pxb_is_root(PCIBus *bus)
static uint16_t pxb_bus_numa_node(PCIBus *bus)
{
- PXBDev *pxb = PXB_DEV(bus->parent_dev);
+ PXBDev *pxb = convert_to_pxb(bus->parent_dev);
return pxb->numa_node;
}
@@ -82,10 +94,18 @@ static const TypeInfo pxb_bus_info = {
.class_init = pxb_bus_class_init,
};
+static const TypeInfo pxb_pcie_bus_info = {
+ .name = TYPE_PXB_PCIE_BUS,
+ .parent = TYPE_PCIE_BUS,
+ .instance_size = sizeof(PXBBus),
+ .class_init = pxb_bus_class_init,
+};
+
static const char *pxb_host_root_bus_path(PCIHostState *host_bridge,
PCIBus *rootbus)
{
- PXBBus *bus = PXB_BUS(rootbus);
+ PXBBus *bus = pci_bus_is_express(rootbus) ?
+ PXB_PCIE_BUS(rootbus) : PXB_BUS(rootbus);
snprintf(bus->bus_path, 8, "0000:%02x", pxb_bus_num(rootbus));
return bus->bus_path;
@@ -103,7 +123,7 @@ static char *pxb_host_ofw_unit_address(const SysBusDevice *dev)
pxb_host = PCI_HOST_BRIDGE(dev);
pxb_bus = pxb_host->bus;
- pxb_dev = PXB_DEV(pxb_bus->parent_dev);
+ pxb_dev = convert_to_pxb(pxb_bus->parent_dev);
position = g_list_index(pxb_dev_list, pxb_dev);
assert(position >= 0);
@@ -193,10 +213,10 @@ static gint pxb_compare(gconstpointer a, gconstpointer b)
0;
}
-static int pxb_dev_initfn(PCIDevice *dev)
+static int pxb_dev_init_common(PCIDevice *dev, bool pcie)
{
- PXBDev *pxb = PXB_DEV(dev);
- DeviceState *ds, *bds;
+ PXBDev *pxb = convert_to_pxb(dev);
+ DeviceState *ds, *bds = NULL;
PCIBus *bus;
const char *dev_name = NULL;
@@ -211,26 +231,31 @@ static int pxb_dev_initfn(PCIDevice *dev)
}
ds = qdev_create(NULL, TYPE_PXB_HOST);
- bus = pci_bus_new(ds, "pxb-internal", NULL, NULL, 0, TYPE_PXB_BUS);
+ if (pcie) {
+ bus = pci_bus_new(ds, dev_name, NULL, NULL, 0, TYPE_PXB_PCIE_BUS);
+ } else {
+ bus = pci_bus_new(ds, "pxb-internal", NULL, NULL, 0, TYPE_PXB_BUS);
+ bds = qdev_create(BUS(bus), "pci-bridge");
+ bds->id = dev_name;
+ qdev_prop_set_uint8(bds, PCI_BRIDGE_DEV_PROP_CHASSIS_NR, pxb->bus_nr);
+ qdev_prop_set_bit(bds, PCI_BRIDGE_DEV_PROP_SHPC, false);
+ }
bus->parent_dev = dev;
bus->address_space_mem = dev->bus->address_space_mem;
bus->address_space_io = dev->bus->address_space_io;
bus->map_irq = pxb_map_irq_fn;
- bds = qdev_create(BUS(bus), "pci-bridge");
- bds->id = dev_name;
- qdev_prop_set_uint8(bds, PCI_BRIDGE_DEV_PROP_CHASSIS_NR, pxb->bus_nr);
- qdev_prop_set_bit(bds, PCI_BRIDGE_DEV_PROP_SHPC, false);
-
PCI_HOST_BRIDGE(ds)->bus = bus;
if (pxb_register_bus(dev, bus)) {
- return -EINVAL;
+ goto err_register_bus;
}
qdev_init_nofail(ds);
- qdev_init_nofail(bds);
+ if (bds) {
+ qdev_init_nofail(bds);
+ }
pci_word_test_and_set_mask(dev->config + PCI_STATUS,
PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK);
@@ -238,17 +263,33 @@ static int pxb_dev_initfn(PCIDevice *dev)
pxb_dev_list = g_list_insert_sorted(pxb_dev_list, pxb, pxb_compare);
return 0;
+
+err_register_bus:
+ object_unref(OBJECT(bds));
+ object_unparent(OBJECT(bus));
+ object_unref(OBJECT(ds));
+ return -EINVAL;
+}
+
+static int pxb_dev_initfn(PCIDevice *dev)
+{
+ if (pci_bus_is_express(dev->bus)) {
+ error_report("pxb devices cannot reside on a PCIe bus!");
+ return -EINVAL;
+ }
+
+ return pxb_dev_init_common(dev, false);
}
static void pxb_dev_exitfn(PCIDevice *pci_dev)
{
- PXBDev *pxb = PXB_DEV(pci_dev);
+ PXBDev *pxb = convert_to_pxb(pci_dev);
pxb_dev_list = g_list_remove(pxb_dev_list, pxb);
}
static Property pxb_dev_properties[] = {
- /* Note: 0 is not a legal a PXB bus number. */
+ /* Note: 0 is not a legal PXB bus number. */
DEFINE_PROP_UINT8("bus_nr", PXBDev, bus_nr, 0),
DEFINE_PROP_UINT16("numa_node", PXBDev, numa_node, NUMA_NODE_UNASSIGNED),
DEFINE_PROP_END_OF_LIST(),
@@ -267,6 +308,7 @@ static void pxb_dev_class_init(ObjectClass *klass, void *data)
dc->desc = "PCI Expander Bridge";
dc->props = pxb_dev_properties;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
}
static const TypeInfo pxb_dev_info = {
@@ -276,11 +318,46 @@ static const TypeInfo pxb_dev_info = {
.class_init = pxb_dev_class_init,
};
+static int pxb_pcie_dev_initfn(PCIDevice *dev)
+{
+ if (!pci_bus_is_express(dev->bus)) {
+ error_report("pxb-pcie devices cannot reside on a PCI bus!");
+ return -EINVAL;
+ }
+
+ return pxb_dev_init_common(dev, true);
+}
+
+static void pxb_pcie_dev_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->init = pxb_pcie_dev_initfn;
+ k->exit = pxb_dev_exitfn;
+ k->vendor_id = PCI_VENDOR_ID_REDHAT;
+ k->device_id = PCI_DEVICE_ID_REDHAT_PXB_PCIE;
+ k->class_id = PCI_CLASS_BRIDGE_HOST;
+
+ dc->desc = "PCI Express Expander Bridge";
+ dc->props = pxb_dev_properties;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+}
+
+static const TypeInfo pxb_pcie_dev_info = {
+ .name = TYPE_PXB_PCIE_DEVICE,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(PXBDev),
+ .class_init = pxb_pcie_dev_class_init,
+};
+
static void pxb_register_types(void)
{
type_register_static(&pxb_bus_info);
+ type_register_static(&pxb_pcie_bus_info);
type_register_static(&pxb_host_info);
type_register_static(&pxb_dev_info);
+ type_register_static(&pxb_pcie_dev_info);
}
type_init(pxb_register_types)
diff --git a/qemu/hw/pci-bridge/xio3130_downstream.c b/qemu/hw/pci-bridge/xio3130_downstream.c
index b3a647926..cf1ee63ab 100644
--- a/qemu/hw/pci-bridge/xio3130_downstream.c
+++ b/qemu/hw/pci-bridge/xio3130_downstream.c
@@ -19,6 +19,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/pci/pci_ids.h"
#include "hw/pci/msi.h"
#include "hw/pci/pcie.h"
@@ -60,11 +61,7 @@ static int xio3130_downstream_initfn(PCIDevice *d)
PCIESlot *s = PCIE_SLOT(d);
int rc;
- rc = pci_bridge_initfn(d, TYPE_PCIE_BUS);
- if (rc < 0) {
- return rc;
- }
-
+ pci_bridge_initfn(d, TYPE_PCIE_BUS);
pcie_port_init_reg(d);
rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
@@ -92,7 +89,7 @@ static int xio3130_downstream_initfn(PCIDevice *d)
goto err_pcie_cap;
}
pcie_cap_arifwd_init(d);
- rc = pcie_aer_init(d, XIO3130_AER_OFFSET);
+ rc = pcie_aer_init(d, XIO3130_AER_OFFSET, PCI_ERR_SIZEOF);
if (rc < 0) {
goto err;
}
diff --git a/qemu/hw/pci-bridge/xio3130_upstream.c b/qemu/hw/pci-bridge/xio3130_upstream.c
index eada5828a..164ef58c4 100644
--- a/qemu/hw/pci-bridge/xio3130_upstream.c
+++ b/qemu/hw/pci-bridge/xio3130_upstream.c
@@ -19,6 +19,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/pci/pci_ids.h"
#include "hw/pci/msi.h"
#include "hw/pci/pcie.h"
@@ -56,11 +57,7 @@ static int xio3130_upstream_initfn(PCIDevice *d)
PCIEPort *p = PCIE_PORT(d);
int rc;
- rc = pci_bridge_initfn(d, TYPE_PCIE_BUS);
- if (rc < 0) {
- return rc;
- }
-
+ pci_bridge_initfn(d, TYPE_PCIE_BUS);
pcie_port_init_reg(d);
rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
@@ -81,7 +78,7 @@ static int xio3130_upstream_initfn(PCIDevice *d)
}
pcie_cap_flr_init(d);
pcie_cap_deverr_init(d);
- rc = pcie_aer_init(d, XIO3130_AER_OFFSET);
+ rc = pcie_aer_init(d, XIO3130_AER_OFFSET, PCI_ERR_SIZEOF);
if (rc < 0) {
goto err;
}
diff --git a/qemu/hw/pci-host/apb.c b/qemu/hw/pci-host/apb.c
index 599768e2d..aaef7bb3a 100644
--- a/qemu/hw/pci-host/apb.c
+++ b/qemu/hw/pci-host/apb.c
@@ -27,6 +27,7 @@
Ultrasparc PCI host is called the PCI Bus Module (PBM). The APB is
the secondary PCI bridge. */
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_host.h"
@@ -634,12 +635,7 @@ static void pci_apb_set_irq(void *opaque, int irq_num, int level)
static int apb_pci_bridge_initfn(PCIDevice *dev)
{
- int rc;
-
- rc = pci_bridge_initfn(dev, TYPE_PCI_BUS);
- if (rc < 0) {
- return rc;
- }
+ pci_bridge_initfn(dev, TYPE_PCI_BUS);
/*
* command register:
diff --git a/qemu/hw/pci-host/bonito.c b/qemu/hw/pci-host/bonito.c
index 3a731fe18..1999ece59 100644
--- a/qemu/hw/pci-host/bonito.c
+++ b/qemu/hw/pci-host/bonito.c
@@ -37,7 +37,7 @@
* north bridge address to pci address.
*/
-#include <assert.h>
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
@@ -180,8 +180,6 @@
#define PCI_ADDR(busno,devno,funno,regno) \
((((busno)<<16)&0xff0000) + (((devno)<<11)&0xf800) + (((funno)<<8)&0x700) + (regno))
-#define TYPE_BONITO_PCI_HOST_BRIDGE "Bonito-pcihost"
-
typedef struct BonitoState BonitoState;
typedef struct PCIBonitoState
@@ -215,17 +213,20 @@ typedef struct PCIBonitoState
} PCIBonitoState;
-#define BONITO_PCI_HOST_BRIDGE(obj) \
- OBJECT_CHECK(BonitoState, (obj), TYPE_BONITO_PCI_HOST_BRIDGE)
-
struct BonitoState {
PCIHostState parent_obj;
-
qemu_irq *pic;
-
PCIBonitoState *pci_dev;
};
+#define TYPE_BONITO_PCI_HOST_BRIDGE "Bonito-pcihost"
+#define BONITO_PCI_HOST_BRIDGE(obj) \
+ OBJECT_CHECK(BonitoState, (obj), TYPE_BONITO_PCI_HOST_BRIDGE)
+
+#define TYPE_PCI_BONITO "Bonito"
+#define PCI_BONITO(obj) \
+ OBJECT_CHECK(PCIBonitoState, (obj), TYPE_PCI_BONITO)
+
static void bonito_writel(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
@@ -355,6 +356,10 @@ static uint64_t bonito_ldma_readl(void *opaque, hwaddr addr,
uint32_t val;
PCIBonitoState *s = opaque;
+ if (addr >= sizeof(s->bonldma)) {
+ return 0;
+ }
+
val = ((uint32_t *)(&s->bonldma))[addr/sizeof(uint32_t)];
return val;
@@ -365,6 +370,10 @@ static void bonito_ldma_writel(void *opaque, hwaddr addr,
{
PCIBonitoState *s = opaque;
+ if (addr >= sizeof(s->bonldma)) {
+ return;
+ }
+
((uint32_t *)(&s->bonldma))[addr/sizeof(uint32_t)] = val & 0xffffffff;
}
@@ -384,6 +393,10 @@ static uint64_t bonito_cop_readl(void *opaque, hwaddr addr,
uint32_t val;
PCIBonitoState *s = opaque;
+ if (addr >= sizeof(s->boncop)) {
+ return 0;
+ }
+
val = ((uint32_t *)(&s->boncop))[addr/sizeof(uint32_t)];
return val;
@@ -394,6 +407,10 @@ static void bonito_cop_writel(void *opaque, hwaddr addr,
{
PCIBonitoState *s = opaque;
+ if (addr >= sizeof(s->boncop)) {
+ return;
+ }
+
((uint32_t *)(&s->boncop))[addr/sizeof(uint32_t)] = val & 0xffffffff;
}
@@ -707,7 +724,7 @@ static int bonito_pcihost_initfn(SysBusDevice *dev)
static void bonito_realize(PCIDevice *dev, Error **errp)
{
- PCIBonitoState *s = DO_UPCAST(PCIBonitoState, dev, dev);
+ PCIBonitoState *s = PCI_BONITO(dev);
SysBusDevice *sysbus = SYS_BUS_DEVICE(s->pcihost);
PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost);
@@ -783,8 +800,8 @@ PCIBus *bonito_init(qemu_irq *pic)
qdev_init_nofail(dev);
/* set the pcihost pointer before bonito_initfn is called */
- d = pci_create(phb->bus, PCI_DEVFN(0, 0), "Bonito");
- s = DO_UPCAST(PCIBonitoState, dev, d);
+ d = pci_create(phb->bus, PCI_DEVFN(0, 0), TYPE_PCI_BONITO);
+ s = PCI_BONITO(d);
s->pcihost = pcihost;
pcihost->pci_dev = s;
qdev_init_nofail(DEVICE(d));
@@ -812,7 +829,7 @@ static void bonito_class_init(ObjectClass *klass, void *data)
}
static const TypeInfo bonito_info = {
- .name = "Bonito",
+ .name = TYPE_PCI_BONITO,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(PCIBonitoState),
.class_init = bonito_class_init,
diff --git a/qemu/hw/pci-host/gpex.c b/qemu/hw/pci-host/gpex.c
index 9d8fb5a49..66055ee5c 100644
--- a/qemu/hw/pci-host/gpex.c
+++ b/qemu/hw/pci-host/gpex.c
@@ -28,6 +28,7 @@
* http://www.kernel.org/doc/Documentation/devicetree/bindings/pci/host-generic-pci.txt
* http://www.firmware.org/1275/practice/imap/imap0_9d.pdf
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/pci-host/gpex.h"
diff --git a/qemu/hw/pci-host/grackle.c b/qemu/hw/pci-host/grackle.c
index bfe707a1a..8f9121615 100644
--- a/qemu/hw/pci-host/grackle.c
+++ b/qemu/hw/pci-host/grackle.c
@@ -23,6 +23,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/pci/pci_host.h"
#include "hw/ppc/mac.h"
#include "hw/pci/pci.h"
@@ -146,8 +147,10 @@ static const TypeInfo grackle_pci_info = {
static void pci_grackle_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
k->init = pci_grackle_init_device;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
}
static const TypeInfo grackle_pci_host_info = {
diff --git a/qemu/hw/pci-host/pam.c b/qemu/hw/pci-host/pam.c
index 17d826cba..e361ecb7e 100644
--- a/qemu/hw/pci-host/pam.c
+++ b/qemu/hw/pci-host/pam.c
@@ -27,6 +27,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "qom/object.h"
#include "sysemu/sysemu.h"
#include "hw/pci-host/pam.h"
diff --git a/qemu/hw/pci-host/piix.c b/qemu/hw/pci-host/piix.c
index ad55f9966..df2b0e26f 100644
--- a/qemu/hw/pci-host/piix.c
+++ b/qemu/hw/pci-host/piix.c
@@ -22,25 +22,27 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_host.h"
#include "hw/isa/isa.h"
#include "hw/sysbus.h"
+#include "qapi/error.h"
#include "qemu/range.h"
#include "hw/xen/xen.h"
#include "hw/pci-host/pam.h"
#include "sysemu/sysemu.h"
#include "hw/i386/ioapic.h"
#include "qapi/visitor.h"
+#include "qemu/error-report.h"
/*
* I440FX chipset data sheet.
* http://download.intel.com/design/chipsets/datashts/29054901.pdf
*/
-#define TYPE_I440FX_PCI_HOST_BRIDGE "i440FX-pcihost"
#define I440FX_PCI_HOST_BRIDGE(obj) \
OBJECT_CHECK(I440FXState, (obj), TYPE_I440FX_PCI_HOST_BRIDGE)
@@ -95,7 +97,6 @@ typedef struct PIIX3State {
#define PIIX3_PCI_DEVICE(obj) \
OBJECT_CHECK(PIIX3State, (obj), TYPE_PIIX3_PCI_DEVICE)
-#define TYPE_I440FX_PCI_DEVICE "i440FX"
#define I440FX_PCI_DEVICE(obj) \
OBJECT_CHECK(PCII440FXState, (obj), TYPE_I440FX_PCI_DEVICE)
@@ -117,6 +118,11 @@ struct PCII440FXState {
#define I440FX_PAM_SIZE 7
#define I440FX_SMRAM 0x72
+/* Older coreboot versions (4.0 and older) read a config register that doesn't
+ * exist in real hardware, to get the RAM size from QEMU.
+ */
+#define I440FX_COREBOOT_RAM_SIZE 0x57
+
static void piix3_set_irq(void *opaque, int pirq, int level);
static PCIINTxRoute piix3_route_intx_pin_to_irq(void *opaque, int pci_intx);
static void piix3_write_config_xen(PCIDevice *dev,
@@ -211,39 +217,39 @@ static const VMStateDescription vmstate_i440fx = {
};
static void i440fx_pcihost_get_pci_hole_start(Object *obj, Visitor *v,
- void *opaque, const char *name,
+ const char *name, void *opaque,
Error **errp)
{
I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj);
uint32_t value = s->pci_info.w32.begin;
- visit_type_uint32(v, &value, name, errp);
+ visit_type_uint32(v, name, &value, errp);
}
static void i440fx_pcihost_get_pci_hole_end(Object *obj, Visitor *v,
- void *opaque, const char *name,
+ const char *name, void *opaque,
Error **errp)
{
I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj);
uint32_t value = s->pci_info.w32.end;
- visit_type_uint32(v, &value, name, errp);
+ visit_type_uint32(v, name, &value, errp);
}
static void i440fx_pcihost_get_pci_hole64_start(Object *obj, Visitor *v,
- void *opaque, const char *name,
- Error **errp)
+ const char *name,
+ void *opaque, Error **errp)
{
PCIHostState *h = PCI_HOST_BRIDGE(obj);
Range w64;
pci_bus_get_w64_range(h->bus, &w64);
- visit_type_uint64(v, &w64.begin, name, errp);
+ visit_type_uint64(v, name, &w64.begin, errp);
}
static void i440fx_pcihost_get_pci_hole64_end(Object *obj, Visitor *v,
- void *opaque, const char *name,
+ const char *name, void *opaque,
Error **errp)
{
PCIHostState *h = PCI_HOST_BRIDGE(obj);
@@ -251,7 +257,7 @@ static void i440fx_pcihost_get_pci_hole64_end(Object *obj, Visitor *v,
pci_bus_get_w64_range(h->bus, &w64);
- visit_type_uint64(v, &w64.end, name, errp);
+ visit_type_uint64(v, name, &w64.end, errp);
}
static void i440fx_pcihost_initfn(Object *obj)
@@ -298,9 +304,14 @@ static void i440fx_pcihost_realize(DeviceState *dev, Error **errp)
static void i440fx_realize(PCIDevice *dev, Error **errp)
{
dev->config[I440FX_SMRAM] = 0x02;
+
+ if (object_property_get_bool(qdev_get_machine(), "iommu", NULL)) {
+ error_report("warning: i440fx doesn't support emulated iommu");
+ }
}
-PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
+PCIBus *i440fx_init(const char *host_type, const char *pci_type,
+ PCII440FXState **pi440fx_state,
int *piix3_devfn,
ISABus **isa_bus, qemu_irq *pic,
MemoryRegion *address_space_mem,
@@ -320,7 +331,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
unsigned i;
I440FXState *i440fx;
- dev = qdev_create(NULL, TYPE_I440FX_PCI_HOST_BRIDGE);
+ dev = qdev_create(NULL, host_type);
s = PCI_HOST_BRIDGE(dev);
b = pci_bus_new(dev, NULL, pci_address_space,
address_space_io, 0, TYPE_PCI_BUS);
@@ -328,7 +339,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
object_property_add_child(qdev_get_machine(), "i440fx", OBJECT(dev), NULL);
qdev_init_nofail(dev);
- d = pci_create_simple(b, 0, TYPE_I440FX_PCI_DEVICE);
+ d = pci_create_simple(b, 0, pci_type);
*pi440fx_state = I440FX_PCI_DEVICE(d);
f = *pi440fx_state;
f->system_memory = address_space_mem;
@@ -394,7 +405,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
if (ram_size > 255) {
ram_size = 255;
}
- d->config[0x57] = ram_size;
+ d->config[I440FX_COREBOOT_RAM_SIZE] = ram_size;
i440fx_update_memory_mappings(f);
@@ -642,8 +653,10 @@ static void piix3_realize(PCIDevice *dev, Error **errp)
{
PIIX3State *d = PIIX3_PCI_DEVICE(dev);
- isa_bus_new(DEVICE(d), get_system_memory(),
- pci_address_space_io(dev));
+ if (!isa_bus_new(DEVICE(d), get_system_memory(),
+ pci_address_space_io(dev), errp)) {
+ return;
+ }
memory_region_init_io(&d->rcr_mem, OBJECT(dev), &rcr_ops, d,
"piix3-reset-control", 1);
@@ -735,6 +748,95 @@ static const TypeInfo i440fx_info = {
.class_init = i440fx_class_init,
};
+/* IGD Passthrough Host Bridge. */
+typedef struct {
+ uint8_t offset;
+ uint8_t len;
+} IGDHostInfo;
+
+/* Here we just expose minimal host bridge offset subset. */
+static const IGDHostInfo igd_host_bridge_infos[] = {
+ {0x08, 2}, /* revision id */
+ {0x2c, 2}, /* sybsystem vendor id */
+ {0x2e, 2}, /* sybsystem id */
+ {0x50, 2}, /* SNB: processor graphics control register */
+ {0x52, 2}, /* processor graphics control register */
+ {0xa4, 4}, /* SNB: graphics base of stolen memory */
+ {0xa8, 4}, /* SNB: base of GTT stolen memory */
+};
+
+static int host_pci_config_read(int pos, int len, uint32_t *val)
+{
+ char path[PATH_MAX];
+ int config_fd;
+ ssize_t size = sizeof(path);
+ /* Access real host bridge. */
+ int rc = snprintf(path, size, "/sys/bus/pci/devices/%04x:%02x:%02x.%d/%s",
+ 0, 0, 0, 0, "config");
+ int ret = 0;
+
+ if (rc >= size || rc < 0) {
+ return -ENODEV;
+ }
+
+ config_fd = open(path, O_RDWR);
+ if (config_fd < 0) {
+ return -ENODEV;
+ }
+
+ if (lseek(config_fd, pos, SEEK_SET) != pos) {
+ ret = -errno;
+ goto out;
+ }
+
+ do {
+ rc = read(config_fd, (uint8_t *)val, len);
+ } while (rc < 0 && (errno == EINTR || errno == EAGAIN));
+ if (rc != len) {
+ ret = -errno;
+ }
+
+out:
+ close(config_fd);
+ return ret;
+}
+
+static int igd_pt_i440fx_initfn(struct PCIDevice *pci_dev)
+{
+ uint32_t val = 0;
+ int rc, i, num;
+ int pos, len;
+
+ num = ARRAY_SIZE(igd_host_bridge_infos);
+ for (i = 0; i < num; i++) {
+ pos = igd_host_bridge_infos[i].offset;
+ len = igd_host_bridge_infos[i].len;
+ rc = host_pci_config_read(pos, len, &val);
+ if (rc) {
+ return -ENODEV;
+ }
+ pci_default_write_config(pci_dev, pos, val, len);
+ }
+
+ return 0;
+}
+
+static void igd_passthrough_i440fx_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->init = igd_pt_i440fx_initfn;
+ dc->desc = "IGD Passthrough Host bridge";
+}
+
+static const TypeInfo igd_passthrough_i440fx_info = {
+ .name = TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE,
+ .parent = TYPE_I440FX_PCI_DEVICE,
+ .instance_size = sizeof(PCII440FXState),
+ .class_init = igd_passthrough_i440fx_class_init,
+};
+
static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge,
PCIBus *rootbus)
{
@@ -776,6 +878,7 @@ static const TypeInfo i440fx_pcihost_info = {
static void i440fx_register_types(void)
{
type_register_static(&i440fx_info);
+ type_register_static(&igd_passthrough_i440fx_info);
type_register_static(&piix3_pci_type_info);
type_register_static(&piix3_info);
type_register_static(&piix3_xen_info);
diff --git a/qemu/hw/pci-host/ppce500.c b/qemu/hw/pci-host/ppce500.c
index 613ba73c6..e502bc050 100644
--- a/qemu/hw/pci-host/ppce500.c
+++ b/qemu/hw/pci-host/ppce500.c
@@ -14,6 +14,7 @@
* (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/ppc/e500-ccsr.h"
#include "hw/pci/pci.h"
@@ -140,7 +141,7 @@ static uint64_t pci_reg_read4(void *opaque, hwaddr addr,
case PPCE500_PCI_OW3:
case PPCE500_PCI_OW4:
idx = (addr >> 5) & 0x7;
- switch (addr & 0xC) {
+ switch (addr & 0x1F) {
case PCI_POTAR:
value = pci->pob[idx].potar;
break;
@@ -162,7 +163,7 @@ static uint64_t pci_reg_read4(void *opaque, hwaddr addr,
case PPCE500_PCI_IW2:
case PPCE500_PCI_IW1:
idx = ((addr >> 5) & 0x3) - 1;
- switch (addr & 0xC) {
+ switch (addr & 0x1F) {
case PCI_PITAR:
value = pci->pib[idx].pitar;
break;
diff --git a/qemu/hw/pci-host/prep.c b/qemu/hw/pci-host/prep.c
index c63f45d21..487e32ecb 100644
--- a/qemu/hw/pci-host/prep.c
+++ b/qemu/hw/pci-host/prep.c
@@ -23,6 +23,8 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_bus.h"
@@ -302,7 +304,7 @@ static void raven_realize(PCIDevice *d, Error **errp)
d->config[0x34] = 0x00; // capabilities_pointer
memory_region_init_ram(&s->bios, OBJECT(s), "bios", BIOS_SIZE,
- &error_abort);
+ &error_fatal);
memory_region_set_readonly(&s->bios, true);
memory_region_add_subregion(get_system_memory(), (uint32_t)(-BIOS_SIZE),
&s->bios);
@@ -312,7 +314,7 @@ static void raven_realize(PCIDevice *d, Error **errp)
if (filename) {
if (s->elf_machine != EM_NONE) {
bios_size = load_elf(filename, NULL, NULL, NULL,
- NULL, NULL, 1, s->elf_machine, 0);
+ NULL, NULL, 1, s->elf_machine, 0, 0);
}
if (bios_size < 0) {
bios_size = get_image_size(filename);
@@ -326,11 +328,10 @@ static void raven_realize(PCIDevice *d, Error **errp)
}
}
if (bios_size < 0 || bios_size > BIOS_SIZE) {
+ /* FIXME should error_setg() */
hw_error("qemu: could not load bios image '%s'\n", s->bios_name);
}
- if (filename) {
- g_free(filename);
- }
+ g_free(filename);
}
}
@@ -357,8 +358,9 @@ static void raven_class_init(ObjectClass *klass, void *data)
dc->desc = "PReP Host Bridge - Motorola Raven";
dc->vmsd = &vmstate_raven;
/*
- * PCI-facing part of the host bridge, not usable without the
- * host-facing part, which can't be device_add'ed, yet.
+ * Reason: PCI-facing part of the host bridge, not usable without
+ * the host-facing part, which can't be device_add'ed, yet.
+ * Reason: realize() method uses hw_error().
*/
dc->cannot_instantiate_with_device_add_yet = true;
}
diff --git a/qemu/hw/pci-host/q35.c b/qemu/hw/pci-host/q35.c
index bd7409456..70f897e3a 100644
--- a/qemu/hw/pci-host/q35.c
+++ b/qemu/hw/pci-host/q35.c
@@ -27,8 +27,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/pci-host/q35.h"
+#include "qapi/error.h"
#include "qapi/visitor.h"
/****************************************************************************
@@ -67,27 +69,27 @@ static const char *q35_host_root_bus_path(PCIHostState *host_bridge,
}
static void q35_host_get_pci_hole_start(Object *obj, Visitor *v,
- void *opaque, const char *name,
+ const char *name, void *opaque,
Error **errp)
{
Q35PCIHost *s = Q35_HOST_DEVICE(obj);
uint32_t value = s->mch.pci_info.w32.begin;
- visit_type_uint32(v, &value, name, errp);
+ visit_type_uint32(v, name, &value, errp);
}
static void q35_host_get_pci_hole_end(Object *obj, Visitor *v,
- void *opaque, const char *name,
+ const char *name, void *opaque,
Error **errp)
{
Q35PCIHost *s = Q35_HOST_DEVICE(obj);
uint32_t value = s->mch.pci_info.w32.end;
- visit_type_uint32(v, &value, name, errp);
+ visit_type_uint32(v, name, &value, errp);
}
static void q35_host_get_pci_hole64_start(Object *obj, Visitor *v,
- void *opaque, const char *name,
+ const char *name, void *opaque,
Error **errp)
{
PCIHostState *h = PCI_HOST_BRIDGE(obj);
@@ -95,11 +97,11 @@ static void q35_host_get_pci_hole64_start(Object *obj, Visitor *v,
pci_bus_get_w64_range(h->bus, &w64);
- visit_type_uint64(v, &w64.begin, name, errp);
+ visit_type_uint64(v, name, &w64.begin, errp);
}
static void q35_host_get_pci_hole64_end(Object *obj, Visitor *v,
- void *opaque, const char *name,
+ const char *name, void *opaque,
Error **errp)
{
PCIHostState *h = PCI_HOST_BRIDGE(obj);
@@ -107,17 +109,16 @@ static void q35_host_get_pci_hole64_end(Object *obj, Visitor *v,
pci_bus_get_w64_range(h->bus, &w64);
- visit_type_uint64(v, &w64.end, name, errp);
+ visit_type_uint64(v, name, &w64.end, errp);
}
-static void q35_host_get_mmcfg_size(Object *obj, Visitor *v,
- void *opaque, const char *name,
- Error **errp)
+static void q35_host_get_mmcfg_size(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
PCIExpressHost *e = PCIE_HOST_BRIDGE(obj);
uint32_t value = e->size;
- visit_type_uint32(v, &value, name, errp);
+ visit_type_uint32(v, name, &value, errp);
}
static Property mch_props[] = {
@@ -426,31 +427,12 @@ static void mch_reset(DeviceState *qdev)
static AddressSpace *q35_host_dma_iommu(PCIBus *bus, void *opaque, int devfn)
{
IntelIOMMUState *s = opaque;
- VTDAddressSpace **pvtd_as;
- int bus_num = pci_bus_num(bus);
+ VTDAddressSpace *vtd_as;
- assert(0 <= bus_num && bus_num <= VTD_PCI_BUS_MAX);
assert(0 <= devfn && devfn <= VTD_PCI_DEVFN_MAX);
- pvtd_as = s->address_spaces[bus_num];
- if (!pvtd_as) {
- /* No corresponding free() */
- pvtd_as = g_malloc0(sizeof(VTDAddressSpace *) * VTD_PCI_DEVFN_MAX);
- s->address_spaces[bus_num] = pvtd_as;
- }
- if (!pvtd_as[devfn]) {
- pvtd_as[devfn] = g_malloc0(sizeof(VTDAddressSpace));
-
- pvtd_as[devfn]->bus_num = (uint8_t)bus_num;
- pvtd_as[devfn]->devfn = (uint8_t)devfn;
- pvtd_as[devfn]->iommu_state = s;
- pvtd_as[devfn]->context_cache_entry.context_cache_gen = 0;
- memory_region_init_iommu(&pvtd_as[devfn]->iommu, OBJECT(s),
- &s->iommu_ops, "intel_iommu", UINT64_MAX);
- address_space_init(&pvtd_as[devfn]->as,
- &pvtd_as[devfn]->iommu, "intel_iommu");
- }
- return &pvtd_as[devfn]->as;
+ vtd_as = vtd_find_add_as(s, bus, devfn);
+ return &vtd_as->as;
}
static void mch_init_dmar(MCHPCIState *mch)
@@ -525,7 +507,7 @@ static void mch_realize(PCIDevice *d, Error **errp)
PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, PAM_EXPAN_SIZE);
}
/* Intel IOMMU (VT-d) */
- if (machine_iommu(current_machine)) {
+ if (object_property_get_bool(qdev_get_machine(), "iommu", NULL)) {
mch_init_dmar(mch);
}
}
diff --git a/qemu/hw/pci-host/uninorth.c b/qemu/hw/pci-host/uninorth.c
index f0144eb7b..15b105423 100644
--- a/qemu/hw/pci-host/uninorth.c
+++ b/qemu/hw/pci-host/uninorth.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/ppc/mac.h"
#include "hw/pci/pci.h"
@@ -119,7 +120,7 @@ static void unin_data_write(void *opaque, hwaddr addr,
{
UNINState *s = opaque;
PCIHostState *phb = PCI_HOST_BRIDGE(s);
- UNIN_DPRINTF("write addr %" TARGET_FMT_plx " len %d val %"PRIx64"\n",
+ UNIN_DPRINTF("write addr " TARGET_FMT_plx " len %d val %"PRIx64"\n",
addr, len, val);
pci_data_write(phb->bus,
unin_get_config_reg(phb->config_reg, addr),
@@ -136,7 +137,7 @@ static uint64_t unin_data_read(void *opaque, hwaddr addr,
val = pci_data_read(phb->bus,
unin_get_config_reg(phb->config_reg, addr),
len);
- UNIN_DPRINTF("read addr %" TARGET_FMT_plx " len %d val %x\n",
+ UNIN_DPRINTF("read addr " TARGET_FMT_plx " len %d val %x\n",
addr, len, val);
return val;
}
@@ -330,6 +331,15 @@ static void unin_agp_pci_host_realize(PCIDevice *d, Error **errp)
d->config[0x0C] = 0x08; // cache_line_size
d->config[0x0D] = 0x10; // latency_timer
// d->config[0x34] = 0x80; // capabilities_pointer
+ /*
+ * Set kMacRISCPCIAddressSelect (0x48) register to indicate PCI
+ * memory space with base 0x80000000, size 0x10000000 for Apple's
+ * AppleMacRiscPCI driver
+ */
+ d->config[0x48] = 0x0;
+ d->config[0x49] = 0x0;
+ d->config[0x4a] = 0x0;
+ d->config[0x4b] = 0x1;
}
static void u3_agp_pci_host_realize(PCIDevice *d, Error **errp)
@@ -446,8 +456,10 @@ static const TypeInfo unin_internal_pci_host_info = {
static void pci_unin_main_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
sbc->init = pci_unin_main_init_device;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
}
static const TypeInfo pci_unin_main_info = {
@@ -460,8 +472,10 @@ static const TypeInfo pci_unin_main_info = {
static void pci_u3_agp_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
sbc->init = pci_u3_agp_init_device;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
}
static const TypeInfo pci_u3_agp_info = {
@@ -474,8 +488,10 @@ static const TypeInfo pci_u3_agp_info = {
static void pci_unin_agp_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
sbc->init = pci_unin_agp_init_device;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
}
static const TypeInfo pci_unin_agp_info = {
@@ -488,8 +504,10 @@ static const TypeInfo pci_unin_agp_info = {
static void pci_unin_internal_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
sbc->init = pci_unin_internal_init_device;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
}
static const TypeInfo pci_unin_internal_info = {
diff --git a/qemu/hw/pci-host/versatile.c b/qemu/hw/pci-host/versatile.c
index 6d2355309..339ec2c50 100644
--- a/qemu/hw/pci-host/versatile.c
+++ b/qemu/hw/pci-host/versatile.c
@@ -7,6 +7,7 @@
* This code is licensed under the LGPL.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_bus.h"
@@ -500,6 +501,8 @@ static void pci_vpb_class_init(ObjectClass *klass, void *data)
dc->reset = pci_vpb_reset;
dc->vmsd = &pci_vpb_vmstate;
dc->props = pci_vpb_properties;
+ /* Reason: object_unref() hangs */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo pci_vpb_info = {
@@ -521,10 +524,19 @@ static void pci_realview_init(Object *obj)
s->mem_win_size[2] = 0x08000000;
}
+static void pci_realview_class_init(ObjectClass *class, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(class);
+
+ /* Reason: object_unref() hangs */
+ dc->cannot_destroy_with_object_finalize_yet = true;
+}
+
static const TypeInfo pci_realview_info = {
.name = "realview_pci",
.parent = TYPE_VERSATILE_PCI,
.instance_init = pci_realview_init,
+ .class_init = pci_realview_class_init,
};
static void versatile_pci_register_types(void)
diff --git a/qemu/hw/pci/msi.c b/qemu/hw/pci/msi.c
index f9c048442..e0e64c2d9 100644
--- a/qemu/hw/pci/msi.c
+++ b/qemu/hw/pci/msi.c
@@ -18,7 +18,9 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/pci/msi.h"
+#include "hw/xen/xen.h"
#include "qemu/range.h"
/* PCI_MSI_ADDRESS_LO */
@@ -32,8 +34,21 @@
#define PCI_MSI_VECTORS_MAX 32
-/* Flag for interrupt controller to declare MSI/MSI-X support */
-bool msi_supported;
+/*
+ * Flag for interrupt controllers to declare broken MSI/MSI-X support.
+ * values: false - broken; true - non-broken.
+ *
+ * Setting this flag to false will remove MSI/MSI-X capability from all devices.
+ *
+ * It is preferrable for controllers to set this to true (non-broken) even if
+ * they do not actually support MSI/MSI-X: guests normally probe the controller
+ * type and do not attempt to enable MSI/MSI-X with interrupt controllers not
+ * supporting such, so removing the capability is not required, and
+ * it seems cleaner to have a given device look the same for all boards.
+ *
+ * TODO: some existing controllers violate the above rule. Identify and fix them.
+ */
+bool msi_nonbroken;
/* If we get rid of cap allocator, we won't need this. */
static inline uint8_t msi_cap_sizeof(uint16_t flags)
@@ -158,7 +173,7 @@ int msi_init(struct PCIDevice *dev, uint8_t offset,
uint8_t cap_size;
int config_offset;
- if (!msi_supported) {
+ if (!msi_nonbroken) {
return -ENOTSUP;
}
@@ -253,13 +268,19 @@ void msi_reset(PCIDevice *dev)
static bool msi_is_masked(const PCIDevice *dev, unsigned int vector)
{
uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
- uint32_t mask;
+ uint32_t mask, data;
+ bool msi64bit = flags & PCI_MSI_FLAGS_64BIT;
assert(vector < PCI_MSI_VECTORS_MAX);
if (!(flags & PCI_MSI_FLAGS_MASKBIT)) {
return false;
}
+ data = pci_get_word(dev->config + msi_data_off(dev, msi64bit));
+ if (xen_is_pirq_msi(data)) {
+ return false;
+ }
+
mask = pci_get_long(dev->config +
msi_mask_off(dev, flags & PCI_MSI_FLAGS_64BIT));
return mask & (1U << vector);
@@ -294,7 +315,7 @@ void msi_send_message(PCIDevice *dev, MSIMessage msg)
{
MemTxAttrs attrs = {};
- attrs.stream_id = (pci_bus_num(dev->bus) << 8) | dev->devfn;
+ attrs.requester_id = pci_requester_id(dev);
address_space_stl_le(&dev->bus_master_as, msg.address, msg.data,
attrs, NULL);
}
diff --git a/qemu/hw/pci/msix.c b/qemu/hw/pci/msix.c
index 7716bf364..b75f0e9c4 100644
--- a/qemu/hw/pci/msix.c
+++ b/qemu/hw/pci/msix.c
@@ -14,10 +14,12 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/pci/msi.h"
#include "hw/pci/msix.h"
#include "hw/pci/pci.h"
+#include "hw/xen/xen.h"
#include "qemu/range.h"
#define MSIX_CAP_LENGTH 12
@@ -77,8 +79,15 @@ static void msix_clr_pending(PCIDevice *dev, int vector)
static bool msix_vector_masked(PCIDevice *dev, unsigned int vector, bool fmask)
{
- unsigned offset = vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL;
- return fmask || dev->msix_table[offset] & PCI_MSIX_ENTRY_CTRL_MASKBIT;
+ unsigned offset = vector * PCI_MSIX_ENTRY_SIZE;
+ uint8_t *data = &dev->msix_table[offset + PCI_MSIX_ENTRY_DATA];
+ /* MSIs on Xen can be remapped into pirqs. In those cases, masking
+ * and unmasking go through the PV evtchn path. */
+ if (xen_enabled() && xen_is_pirq_msi(pci_get_long(data))) {
+ return false;
+ }
+ return fmask || dev->msix_table[offset + PCI_MSIX_ENTRY_VECTOR_CTRL] &
+ PCI_MSIX_ENTRY_CTRL_MASKBIT;
}
bool msix_is_masked(PCIDevice *dev, unsigned int vector)
@@ -200,8 +209,14 @@ static uint64_t msix_pba_mmio_read(void *opaque, hwaddr addr,
return pci_get_long(dev->msix_pba + addr);
}
+static void msix_pba_mmio_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+}
+
static const MemoryRegionOps msix_pba_mmio_ops = {
.read = msix_pba_mmio_read,
+ .write = msix_pba_mmio_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 4,
@@ -234,7 +249,7 @@ int msix_init(struct PCIDevice *dev, unsigned short nentries,
uint8_t *config;
/* Nothing to do if MSI is not supported by interrupt controller */
- if (!msi_supported) {
+ if (!msi_nonbroken) {
return -ENOTSUP;
}
@@ -314,9 +329,7 @@ int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries,
bar_size = bar_pba_offset + bar_pba_size;
}
- if (bar_size & (bar_size - 1)) {
- bar_size = 1 << qemu_fls(bar_size);
- }
+ bar_size = pow2ceil(bar_size);
name = g_strdup_printf("%s-msix", dev->name);
memory_region_init(&dev->msix_exclusive_bar, OBJECT(dev), name, bar_size);
diff --git a/qemu/hw/pci/pci-stub.c b/qemu/hw/pci/pci-stub.c
index 063a7c242..36d2c430c 100644
--- a/qemu/hw/pci/pci-stub.c
+++ b/qemu/hw/pci/pci-stub.c
@@ -18,6 +18,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "sysemu/sysemu.h"
#include "monitor/monitor.h"
#include "qapi/qmp/qerror.h"
diff --git a/qemu/hw/pci/pci.c b/qemu/hw/pci/pci.c
index a017614d4..bb605efae 100644
--- a/qemu/hw/pci/pci.c
+++ b/qemu/hw/pci/pci.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_bridge.h"
@@ -38,6 +39,8 @@
#include "hw/pci/msix.h"
#include "exec/address-spaces.h"
#include "hw/hotplug.h"
+#include "hw/boards.h"
+#include "qemu/cutils.h"
//#define DEBUG_PCI
#ifdef DEBUG_PCI
@@ -276,9 +279,9 @@ static void pcibus_reset(BusState *qbus)
}
}
-static void pci_host_bus_register(PCIBus *bus, DeviceState *parent)
+static void pci_host_bus_register(DeviceState *host)
{
- PCIHostState *host_bridge = PCI_HOST_BRIDGE(parent);
+ PCIHostState *host_bridge = PCI_HOST_BRIDGE(host);
QLIST_INSERT_HEAD(&pci_host_bridges, host_bridge, next);
}
@@ -329,7 +332,6 @@ const char *pci_root_bus_path(PCIDevice *dev)
}
static void pci_bus_init(PCIBus *bus, DeviceState *parent,
- const char *name,
MemoryRegion *address_space_mem,
MemoryRegion *address_space_io,
uint8_t devfn_min)
@@ -342,7 +344,7 @@ static void pci_bus_init(PCIBus *bus, DeviceState *parent,
/* host bridge */
QLIST_INIT(&bus->child);
- pci_host_bus_register(bus, parent);
+ pci_host_bus_register(parent);
}
bool pci_bus_is_express(PCIBus *bus)
@@ -362,8 +364,7 @@ void pci_bus_new_inplace(PCIBus *bus, size_t bus_size, DeviceState *parent,
uint8_t devfn_min, const char *typename)
{
qbus_create_inplace(bus, bus_size, typename, parent, name);
- pci_bus_init(bus, parent, name, address_space_mem,
- address_space_io, devfn_min);
+ pci_bus_init(bus, parent, address_space_mem, address_space_io, devfn_min);
}
PCIBus *pci_bus_new(DeviceState *parent, const char *name,
@@ -374,8 +375,7 @@ PCIBus *pci_bus_new(DeviceState *parent, const char *name,
PCIBus *bus;
bus = PCI_BUS(qbus_create(typename, parent, name));
- pci_bus_init(bus, parent, name, address_space_mem,
- address_space_io, devfn_min);
+ pci_bus_init(bus, parent, address_space_mem, address_space_io, devfn_min);
return bus;
}
@@ -846,6 +846,16 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,
PCIConfigWriteFunc *config_write = pc->config_write;
Error *local_err = NULL;
AddressSpace *dma_as;
+ DeviceState *dev = DEVICE(pci_dev);
+
+ pci_dev->bus = bus;
+ /* Only pci bridges can be attached to extra PCI root buses */
+ if (pci_bus_is_root(bus) && bus->parent_dev && !pc->is_bridge) {
+ error_setg(errp,
+ "PCI: Only PCI/PCIe bridges can be plugged into %s",
+ bus->parent_dev->name);
+ return NULL;
+ }
if (devfn < 0) {
for(devfn = bus->devfn_min ; devfn < ARRAY_SIZE(bus->devices);
@@ -863,9 +873,17 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,
PCI_SLOT(devfn), PCI_FUNC(devfn), name,
bus->devices[devfn]->name);
return NULL;
+ } else if (dev->hotplugged &&
+ pci_get_function_0(pci_dev)) {
+ error_setg(errp, "PCI: slot %d function 0 already ocuppied by %s,"
+ " new func %s cannot be exposed to guest.",
+ PCI_SLOT(devfn),
+ bus->devices[PCI_DEVFN(PCI_SLOT(devfn), 0)]->name,
+ name);
+
+ return NULL;
}
- pci_dev->bus = bus;
pci_dev->devfn = devfn;
dma_as = pci_device_iommu_address_space(pci_dev);
@@ -1065,6 +1083,10 @@ static pcibus_t pci_bar_address(PCIDevice *d,
pcibus_t new_addr, last_addr;
int bar = pci_bar(d, reg);
uint16_t cmd = pci_get_word(d->config + PCI_COMMAND);
+ Object *machine = qdev_get_machine();
+ ObjectClass *oc = object_get_class(machine);
+ MachineClass *mc = MACHINE_CLASS(oc);
+ bool allow_0_address = mc->pci_allow_0_address;
if (type & PCI_BASE_ADDRESS_SPACE_IO) {
if (!(cmd & PCI_COMMAND_IO)) {
@@ -1075,7 +1097,8 @@ static pcibus_t pci_bar_address(PCIDevice *d,
/* Check if 32 bit BAR wraps around explicitly.
* TODO: make priorities correct and remove this work around.
*/
- if (last_addr <= new_addr || new_addr == 0 || last_addr >= UINT32_MAX) {
+ if (last_addr <= new_addr || last_addr >= UINT32_MAX ||
+ (!allow_0_address && new_addr == 0)) {
return PCI_BAR_UNMAPPED;
}
return new_addr;
@@ -1099,8 +1122,8 @@ static pcibus_t pci_bar_address(PCIDevice *d,
/* XXX: as we cannot support really dynamic
mappings, we handle specific values as invalid
mappings. */
- if (last_addr <= new_addr || new_addr == 0 ||
- last_addr == PCI_BAR_UNMAPPED) {
+ if (last_addr <= new_addr || last_addr == PCI_BAR_UNMAPPED ||
+ (!allow_0_address && new_addr == 0)) {
return PCI_BAR_UNMAPPED;
}
@@ -1148,16 +1171,16 @@ static void pci_update_mappings(PCIDevice *d)
/* now do the real mapping */
if (r->addr != PCI_BAR_UNMAPPED) {
trace_pci_update_mappings_del(d, pci_bus_num(d->bus),
- PCI_FUNC(d->devfn),
PCI_SLOT(d->devfn),
+ PCI_FUNC(d->devfn),
i, r->addr, r->size);
memory_region_del_subregion(r->address_space, r->memory);
}
r->addr = new_addr;
if (r->addr != PCI_BAR_UNMAPPED) {
trace_pci_update_mappings_add(d, pci_bus_num(d->bus),
- PCI_FUNC(d->devfn),
PCI_SLOT(d->devfn),
+ PCI_FUNC(d->devfn),
i, r->addr, r->size);
memory_region_add_subregion_overlap(r->address_space,
r->addr, r->memory, 1);
@@ -2065,9 +2088,7 @@ static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom,
g_free(path);
return;
}
- if (size & (size - 1)) {
- size = 1 << qemu_fls(size);
- }
+ size = pow2ceil(size);
vmsd = qdev_get_vmsd(DEVICE(pdev));
@@ -2077,7 +2098,7 @@ static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom,
snprintf(name, sizeof(name), "%s.rom", object_get_typename(OBJECT(pdev)));
}
pdev->has_rom = true;
- memory_region_init_ram(&pdev->rom, OBJECT(pdev), name, size, &error_abort);
+ memory_region_init_ram(&pdev->rom, OBJECT(pdev), name, size, &error_fatal);
vmstate_register_ram(&pdev->rom, &pdev->qdev);
ptr = memory_region_get_ram_ptr(&pdev->rom);
load_image(path, ptr);
@@ -2379,17 +2400,14 @@ static void pci_device_class_init(ObjectClass *klass, void *data)
AddressSpace *pci_device_iommu_address_space(PCIDevice *dev)
{
PCIBus *bus = PCI_BUS(dev->bus);
+ PCIBus *iommu_bus = bus;
- if (bus->iommu_fn) {
- return bus->iommu_fn(bus, bus->iommu_opaque, dev->devfn);
+ while(iommu_bus && !iommu_bus->iommu_fn && iommu_bus->parent_dev) {
+ iommu_bus = PCI_BUS(iommu_bus->parent_dev->bus);
}
-
- if (bus->parent_dev) {
- /** We are ignoring the bus master DMA bit of the bridge
- * as it would complicate things such as VFIO for no good reason */
- return pci_device_iommu_address_space(bus->parent_dev);
+ if (iommu_bus && iommu_bus->iommu_fn) {
+ return iommu_bus->iommu_fn(bus, iommu_bus->iommu_opaque, dev->devfn);
}
-
return &address_space_memory;
}
@@ -2453,6 +2471,33 @@ void pci_bus_get_w64_range(PCIBus *bus, Range *range)
pci_for_each_device_under_bus(bus, pci_dev_get_w64, range);
}
+static bool pcie_has_upstream_port(PCIDevice *dev)
+{
+ PCIDevice *parent_dev = pci_bridge_get_device(dev->bus);
+
+ /* Device associated with an upstream port.
+ * As there are several types of these, it's easier to check the
+ * parent device: upstream ports are always connected to
+ * root or downstream ports.
+ */
+ return parent_dev &&
+ pci_is_express(parent_dev) &&
+ parent_dev->exp.exp_cap &&
+ (pcie_cap_get_type(parent_dev) == PCI_EXP_TYPE_ROOT_PORT ||
+ pcie_cap_get_type(parent_dev) == PCI_EXP_TYPE_DOWNSTREAM);
+}
+
+PCIDevice *pci_get_function_0(PCIDevice *pci_dev)
+{
+ if(pcie_has_upstream_port(pci_dev)) {
+ /* With an upstream PCIe port, we only support 1 device at slot 0 */
+ return pci_dev->bus->devices[0];
+ } else {
+ /* Other bus types might support multiple devices at slots 0-31 */
+ return pci_dev->bus->devices[PCI_DEVFN(PCI_SLOT(pci_dev->devfn), 0)];
+ }
+}
+
static const TypeInfo pci_device_type_info = {
.name = TYPE_PCI_DEVICE,
.parent = TYPE_DEVICE,
diff --git a/qemu/hw/pci/pci_bridge.c b/qemu/hw/pci/pci_bridge.c
index 40c97b155..3cf30bd33 100644
--- a/qemu/hw/pci/pci_bridge.c
+++ b/qemu/hw/pci/pci_bridge.c
@@ -29,6 +29,7 @@
* VA Linux Systems Japan K.K.
*/
+#include "qemu/osdep.h"
#include "hw/pci/pci_bridge.h"
#include "hw/pci/pci_bus.h"
#include "qemu/range.h"
@@ -332,7 +333,7 @@ void pci_bridge_reset(DeviceState *qdev)
}
/* default qdev initialization function for PCI-to-PCI bridge */
-int pci_bridge_initfn(PCIDevice *dev, const char *typename)
+void pci_bridge_initfn(PCIDevice *dev, const char *typename)
{
PCIBus *parent = dev->bus;
PCIBridge *br = PCI_BRIDGE(dev);
@@ -378,7 +379,6 @@ int pci_bridge_initfn(PCIDevice *dev, const char *typename)
br->windows = pci_bridge_region_init(br);
QLIST_INIT(&sec_bus->child);
QLIST_INSERT_HEAD(&parent->child, sec_bus, sibling);
- return 0;
}
/* default qdev clean up function for PCI-to-PCI bridge */
diff --git a/qemu/hw/pci/pci_host.c b/qemu/hw/pci/pci_host.c
index 3e26f9256..5eaa935cb 100644
--- a/qemu/hw/pci/pci_host.c
+++ b/qemu/hw/pci/pci_host.c
@@ -18,8 +18,10 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_host.h"
+#include "hw/pci/pci_bus.h"
#include "trace.h"
/* debug PCI */
@@ -52,6 +54,13 @@ void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr,
uint32_t limit, uint32_t val, uint32_t len)
{
assert(len <= 4);
+ /* non-zero functions are only exposed when function 0 is present,
+ * allowing direct removal of unexposed functions.
+ */
+ if (pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) {
+ return;
+ }
+
trace_pci_cfg_write(pci_dev->name, PCI_SLOT(pci_dev->devfn),
PCI_FUNC(pci_dev->devfn), addr, val);
pci_dev->config_write(pci_dev, addr, val, MIN(len, limit - addr));
@@ -63,6 +72,13 @@ uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr,
uint32_t ret;
assert(len <= 4);
+ /* non-zero functions are only exposed when function 0 is present,
+ * allowing direct removal of unexposed functions.
+ */
+ if (pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) {
+ return ~0x0;
+ }
+
ret = pci_dev->config_read(pci_dev, addr, MIN(len, limit - addr));
trace_pci_cfg_read(pci_dev->name, PCI_SLOT(pci_dev->devfn),
PCI_FUNC(pci_dev->devfn), addr, ret);
diff --git a/qemu/hw/pci/pcie.c b/qemu/hw/pci/pcie.c
index 6e28985bd..728386ada 100644
--- a/qemu/hw/pci/pcie.c
+++ b/qemu/hw/pci/pcie.c
@@ -18,6 +18,8 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu-common.h"
#include "hw/pci/pci_bridge.h"
#include "hw/pci/pcie.h"
@@ -249,25 +251,43 @@ void pcie_cap_slot_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
return;
}
- /* TODO: multifunction hot-plug.
- * Right now, only a device of function = 0 is allowed to be
- * hot plugged/unplugged.
+ /* To enable multifunction hot-plug, we just ensure the function
+ * 0 added last. When function 0 is added, we set the sltsta and
+ * inform OS via event notification.
*/
- assert(PCI_FUNC(pci_dev->devfn) == 0);
+ if (pci_get_function_0(pci_dev)) {
+ pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA,
+ PCI_EXP_SLTSTA_PDS);
+ pcie_cap_slot_event(PCI_DEVICE(hotplug_dev),
+ PCI_EXP_HP_EV_PDC | PCI_EXP_HP_EV_ABP);
+ }
+}
- pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA,
- PCI_EXP_SLTSTA_PDS);
- pcie_cap_slot_event(PCI_DEVICE(hotplug_dev),
- PCI_EXP_HP_EV_PDC | PCI_EXP_HP_EV_ABP);
+static void pcie_unplug_device(PCIBus *bus, PCIDevice *dev, void *opaque)
+{
+ object_unparent(OBJECT(dev));
}
void pcie_cap_slot_hot_unplug_request_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
uint8_t *exp_cap;
+ PCIDevice *pci_dev = PCI_DEVICE(dev);
+ PCIBus *bus = pci_dev->bus;
pcie_cap_slot_hotplug_common(PCI_DEVICE(hotplug_dev), dev, &exp_cap, errp);
+ /* In case user cancel the operation of multi-function hot-add,
+ * remove the function that is unexposed to guest individually,
+ * without interaction with guest.
+ */
+ if (pci_dev->devfn &&
+ !bus->devices[0]) {
+ pcie_unplug_device(bus, pci_dev, NULL);
+
+ return;
+ }
+
pcie_cap_slot_push_attention_button(PCI_DEVICE(hotplug_dev));
}
@@ -378,11 +398,6 @@ void pcie_cap_slot_reset(PCIDevice *dev)
hotplug_event_update_event_status(dev);
}
-static void pcie_unplug_device(PCIBus *bus, PCIDevice *dev, void *opaque)
-{
- object_unparent(OBJECT(dev));
-}
-
void pcie_cap_slot_write_config(PCIDevice *dev,
uint32_t addr, uint32_t val, int len)
{
@@ -413,13 +428,13 @@ void pcie_cap_slot_write_config(PCIDevice *dev,
*/
if ((sltsta & PCI_EXP_SLTSTA_PDS) && (val & PCI_EXP_SLTCTL_PCC) &&
((val & PCI_EXP_SLTCTL_PIC_OFF) == PCI_EXP_SLTCTL_PIC_OFF)) {
- PCIBus *sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(dev));
- pci_for_each_device(sec_bus, pci_bus_num(sec_bus),
- pcie_unplug_device, NULL);
+ PCIBus *sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(dev));
+ pci_for_each_device(sec_bus, pci_bus_num(sec_bus),
+ pcie_unplug_device, NULL);
- pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA,
- PCI_EXP_SLTSTA_PDS);
- pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA,
+ pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA,
+ PCI_EXP_SLTSTA_PDS);
+ pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA,
PCI_EXP_SLTSTA_PDC);
}
@@ -594,7 +609,7 @@ void pcie_add_capability(PCIDevice *dev,
assert(offset >= PCI_CONFIG_SPACE_SIZE);
assert(offset < offset + size);
- assert(offset + size < PCIE_CONFIG_SPACE_SIZE);
+ assert(offset + size <= PCIE_CONFIG_SPACE_SIZE);
assert(size >= 8);
assert(pci_is_express(dev));
diff --git a/qemu/hw/pci/pcie_aer.c b/qemu/hw/pci/pcie_aer.c
index f1847ac21..e2d4e68ba 100644
--- a/qemu/hw/pci/pcie_aer.c
+++ b/qemu/hw/pci/pcie_aer.c
@@ -18,6 +18,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "sysemu/sysemu.h"
#include "qapi/qmp/types.h"
#include "monitor/monitor.h"
@@ -94,12 +95,12 @@ static void aer_log_clear_all_err(PCIEAERLog *aer_log)
aer_log->log_num = 0;
}
-int pcie_aer_init(PCIDevice *dev, uint16_t offset)
+int pcie_aer_init(PCIDevice *dev, uint16_t offset, uint16_t size)
{
PCIExpressDevice *exp;
pcie_add_capability(dev, PCI_EXT_CAP_ID_ERR, PCI_ERR_VER,
- offset, PCI_ERR_SIZEOF);
+ offset, size);
exp = &dev->exp;
exp->aer_cap = offset;
@@ -370,7 +371,7 @@ static void pcie_aer_msg_root_port(PCIDevice *dev, const PCIEAERMsg *msg)
*
* Walk up the bus tree from the device, propagate the error message.
*/
-static void pcie_aer_msg(PCIDevice *dev, const PCIEAERMsg *msg)
+void pcie_aer_msg(PCIDevice *dev, const PCIEAERMsg *msg)
{
uint8_t type;
@@ -827,10 +828,6 @@ typedef struct PCIEAERErrorName {
*/
static const struct PCIEAERErrorName pcie_aer_error_list[] = {
{
- .name = "TRAIN",
- .val = PCI_ERR_UNC_TRAIN,
- .correctable = false,
- }, {
.name = "DLP",
.val = PCI_ERR_UNC_DLP,
.correctable = false,
@@ -983,7 +980,7 @@ static int do_pcie_aer_inject_error(Monitor *mon,
}
}
err.status = error_status;
- err.source_id = (pci_bus_num(dev->bus) << 8) | dev->devfn;
+ err.source_id = pci_requester_id(dev);
err.flags = 0;
if (correctable) {
diff --git a/qemu/hw/pci/pcie_host.c b/qemu/hw/pci/pcie_host.c
index d8afba863..dcebf57ed 100644
--- a/qemu/hw/pci/pcie_host.c
+++ b/qemu/hw/pci/pcie_host.c
@@ -19,6 +19,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "hw/pci/pcie_host.h"
diff --git a/qemu/hw/pci/pcie_port.c b/qemu/hw/pci/pcie_port.c
index 40ca8d5d1..6432b9ac1 100644
--- a/qemu/hw/pci/pcie_port.c
+++ b/qemu/hw/pci/pcie_port.c
@@ -18,6 +18,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/pci/pcie_port.h"
#include "hw/hotplug.h"
diff --git a/qemu/hw/pci/shpc.c b/qemu/hw/pci/shpc.c
index bfb4d31b6..3dcd472eb 100644
--- a/qemu/hw/pci/shpc.c
+++ b/qemu/hw/pci/shpc.c
@@ -1,6 +1,6 @@
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu-common.h"
-#include <strings.h>
-#include <stdint.h>
#include "qemu/range.h"
#include "qemu/error-report.h"
#include "hw/pci/shpc.h"
diff --git a/qemu/hw/pci/slotid_cap.c b/qemu/hw/pci/slotid_cap.c
index 1c01d346c..aec1e9166 100644
--- a/qemu/hw/pci/slotid_cap.c
+++ b/qemu/hw/pci/slotid_cap.c
@@ -1,3 +1,4 @@
+#include "qemu/osdep.h"
#include "hw/pci/slotid_cap.h"
#include "hw/pci/pci.h"
#include "qemu/error-report.h"
diff --git a/qemu/hw/pcmcia/pcmcia.c b/qemu/hw/pcmcia/pcmcia.c
index 78efe5a67..195672186 100644
--- a/qemu/hw/pcmcia/pcmcia.c
+++ b/qemu/hw/pcmcia/pcmcia.c
@@ -4,6 +4,7 @@
* Copyright 2013 SUSE LINUX Products GmbH
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "hw/hw.h"
#include "hw/pcmcia.h"
diff --git a/qemu/hw/pcmcia/pxa2xx.c b/qemu/hw/pcmcia/pxa2xx.c
index a7e187743..20c9c753d 100644
--- a/qemu/hw/pcmcia/pxa2xx.c
+++ b/qemu/hw/pcmcia/pxa2xx.c
@@ -10,6 +10,7 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "hw/pcmcia.h"
@@ -163,7 +164,7 @@ static void pxa2xx_pcmcia_initfn(Object *obj)
sysbus_init_mmio(sbd, &s->container_mem);
/* Socket I/O Memory Space */
- memory_region_init_io(&s->iomem, NULL, &pxa2xx_pcmcia_io_ops, s,
+ memory_region_init_io(&s->iomem, obj, &pxa2xx_pcmcia_io_ops, s,
"pxa2xx-pcmcia-io", 0x04000000);
memory_region_add_subregion(&s->container_mem, 0x00000000,
&s->iomem);
@@ -171,13 +172,13 @@ static void pxa2xx_pcmcia_initfn(Object *obj)
/* Then next 64 MB is reserved */
/* Socket Attribute Memory Space */
- memory_region_init_io(&s->attr_iomem, NULL, &pxa2xx_pcmcia_attr_ops, s,
+ memory_region_init_io(&s->attr_iomem, obj, &pxa2xx_pcmcia_attr_ops, s,
"pxa2xx-pcmcia-attribute", 0x04000000);
memory_region_add_subregion(&s->container_mem, 0x08000000,
&s->attr_iomem);
/* Socket Common Memory Space */
- memory_region_init_io(&s->common_iomem, NULL, &pxa2xx_pcmcia_common_ops, s,
+ memory_region_init_io(&s->common_iomem, obj, &pxa2xx_pcmcia_common_ops, s,
"pxa2xx-pcmcia-common", 0x04000000);
memory_region_add_subregion(&s->container_mem, 0x0c000000,
&s->common_iomem);
diff --git a/qemu/hw/ppc/Makefile.objs b/qemu/hw/ppc/Makefile.objs
index c8ab06e7f..c1ffc7771 100644
--- a/qemu/hw/ppc/Makefile.objs
+++ b/qemu/hw/ppc/Makefile.objs
@@ -3,7 +3,7 @@ obj-y += ppc.o ppc_booke.o
# IBM pSeries (sPAPR)
obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o
obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
-obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o
+obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
obj-y += spapr_pci_vfio.o
endif
diff --git a/qemu/hw/ppc/e500.c b/qemu/hw/ppc/e500.c
index d300846c3..ee1c60b82 100644
--- a/qemu/hw/ppc/e500.c
+++ b/qemu/hw/ppc/e500.c
@@ -14,7 +14,8 @@
* (at your option) any later version.
*/
-#include "config.h"
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu-common.h"
#include "e500.h"
#include "e500-ccsr.h"
@@ -751,8 +752,8 @@ static qemu_irq *ppce500_init_mpic(MachineState *machine, PPCE500Params *params,
dev = ppce500_init_mpic_kvm(params, irqs, &err);
}
if (machine_kernel_irqchip_required(machine) && !dev) {
- error_report("kernel_irqchip requested but unavailable: %s",
- error_get_pretty(err));
+ error_reportf_err(err,
+ "kernel_irqchip requested but unavailable: ");
exit(1);
}
}
@@ -1017,7 +1018,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
bios_size = load_elf(filename, NULL, NULL, &bios_entry, &loadaddr, NULL,
- 1, ELF_MACHINE, 0);
+ 1, PPC_ELF_MACHINE, 0, 0);
if (bios_size < 0) {
/*
* Hrm. No ELF image? Try a uImage, maybe someone is giving us an
@@ -1048,10 +1049,6 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
boot_info->entry = bios_entry;
boot_info->dt_base = dt_base;
boot_info->dt_size = dt_size;
-
- if (kvm_enabled()) {
- kvmppc_init();
- }
}
static int e500_ccsr_initfn(SysBusDevice *dev)
diff --git a/qemu/hw/ppc/e500plat.c b/qemu/hw/ppc/e500plat.c
index 14b14eaa7..b00565c3d 100644
--- a/qemu/hw/ppc/e500plat.c
+++ b/qemu/hw/ppc/e500plat.c
@@ -9,7 +9,7 @@
* (at your option) any later version.
*/
-#include "config.h"
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "e500.h"
#include "hw/boards.h"
@@ -57,17 +57,12 @@ static void e500plat_init(MachineState *machine)
ppce500_init(machine, &params);
}
-static QEMUMachine e500plat_machine = {
- .name = "ppce500",
- .desc = "generic paravirt e500 platform",
- .init = e500plat_init,
- .max_cpus = 32,
- .has_dynamic_sysbus = true,
-};
-
-static void e500plat_machine_init(void)
+static void e500plat_machine_init(MachineClass *mc)
{
- qemu_register_machine(&e500plat_machine);
+ mc->desc = "generic paravirt e500 platform";
+ mc->init = e500plat_init;
+ mc->max_cpus = 32;
+ mc->has_dynamic_sysbus = true;
}
-machine_init(e500plat_machine_init);
+DEFINE_MACHINE("ppce500", e500plat_machine_init)
diff --git a/qemu/hw/ppc/mac.h b/qemu/hw/ppc/mac.h
index 8bdba30c1..5764b86c2 100644
--- a/qemu/hw/ppc/mac.h
+++ b/qemu/hw/ppc/mac.h
@@ -103,11 +103,16 @@ typedef struct CUDAState {
uint8_t last_b;
uint8_t last_acr;
+ /* MacOS 9 is racy and requires a delay upon setting the SR_INT bit */
+ QEMUTimer *sr_delay_timer;
+
int data_in_size;
int data_in_index;
int data_out_index;
qemu_irq irq;
+ uint16_t adb_poll_mask;
+ uint8_t autopoll_rate_ms;
uint8_t autopoll;
uint8_t data_in[128];
uint8_t data_out[16];
@@ -131,7 +136,6 @@ typedef struct MACIOIDEState {
MemoryRegion mem;
IDEBus bus;
- BlockAIOCB *aiocb;
IDEDMA dma;
void *dbdma;
bool dma_active;
diff --git a/qemu/hw/ppc/mac_newworld.c b/qemu/hw/ppc/mac_newworld.c
index 77d5c819e..32e88b378 100644
--- a/qemu/hw/ppc/mac_newworld.c
+++ b/qemu/hw/ppc/mac_newworld.c
@@ -46,6 +46,8 @@
* 0001:05:0c.0 IDE interface [0101]: Broadcom K2 SATA [1166:0240]
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/ppc/ppc.h"
#include "hw/ppc/mac.h"
@@ -62,12 +64,14 @@
#include "hw/ide.h"
#include "hw/loader.h"
#include "elf.h"
+#include "qemu/error-report.h"
#include "sysemu/kvm.h"
#include "kvm_ppc.h"
#include "hw/usb.h"
#include "sysemu/block-backend.h"
#include "exec/address-spaces.h"
#include "hw/sysbus.h"
+#include "qemu/cutils.h"
#define MAX_IDE_BUS 2
#define CFG_ADDR 0xf0000510
@@ -207,7 +211,7 @@ static void ppc_core99_init(MachineState *machine)
/* allocate and load BIOS */
memory_region_init_ram(bios, NULL, "ppc_core99.bios", BIOS_SIZE,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(bios);
if (bios_name == NULL)
@@ -219,14 +223,14 @@ static void ppc_core99_init(MachineState *machine)
/* Load OpenBIOS (ELF) */
if (filename) {
bios_size = load_elf(filename, NULL, NULL, NULL,
- NULL, NULL, 1, ELF_MACHINE, 0);
+ NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0);
g_free(filename);
} else {
bios_size = -1;
}
if (bios_size < 0 || bios_size > BIOS_SIZE) {
- hw_error("qemu: could not load PowerPC bios '%s'\n", bios_name);
+ error_report("could not load PowerPC bios '%s'", bios_name);
exit(1);
}
@@ -242,7 +246,8 @@ static void ppc_core99_init(MachineState *machine)
kernel_base = KERNEL_LOAD_ADDR;
kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL,
- NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0);
+ NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE,
+ 0, 0);
if (kernel_size < 0)
kernel_size = load_aout(kernel_filename, kernel_base,
ram_size - kernel_base, bswap_needed,
@@ -252,7 +257,7 @@ static void ppc_core99_init(MachineState *machine)
kernel_base,
ram_size - kernel_base);
if (kernel_size < 0) {
- hw_error("qemu: could not load kernel '%s'\n", kernel_filename);
+ error_report("could not load kernel '%s'", kernel_filename);
exit(1);
}
/* load initrd */
@@ -261,8 +266,8 @@ static void ppc_core99_init(MachineState *machine)
initrd_size = load_image_targphys(initrd_filename, initrd_base,
ram_size - initrd_base);
if (initrd_size < 0) {
- hw_error("qemu: could not load initial ram disk '%s'\n",
- initrd_filename);
+ error_report("could not load initial ram disk '%s'",
+ initrd_filename);
exit(1);
}
cmdline_base = round_page(initrd_base + initrd_size);
@@ -344,7 +349,7 @@ static void ppc_core99_init(MachineState *machine)
break;
#endif /* defined(TARGET_PPC64) */
default:
- hw_error("Bus model not supported on mac99 machine\n");
+ error_report("Bus model not supported on mac99 machine");
exit(1);
}
}
@@ -371,12 +376,13 @@ static void ppc_core99_init(MachineState *machine)
/* 970 gets a U3 bus */
pci_bus = pci_pmac_u3_init(pic, get_system_memory(), get_system_io());
machine_arch = ARCH_MAC99_U3;
- machine->usb |= defaults_enabled() && !machine->usb_disabled;
} else {
pci_bus = pci_pmac_init(pic, get_system_memory(), get_system_io());
machine_arch = ARCH_MAC99;
}
+ machine->usb |= defaults_enabled() && !machine->usb_disabled;
+
/* Timebase Frequency */
if (kvm_enabled()) {
tbfreq = kvmppc_get_tbfreq();
@@ -508,7 +514,6 @@ static void core99_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
- mc->name = "mac99";
mc->desc = "Mac99 based PowerMAC";
mc->init = ppc_core99_init;
mc->max_cpus = MAX_CPUS;
@@ -517,7 +522,7 @@ static void core99_machine_class_init(ObjectClass *oc, void *data)
}
static const TypeInfo core99_machine_info = {
- .name = "mac99-machine",
+ .name = MACHINE_TYPE_NAME("mac99"),
.parent = TYPE_MACHINE,
.class_init = core99_machine_class_init,
};
diff --git a/qemu/hw/ppc/mac_oldworld.c b/qemu/hw/ppc/mac_oldworld.c
index 06fdbaf58..a9bb1c27d 100644
--- a/qemu/hw/ppc/mac_oldworld.c
+++ b/qemu/hw/ppc/mac_oldworld.c
@@ -23,6 +23,8 @@
* 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/hw.h"
#include "hw/ppc/ppc.h"
#include "mac.h"
@@ -38,10 +40,12 @@
#include "hw/ide.h"
#include "hw/loader.h"
#include "elf.h"
+#include "qemu/error-report.h"
#include "sysemu/kvm.h"
#include "kvm_ppc.h"
#include "sysemu/block-backend.h"
#include "exec/address-spaces.h"
+#include "qemu/cutils.h"
#define MAX_IDE_BUS 2
#define CFG_ADDR 0xf0000510
@@ -135,7 +139,7 @@ static void ppc_heathrow_init(MachineState *machine)
/* allocate and load BIOS */
memory_region_init_ram(bios, NULL, "ppc_heathrow.bios", BIOS_SIZE,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(bios);
if (bios_name == NULL)
@@ -147,13 +151,13 @@ static void ppc_heathrow_init(MachineState *machine)
/* Load OpenBIOS (ELF) */
if (filename) {
bios_size = load_elf(filename, 0, NULL, NULL, NULL, NULL,
- 1, ELF_MACHINE, 0);
+ 1, PPC_ELF_MACHINE, 0, 0);
g_free(filename);
} else {
bios_size = -1;
}
if (bios_size < 0 || bios_size > BIOS_SIZE) {
- hw_error("qemu: could not load PowerPC bios '%s'\n", bios_name);
+ error_report("could not load PowerPC bios '%s'", bios_name);
exit(1);
}
@@ -168,7 +172,8 @@ static void ppc_heathrow_init(MachineState *machine)
#endif
kernel_base = KERNEL_LOAD_ADDR;
kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL,
- NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0);
+ NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE,
+ 0, 0);
if (kernel_size < 0)
kernel_size = load_aout(kernel_filename, kernel_base,
ram_size - kernel_base, bswap_needed,
@@ -178,8 +183,7 @@ static void ppc_heathrow_init(MachineState *machine)
kernel_base,
ram_size - kernel_base);
if (kernel_size < 0) {
- hw_error("qemu: could not load kernel '%s'\n",
- kernel_filename);
+ error_report("could not load kernel '%s'", kernel_filename);
exit(1);
}
/* load initrd */
@@ -188,8 +192,8 @@ static void ppc_heathrow_init(MachineState *machine)
initrd_size = load_image_targphys(initrd_filename, initrd_base,
ram_size - initrd_base);
if (initrd_size < 0) {
- hw_error("qemu: could not load initial ram disk '%s'\n",
- initrd_filename);
+ error_report("could not load initial ram disk '%s'",
+ initrd_filename);
exit(1);
}
cmdline_base = round_page(initrd_base + initrd_size);
@@ -246,7 +250,8 @@ static void ppc_heathrow_init(MachineState *machine)
((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT];
break;
default:
- hw_error("Bus model not supported on OldWorld Mac machine\n");
+ error_report("Bus model not supported on OldWorld Mac machine");
+ exit(1);
}
}
@@ -259,7 +264,8 @@ static void ppc_heathrow_init(MachineState *machine)
/* init basic PC hardware */
if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) {
- hw_error("Only 6xx bus is supported on heathrow machine\n");
+ error_report("Only 6xx bus is supported on heathrow machine");
+ exit(1);
}
pic = heathrow_pic_init(&pic_mem, 1, heathrow_irqs);
pci_bus = pci_grackle_init(0xfec00000, pic,
@@ -357,21 +363,17 @@ static int heathrow_kvm_type(const char *arg)
return 2;
}
-static QEMUMachine heathrow_machine = {
- .name = "g3beige",
- .desc = "Heathrow based PowerMAC",
- .init = ppc_heathrow_init,
- .max_cpus = MAX_CPUS,
+static void heathrow_machine_init(MachineClass *mc)
+{
+ mc->desc = "Heathrow based PowerMAC";
+ mc->init = ppc_heathrow_init;
+ mc->max_cpus = MAX_CPUS;
#ifndef TARGET_PPC64
- .is_default = 1,
+ mc->is_default = 1;
#endif
- .default_boot_order = "cd", /* TOFIX "cad" when Mac floppy is implemented */
- .kvm_type = heathrow_kvm_type,
-};
-
-static void heathrow_machine_init(void)
-{
- qemu_register_machine(&heathrow_machine);
+ /* TOFIX "cad" when Mac floppy is implemented */
+ mc->default_boot_order = "cd";
+ mc->kvm_type = heathrow_kvm_type;
}
-machine_init(heathrow_machine_init);
+DEFINE_MACHINE("g3beige", heathrow_machine_init)
diff --git a/qemu/hw/ppc/mpc8544_guts.c b/qemu/hw/ppc/mpc8544_guts.c
index a10abe978..ba69178d6 100644
--- a/qemu/hw/ppc/mpc8544_guts.c
+++ b/qemu/hw/ppc/mpc8544_guts.c
@@ -17,6 +17,9 @@
*
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "sysemu/sysemu.h"
#include "hw/sysbus.h"
diff --git a/qemu/hw/ppc/mpc8544ds.c b/qemu/hw/ppc/mpc8544ds.c
index 3a3b141e4..27b828901 100644
--- a/qemu/hw/ppc/mpc8544ds.c
+++ b/qemu/hw/ppc/mpc8544ds.c
@@ -9,7 +9,7 @@
* (at your option) any later version.
*/
-#include "config.h"
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "e500.h"
#include "hw/boards.h"
@@ -50,16 +50,11 @@ static void mpc8544ds_init(MachineState *machine)
}
-static QEMUMachine ppce500_machine = {
- .name = "mpc8544ds",
- .desc = "mpc8544ds",
- .init = mpc8544ds_init,
- .max_cpus = 15,
-};
-
-static void ppce500_machine_init(void)
+static void ppce500_machine_init(MachineClass *mc)
{
- qemu_register_machine(&ppce500_machine);
+ mc->desc = "mpc8544ds";
+ mc->init = mpc8544ds_init;
+ mc->max_cpus = 15;
}
-machine_init(ppce500_machine_init);
+DEFINE_MACHINE("mpc8544ds", ppce500_machine_init)
diff --git a/qemu/hw/ppc/ppc.c b/qemu/hw/ppc/ppc.c
index b77e30357..38ff2e159 100644
--- a/qemu/hw/ppc/ppc.c
+++ b/qemu/hw/ppc/ppc.c
@@ -21,6 +21,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "hw/ppc/ppc.h"
#include "hw/ppc/ppc_e500.h"
@@ -462,7 +465,7 @@ void ppce500_set_mpic_proxy(bool enabled)
uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset)
{
/* TB time in tb periods */
- return muldiv64(vmclk, tb_env->tb_freq, get_ticks_per_sec()) + tb_offset;
+ return muldiv64(vmclk, tb_env->tb_freq, NANOSECONDS_PER_SECOND) + tb_offset;
}
uint64_t cpu_ppc_load_tbl (CPUPPCState *env)
@@ -503,7 +506,9 @@ uint32_t cpu_ppc_load_tbu (CPUPPCState *env)
static inline void cpu_ppc_store_tb(ppc_tb_t *tb_env, uint64_t vmclk,
int64_t *tb_offsetp, uint64_t value)
{
- *tb_offsetp = value - muldiv64(vmclk, tb_env->tb_freq, get_ticks_per_sec());
+ *tb_offsetp = value -
+ muldiv64(vmclk, tb_env->tb_freq, NANOSECONDS_PER_SECOND);
+
LOG_TB("%s: tb %016" PRIx64 " offset %08" PRIx64 "\n",
__func__, value, *tb_offsetp);
}
@@ -637,11 +642,11 @@ static inline uint32_t _cpu_ppc_load_decr(CPUPPCState *env, uint64_t next)
diff = next - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
if (diff >= 0) {
- decr = muldiv64(diff, tb_env->decr_freq, get_ticks_per_sec());
+ decr = muldiv64(diff, tb_env->decr_freq, NANOSECONDS_PER_SECOND);
} else if (tb_env->flags & PPC_TIMER_BOOKE) {
decr = 0;
} else {
- decr = -muldiv64(-diff, tb_env->decr_freq, get_ticks_per_sec());
+ decr = -muldiv64(-diff, tb_env->decr_freq, NANOSECONDS_PER_SECOND);
}
LOG_TB("%s: %08" PRIx32 "\n", __func__, decr);
@@ -673,7 +678,8 @@ uint64_t cpu_ppc_load_purr (CPUPPCState *env)
diff = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - tb_env->purr_start;
- return tb_env->purr_load + muldiv64(diff, tb_env->tb_freq, get_ticks_per_sec());
+ return tb_env->purr_load +
+ muldiv64(diff, tb_env->tb_freq, NANOSECONDS_PER_SECOND);
}
/* When decrementer expires,
@@ -749,7 +755,7 @@ static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp,
/* Calculate the next timer event */
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- next = now + muldiv64(value, get_ticks_per_sec(), tb_env->decr_freq);
+ next = now + muldiv64(value, NANOSECONDS_PER_SECOND, tb_env->decr_freq);
*nextp = next;
/* Adjust timer */
@@ -834,7 +840,7 @@ static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq)
static void timebase_pre_save(void *opaque)
{
PPCTimebase *tb = opaque;
- uint64_t ticks = cpu_get_real_ticks();
+ uint64_t ticks = cpu_get_host_ticks();
PowerPCCPU *first_ppc_cpu = POWERPC_CPU(first_cpu);
if (!first_ppc_cpu->env.tb_env) {
@@ -878,7 +884,7 @@ static int timebase_post_load(void *opaque, int version_id)
NANOSECONDS_PER_SECOND);
guest_tb = tb_remote->guest_timebase + MIN(0, migration_duration_tb);
- tb_off_adj = guest_tb - cpu_get_real_ticks();
+ tb_off_adj = guest_tb - cpu_get_host_ticks();
tb_off = first_ppc_cpu->env.tb_env->tb_offset;
trace_ppc_tb_adjust(tb_off, tb_off_adj, tb_off_adj - tb_off,
@@ -1010,7 +1016,7 @@ static void cpu_4xx_fit_cb (void *opaque)
/* Cannot occur, but makes gcc happy */
return;
}
- next = now + muldiv64(next, get_ticks_per_sec(), tb_env->tb_freq);
+ next = now + muldiv64(next, NANOSECONDS_PER_SECOND, tb_env->tb_freq);
if (next == now)
next++;
timer_mod(ppc40x_timer->fit_timer, next);
@@ -1041,7 +1047,7 @@ static void start_stop_pit (CPUPPCState *env, ppc_tb_t *tb_env, int is_excp)
__func__, ppc40x_timer->pit_reload);
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
next = now + muldiv64(ppc40x_timer->pit_reload,
- get_ticks_per_sec(), tb_env->decr_freq);
+ NANOSECONDS_PER_SECOND, tb_env->decr_freq);
if (is_excp)
next += tb_env->decr_next - now;
if (next == now)
@@ -1106,7 +1112,7 @@ static void cpu_4xx_wdt_cb (void *opaque)
/* Cannot occur, but makes gcc happy */
return;
}
- next = now + muldiv64(next, get_ticks_per_sec(), tb_env->decr_freq);
+ next = now + muldiv64(next, NANOSECONDS_PER_SECOND, tb_env->decr_freq);
if (next == now)
next++;
LOG_TB("%s: TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx "\n", __func__,
diff --git a/qemu/hw/ppc/ppc405_boards.c b/qemu/hw/ppc/ppc405_boards.c
index ec6c4cbaf..4b2f07aec 100644
--- a/qemu/hw/ppc/ppc405_boards.c
+++ b/qemu/hw/ppc/ppc405_boards.c
@@ -21,6 +21,10 @@
* 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-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "hw/ppc/ppc.h"
#include "ppc405.h"
@@ -215,7 +219,8 @@ static void ref405ep_init(MachineState *machine)
33333333, &pic, kernel_filename == NULL ? 0 : 1);
/* allocate SRAM */
sram_size = 512 * 1024;
- memory_region_init_ram(sram, NULL, "ef405ep.sram", sram_size, &error_abort);
+ memory_region_init_ram(sram, NULL, "ef405ep.sram", sram_size,
+ &error_fatal);
vmstate_register_ram_global(sram);
memory_region_add_subregion(sysmem, 0xFFF00000, sram);
/* allocate and load BIOS */
@@ -250,7 +255,7 @@ static void ref405ep_init(MachineState *machine)
#endif
bios = g_new(MemoryRegion, 1);
memory_region_init_ram(bios, NULL, "ef405ep.bios", BIOS_SIZE,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(bios);
if (bios_name == NULL)
@@ -368,10 +373,18 @@ static void ref405ep_init(MachineState *machine)
#endif
}
-static QEMUMachine ref405ep_machine = {
- .name = "ref405ep",
- .desc = "ref405ep",
- .init = ref405ep_init,
+static void ref405ep_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "ref405ep";
+ mc->init = ref405ep_init;
+}
+
+static const TypeInfo ref405ep_type = {
+ .name = MACHINE_TYPE_NAME("ref405ep"),
+ .parent = TYPE_MACHINE,
+ .class_init = ref405ep_class_init,
};
/*****************************************************************************/
@@ -399,7 +412,7 @@ struct taihu_cpld_t {
uint8_t reg1;
};
-static uint32_t taihu_cpld_readb (void *opaque, hwaddr addr)
+static uint64_t taihu_cpld_read(void *opaque, hwaddr addr, unsigned size)
{
taihu_cpld_t *cpld;
uint32_t ret;
@@ -420,8 +433,8 @@ static uint32_t taihu_cpld_readb (void *opaque, hwaddr addr)
return ret;
}
-static void taihu_cpld_writeb (void *opaque,
- hwaddr addr, uint32_t value)
+static void taihu_cpld_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
{
taihu_cpld_t *cpld;
@@ -438,48 +451,12 @@ static void taihu_cpld_writeb (void *opaque,
}
}
-static uint32_t taihu_cpld_readw (void *opaque, hwaddr addr)
-{
- uint32_t ret;
-
- ret = taihu_cpld_readb(opaque, addr) << 8;
- ret |= taihu_cpld_readb(opaque, addr + 1);
-
- return ret;
-}
-
-static void taihu_cpld_writew (void *opaque,
- hwaddr addr, uint32_t value)
-{
- taihu_cpld_writeb(opaque, addr, (value >> 8) & 0xFF);
- taihu_cpld_writeb(opaque, addr + 1, value & 0xFF);
-}
-
-static uint32_t taihu_cpld_readl (void *opaque, hwaddr addr)
-{
- uint32_t ret;
-
- ret = taihu_cpld_readb(opaque, addr) << 24;
- ret |= taihu_cpld_readb(opaque, addr + 1) << 16;
- ret |= taihu_cpld_readb(opaque, addr + 2) << 8;
- ret |= taihu_cpld_readb(opaque, addr + 3);
-
- return ret;
-}
-
-static void taihu_cpld_writel (void *opaque,
- hwaddr addr, uint32_t value)
-{
- taihu_cpld_writel(opaque, addr, (value >> 24) & 0xFF);
- taihu_cpld_writel(opaque, addr + 1, (value >> 16) & 0xFF);
- taihu_cpld_writel(opaque, addr + 2, (value >> 8) & 0xFF);
- taihu_cpld_writeb(opaque, addr + 3, value & 0xFF);
-}
-
static const MemoryRegionOps taihu_cpld_ops = {
- .old_mmio = {
- .read = { taihu_cpld_readb, taihu_cpld_readw, taihu_cpld_readl, },
- .write = { taihu_cpld_writeb, taihu_cpld_writew, taihu_cpld_writel, },
+ .read = taihu_cpld_read,
+ .write = taihu_cpld_write,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 1,
},
.endianness = DEVICE_NATIVE_ENDIAN,
};
@@ -579,7 +556,7 @@ static void taihu_405ep_init(MachineState *machine)
bios_name = BIOS_FILENAME;
bios = g_new(MemoryRegion, 1);
memory_region_init_ram(bios, NULL, "taihu_405ep.bios", BIOS_SIZE,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(bios);
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
if (filename) {
@@ -664,16 +641,24 @@ static void taihu_405ep_init(MachineState *machine)
#endif
}
-static QEMUMachine taihu_machine = {
- .name = "taihu",
- .desc = "taihu",
- .init = taihu_405ep_init,
+static void taihu_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "taihu";
+ mc->init = taihu_405ep_init;
+}
+
+static const TypeInfo taihu_type = {
+ .name = MACHINE_TYPE_NAME("taihu"),
+ .parent = TYPE_MACHINE,
+ .class_init = taihu_class_init,
};
static void ppc405_machine_init(void)
{
- qemu_register_machine(&ref405ep_machine);
- qemu_register_machine(&taihu_machine);
+ type_register_static(&ref405ep_type);
+ type_register_static(&taihu_type);
}
-machine_init(ppc405_machine_init);
+type_init(ppc405_machine_init)
diff --git a/qemu/hw/ppc/ppc405_uc.c b/qemu/hw/ppc/ppc405_uc.c
index c77434ae0..d6d3fc2c4 100644
--- a/qemu/hw/ppc/ppc405_uc.c
+++ b/qemu/hw/ppc/ppc405_uc.c
@@ -21,6 +21,10 @@
* 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-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "hw/ppc/ppc.h"
#include "hw/boards.h"
@@ -975,7 +979,7 @@ static void ppc405_ocm_init(CPUPPCState *env)
ocm = g_malloc0(sizeof(ppc405_ocm_t));
/* XXX: Size is 4096 or 0x04000000 */
memory_region_init_ram(&ocm->isarc_ram, NULL, "ppc405.ocm", 4096,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&ocm->isarc_ram);
memory_region_init_alias(&ocm->dsarc_ram, NULL, "ppc405.dsarc", &ocm->isarc_ram,
0, 4096);
@@ -1352,7 +1356,7 @@ static uint32_t ppc4xx_gpt_readl (void *opaque, hwaddr addr)
case 0x00:
/* Time base counter */
ret = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + gpt->tb_offset,
- gpt->tb_freq, get_ticks_per_sec());
+ gpt->tb_freq, NANOSECONDS_PER_SECOND);
break;
case 0x10:
/* Output enable */
@@ -1407,7 +1411,7 @@ static void ppc4xx_gpt_writel (void *opaque,
switch (addr) {
case 0x00:
/* Time base counter */
- gpt->tb_offset = muldiv64(value, get_ticks_per_sec(), gpt->tb_freq)
+ gpt->tb_offset = muldiv64(value, NANOSECONDS_PER_SECOND, gpt->tb_freq)
- qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
ppc4xx_gpt_compute_timer(gpt);
break;
diff --git a/qemu/hw/ppc/ppc440_bamboo.c b/qemu/hw/ppc/ppc440_bamboo.c
index 032fa803d..5c535b18a 100644
--- a/qemu/hw/ppc/ppc440_bamboo.c
+++ b/qemu/hw/ppc/ppc440_bamboo.c
@@ -11,7 +11,7 @@
*
*/
-#include "config.h"
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "net/net.h"
#include "hw/hw.h"
@@ -256,7 +256,8 @@ static void bamboo_init(MachineState *machine)
NULL, NULL);
if (success < 0) {
success = load_elf(kernel_filename, NULL, NULL, &elf_entry,
- &elf_lowaddr, NULL, 1, ELF_MACHINE, 0);
+ &elf_lowaddr, NULL, 1, PPC_ELF_MACHINE,
+ 0, 0);
entry = elf_entry;
loadaddr = elf_lowaddr;
}
@@ -288,20 +289,12 @@ static void bamboo_init(MachineState *machine)
exit(1);
}
}
-
- if (kvm_enabled())
- kvmppc_init();
}
-static QEMUMachine bamboo_machine = {
- .name = "bamboo",
- .desc = "bamboo",
- .init = bamboo_init,
-};
-
-static void bamboo_machine_init(void)
+static void bamboo_machine_init(MachineClass *mc)
{
- qemu_register_machine(&bamboo_machine);
+ mc->desc = "bamboo";
+ mc->init = bamboo_init;
}
-machine_init(bamboo_machine_init);
+DEFINE_MACHINE("bamboo", bamboo_machine_init)
diff --git a/qemu/hw/ppc/ppc4xx_devs.c b/qemu/hw/ppc/ppc4xx_devs.c
index 2f38ff7d2..7d59018fc 100644
--- a/qemu/hw/ppc/ppc4xx_devs.c
+++ b/qemu/hw/ppc/ppc4xx_devs.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/ppc/ppc.h"
#include "hw/ppc/ppc4xx.h"
diff --git a/qemu/hw/ppc/ppc4xx_pci.c b/qemu/hw/ppc/ppc4xx_pci.c
index 0bb3cdb46..683218e5c 100644
--- a/qemu/hw/ppc/ppc4xx_pci.c
+++ b/qemu/hw/ppc/ppc4xx_pci.c
@@ -19,6 +19,7 @@
/* This file implements emulation of the 32-bit PCI controller found in some
* 4xx SoCs, such as the 440EP. */
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/ppc/ppc.h"
#include "hw/ppc/ppc4xx.h"
diff --git a/qemu/hw/ppc/ppc_booke.c b/qemu/hw/ppc/ppc_booke.c
index 8b94da6b0..ab8d026c3 100644
--- a/qemu/hw/ppc/ppc_booke.c
+++ b/qemu/hw/ppc/ppc_booke.c
@@ -21,6 +21,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "hw/ppc/ppc.h"
#include "qemu/timer.h"
@@ -162,7 +165,7 @@ static void booke_update_fixed_timer(CPUPPCState *env,
ticks += delta_tick;
}
- *next = now + muldiv64(ticks, get_ticks_per_sec(), tb_env->tb_freq);
+ *next = now + muldiv64(ticks, NANOSECONDS_PER_SECOND, tb_env->tb_freq);
if ((*next < now) || (*next > INT64_MAX)) {
/* Overflow, so assume the biggest number the qemu timer supports. */
*next = INT64_MAX;
diff --git a/qemu/hw/ppc/ppce500_spin.c b/qemu/hw/ppc/ppce500_spin.c
index a99f7b039..76bd78bfd 100644
--- a/qemu/hw/ppc/ppce500_spin.c
+++ b/qemu/hw/ppc/ppce500_spin.c
@@ -27,6 +27,7 @@
*
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "sysemu/sysemu.h"
#include "hw/sysbus.h"
diff --git a/qemu/hw/ppc/prep.c b/qemu/hw/ppc/prep.c
index 45b5f62d6..3ffb85e60 100644
--- a/qemu/hw/ppc/prep.c
+++ b/qemu/hw/ppc/prep.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/timer/m48t59.h"
#include "hw/i386/pc.h"
@@ -33,6 +34,7 @@
#include "hw/pci/pci_host.h"
#include "hw/ppc/ppc.h"
#include "hw/boards.h"
+#include "qemu/error-report.h"
#include "qemu/log.h"
#include "hw/ide.h"
#include "hw/loader.h"
@@ -42,10 +44,9 @@
#include "sysemu/arch_init.h"
#include "sysemu/qtest.h"
#include "exec/address-spaces.h"
+#include "trace.h"
#include "elf.h"
-
-//#define HARD_DEBUG_PPC_IO
-//#define DEBUG_PPC_IO
+#include "qemu/cutils.h"
/* SMP is not enabled, for now */
#define MAX_CPUS 1
@@ -57,26 +58,6 @@
#define KERNEL_LOAD_ADDR 0x01000000
#define INITRD_LOAD_ADDR 0x01800000
-#if defined (HARD_DEBUG_PPC_IO) && !defined (DEBUG_PPC_IO)
-#define DEBUG_PPC_IO
-#endif
-
-#if defined (HARD_DEBUG_PPC_IO)
-#define PPC_IO_DPRINTF(fmt, ...) \
-do { \
- if (qemu_loglevel_mask(CPU_LOG_IOPORT)) { \
- qemu_log("%s: " fmt, __func__ , ## __VA_ARGS__); \
- } else { \
- printf("%s : " fmt, __func__ , ## __VA_ARGS__); \
- } \
-} while (0)
-#elif defined (DEBUG_PPC_IO)
-#define PPC_IO_DPRINTF(fmt, ...) \
-qemu_log_mask(CPU_LOG_IOPORT, fmt, ## __VA_ARGS__)
-#else
-#define PPC_IO_DPRINTF(fmt, ...) do { } while (0)
-#endif
-
/* Constants for devices init */
static const int ide_iobase[2] = { 0x1f0, 0x170 };
static const int ide_iobase2[2] = { 0x3f6, 0x376 };
@@ -199,8 +180,7 @@ static void PREP_io_800_writeb (void *opaque, uint32_t addr, uint32_t val)
{
sysctrl_t *sysctrl = opaque;
- PPC_IO_DPRINTF("0x%08" PRIx32 " => 0x%02" PRIx32 "\n",
- addr - PPC_IO_BASE, val);
+ trace_prep_io_800_writeb(addr - PPC_IO_BASE, val);
switch (addr) {
case 0x0092:
/* Special port 92 */
@@ -327,8 +307,7 @@ static uint32_t PREP_io_800_readb (void *opaque, uint32_t addr)
printf("ERROR: unaffected IO port: %04" PRIx32 " read\n", addr);
break;
}
- PPC_IO_DPRINTF("0x%08" PRIx32 " <= 0x%02" PRIx32 "\n",
- addr - PPC_IO_BASE, retval);
+ trace_prep_io_800_readb(addr - PPC_IO_BASE, retval);
return retval;
}
@@ -336,15 +315,6 @@ static uint32_t PREP_io_800_readb (void *opaque, uint32_t addr)
#define NVRAM_SIZE 0x2000
-static void cpu_request_exit(void *opaque, int irq, int level)
-{
- CPUState *cpu = current_cpu;
-
- if (cpu && level) {
- cpu_exit(cpu);
- }
-}
-
static void ppc_prep_reset(void *opaque)
{
PowerPCCPU *cpu = opaque;
@@ -565,7 +535,7 @@ static void ppc_prep_init(MachineState *machine)
kernel_size = load_image_targphys(kernel_filename, kernel_base,
ram_size - kernel_base);
if (kernel_size < 0) {
- hw_error("qemu: could not load kernel '%s'\n", kernel_filename);
+ error_report("could not load kernel '%s'", kernel_filename);
exit(1);
}
/* load initrd */
@@ -574,8 +544,9 @@ static void ppc_prep_init(MachineState *machine)
initrd_size = load_image_targphys(initrd_filename, initrd_base,
ram_size - initrd_base);
if (initrd_size < 0) {
- hw_error("qemu: could not load initial ram disk '%s'\n",
- initrd_filename);
+ error_report("could not load initial ram disk '%s'",
+ initrd_filename);
+ exit(1);
}
} else {
initrd_base = 0;
@@ -602,7 +573,8 @@ static void ppc_prep_init(MachineState *machine)
}
if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) {
- hw_error("Only 6xx bus is supported on PREP machine\n");
+ error_report("Only 6xx bus is supported on PREP machine");
+ exit(1);
}
dev = qdev_create(NULL, "raven-pcihost");
@@ -610,7 +582,7 @@ static void ppc_prep_init(MachineState *machine)
bios_name = BIOS_FILENAME;
}
qdev_prop_set_string(dev, "bios-name", bios_name);
- qdev_prop_set_uint32(dev, "elf-machine", ELF_MACHINE);
+ qdev_prop_set_uint32(dev, "elf-machine", PPC_ELF_MACHINE);
pcihost = PCI_HOST_BRIDGE(dev);
object_property_add_child(qdev_get_machine(), "raven", OBJECT(dev), NULL);
qdev_init_nofail(dev);
@@ -626,8 +598,6 @@ static void ppc_prep_init(MachineState *machine)
cpu = POWERPC_CPU(first_cpu);
qdev_connect_gpio_out(&pci->qdev, 0,
cpu->env.irq_inputs[PPC6xx_INPUT_INT]);
- qdev_connect_gpio_out(&pci->qdev, 1,
- qemu_allocate_irq(cpu_request_exit, NULL, 0));
sysbus_connect_irq(&pcihost->busdev, 0, qdev_get_gpio_in(&pci->qdev, 9));
sysbus_connect_irq(&pcihost->busdev, 1, qdev_get_gpio_in(&pci->qdev, 11));
sysbus_connect_irq(&pcihost->busdev, 2, qdev_get_gpio_in(&pci->qdev, 9));
@@ -698,17 +668,12 @@ static void ppc_prep_init(MachineState *machine)
graphic_width, graphic_height, graphic_depth);
}
-static QEMUMachine prep_machine = {
- .name = "prep",
- .desc = "PowerPC PREP platform",
- .init = ppc_prep_init,
- .max_cpus = MAX_CPUS,
- .default_boot_order = "cad",
-};
-
-static void prep_machine_init(void)
+static void prep_machine_init(MachineClass *mc)
{
- qemu_register_machine(&prep_machine);
+ mc->desc = "PowerPC PREP platform";
+ mc->init = ppc_prep_init;
+ mc->max_cpus = MAX_CPUS;
+ mc->default_boot_order = "cad";
}
-machine_init(prep_machine_init);
+DEFINE_MACHINE("prep", prep_machine_init)
diff --git a/qemu/hw/ppc/spapr.c b/qemu/hw/ppc/spapr.c
index a6f19473c..b69995e0d 100644
--- a/qemu/hw/ppc/spapr.c
+++ b/qemu/hw/ppc/spapr.c
@@ -24,15 +24,19 @@
* THE SOFTWARE.
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "sysemu/sysemu.h"
#include "sysemu/numa.h"
#include "hw/hw.h"
#include "hw/fw-path-provider.h"
#include "elf.h"
#include "net/net.h"
+#include "sysemu/device_tree.h"
#include "sysemu/block-backend.h"
#include "sysemu/cpus.h"
#include "sysemu/kvm.h"
+#include "sysemu/device_tree.h"
#include "kvm_ppc.h"
#include "migration/migration.h"
#include "mmu-hash64.h"
@@ -60,6 +64,7 @@
#include "hw/nmi.h"
#include "hw/compat.h"
+#include "qemu/cutils.h"
#include <libfdt.h>
@@ -73,7 +78,7 @@
*
* We load our kernel at 4M, leaving space for SLOF initial image
*/
-#define FDT_MAX_SIZE 0x40000
+#define FDT_MAX_SIZE 0x100000
#define RTAS_MAX_SIZE 0x10000
#define RTAS_MAX_ADDR 0x80000000 /* RTAS must stay below that */
#define FW_MAX_SIZE 0x400000
@@ -85,8 +90,6 @@
#define TIMEBASE_FREQ 512000000ULL
-#define MAX_CPUS 255
-
#define PHANDLE_XICP 0x00001111
#define HTAB_SIZE(spapr) (1ULL << ((spapr)->htab_shift))
@@ -110,7 +113,7 @@ static XICSState *try_create_xics(const char *type, int nr_servers,
}
static XICSState *xics_system_init(MachineState *machine,
- int nr_servers, int nr_irqs)
+ int nr_servers, int nr_irqs, Error **errp)
{
XICSState *icp = NULL;
@@ -121,13 +124,15 @@ static XICSState *xics_system_init(MachineState *machine,
icp = try_create_xics(TYPE_KVM_XICS, nr_servers, nr_irqs, &err);
}
if (machine_kernel_irqchip_required(machine) && !icp) {
- error_report("kernel_irqchip requested but unavailable: %s",
- error_get_pretty(err));
+ error_reportf_err(err,
+ "kernel_irqchip requested but unavailable: ");
+ } else {
+ error_free(err);
}
}
if (!icp) {
- icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs, &error_abort);
+ icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs, errp);
}
return icp;
@@ -373,8 +378,16 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base,
qemu_uuid[14], qemu_uuid[15]);
_FDT((fdt_property_string(fdt, "vm,uuid", buf)));
+ if (qemu_uuid_set) {
+ _FDT((fdt_property_string(fdt, "system-id", buf)));
+ }
g_free(buf);
+ if (qemu_get_vm_name()) {
+ _FDT((fdt_property_string(fdt, "ibm,partition-name",
+ qemu_get_vm_name())));
+ }
+
_FDT((fdt_property_cell(fdt, "#address-cells", 0x2)));
_FDT((fdt_property_cell(fdt, "#size-cells", 0x2)));
@@ -427,6 +440,10 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base,
_FDT((fdt_property_cell(fdt, "rtas-event-scan-rate",
RTAS_EVENT_SCAN_RATE)));
+ if (msi_nonbroken) {
+ _FDT((fdt_property(fdt, "ibm,change-msix-capable", NULL, 0)));
+ }
+
/*
* According to PAPR, rtas ibm,os-term does not guarantee a return
* back to the guest cpu.
@@ -481,10 +498,11 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base,
* Older KVM versions with older guest kernels were broken with the
* magic page, don't allow the guest to map it.
*/
- kvmppc_get_hypercall(first_cpu->env_ptr, hypercall,
- sizeof(hypercall));
- _FDT((fdt_property(fdt, "hcall-instructions", hypercall,
- sizeof(hypercall))));
+ if (!kvmppc_get_hypercall(first_cpu->env_ptr, hypercall,
+ sizeof(hypercall))) {
+ _FDT((fdt_property(fdt, "hcall-instructions", hypercall,
+ sizeof(hypercall))));
+ }
}
_FDT((fdt_end_node(fdt)));
}
@@ -495,44 +513,7 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base,
return fdt;
}
-int spapr_h_cas_compose_response(sPAPRMachineState *spapr,
- target_ulong addr, target_ulong size)
-{
- void *fdt, *fdt_skel;
- sPAPRDeviceTreeUpdateHeader hdr = { .version_id = 1 };
-
- size -= sizeof(hdr);
-
- /* Create sceleton */
- fdt_skel = g_malloc0(size);
- _FDT((fdt_create(fdt_skel, size)));
- _FDT((fdt_begin_node(fdt_skel, "")));
- _FDT((fdt_end_node(fdt_skel)));
- _FDT((fdt_finish(fdt_skel)));
- fdt = g_malloc0(size);
- _FDT((fdt_open_into(fdt_skel, fdt, size)));
- g_free(fdt_skel);
-
- /* Fix skeleton up */
- _FDT((spapr_fixup_cpu_dt(fdt, spapr)));
-
- /* Pack resulting tree */
- _FDT((fdt_pack(fdt)));
-
- if (fdt_totalsize(fdt) + sizeof(hdr) > size) {
- trace_spapr_cas_failed(size);
- return -1;
- }
-
- cpu_physical_memory_write(addr, &hdr, sizeof(hdr));
- cpu_physical_memory_write(addr + sizeof(hdr), fdt, fdt_totalsize(fdt));
- trace_spapr_cas_continue(fdt_totalsize(fdt) + sizeof(hdr));
- g_free(fdt);
-
- return 0;
-}
-
-static void spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start,
+static int spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start,
hwaddr size)
{
uint32_t associativity[] = {
@@ -555,6 +536,7 @@ static void spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start,
sizeof(mem_reg_property))));
_FDT((fdt_setprop(fdt, off, "ibm,associativity", associativity,
sizeof(associativity))));
+ return off;
}
static int spapr_populate_memory(sPAPRMachineState *spapr, void *fdt)
@@ -620,11 +602,27 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset,
uint32_t cpufreq = kvm_enabled() ? kvmppc_get_clockfreq() : 1000000000;
uint32_t page_sizes_prop[64];
size_t page_sizes_prop_size;
- QemuOpts *opts = qemu_opts_find(qemu_find_opts("smp-opts"), NULL);
- unsigned sockets = opts ? qemu_opt_get_number(opts, "sockets", 0) : 0;
- uint32_t cpus_per_socket = sockets ? (smp_cpus / sockets) : 1;
+ uint32_t vcpus_per_socket = smp_threads * smp_cores;
uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)};
+ /* Note: we keep CI large pages off for now because a 64K capable guest
+ * provisioned with large pages might otherwise try to map a qemu
+ * framebuffer (or other kind of memory mapped PCI BAR) using 64K pages
+ * even if that qemu runs on a 4k host.
+ *
+ * We can later add this bit back when we are confident this is not
+ * an issue (!HV KVM or 64K host)
+ */
+ uint8_t pa_features_206[] = { 6, 0,
+ 0xf6, 0x1f, 0xc7, 0x00, 0x80, 0xc0 };
+ uint8_t pa_features_207[] = { 24, 0,
+ 0xf6, 0x1f, 0xc7, 0xc0, 0x80, 0xf0,
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00,
+ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00 };
+ uint8_t *pa_features;
+ size_t pa_size;
+
_FDT((fdt_setprop_cell(fdt, offset, "reg", index)));
_FDT((fdt_setprop_string(fdt, offset, "device_type", "cpu")));
@@ -653,6 +651,7 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset,
_FDT((fdt_setprop_cell(fdt, offset, "timebase-frequency", tbfreq)));
_FDT((fdt_setprop_cell(fdt, offset, "clock-frequency", cpufreq)));
+ _FDT((fdt_setprop_cell(fdt, offset, "slb-size", env->slb_nr)));
_FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", env->slb_nr)));
_FDT((fdt_setprop_string(fdt, offset, "status", "okay")));
_FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0)));
@@ -690,8 +689,21 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset,
page_sizes_prop, page_sizes_prop_size)));
}
+ /* Do the ibm,pa-features property, adjust it for ci-large-pages */
+ if (env->mmu_model == POWERPC_MMU_2_06) {
+ pa_features = pa_features_206;
+ pa_size = sizeof(pa_features_206);
+ } else /* env->mmu_model == POWERPC_MMU_2_07 */ {
+ pa_features = pa_features_207;
+ pa_size = sizeof(pa_features_207);
+ }
+ if (env->ci_large_pages) {
+ pa_features[3] |= 0x20;
+ }
+ _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", pa_features, pa_size)));
+
_FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id",
- cs->cpu_index / cpus_per_socket)));
+ cs->cpu_index / vcpus_per_socket)));
_FDT((fdt_setprop(fdt, offset, "ibm,pft-size",
pft_size_prop, sizeof(pft_size_prop))));
@@ -738,12 +750,162 @@ static void spapr_populate_cpus_dt_node(void *fdt, sPAPRMachineState *spapr)
}
+/*
+ * Adds ibm,dynamic-reconfiguration-memory node.
+ * Refer to docs/specs/ppc-spapr-hotplug.txt for the documentation
+ * of this device tree node.
+ */
+static int spapr_populate_drconf_memory(sPAPRMachineState *spapr, void *fdt)
+{
+ MachineState *machine = MACHINE(spapr);
+ int ret, i, offset;
+ uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE;
+ uint32_t prop_lmb_size[] = {0, cpu_to_be32(lmb_size)};
+ uint32_t nr_lmbs = (machine->maxram_size - machine->ram_size)/lmb_size;
+ uint32_t *int_buf, *cur_index, buf_len;
+ int nr_nodes = nb_numa_nodes ? nb_numa_nodes : 1;
+
+ /*
+ * Don't create the node if there are no DR LMBs.
+ */
+ if (!nr_lmbs) {
+ return 0;
+ }
+
+ /*
+ * Allocate enough buffer size to fit in ibm,dynamic-memory
+ * or ibm,associativity-lookup-arrays
+ */
+ buf_len = MAX(nr_lmbs * SPAPR_DR_LMB_LIST_ENTRY_SIZE + 1, nr_nodes * 4 + 2)
+ * sizeof(uint32_t);
+ cur_index = int_buf = g_malloc0(buf_len);
+
+ offset = fdt_add_subnode(fdt, 0, "ibm,dynamic-reconfiguration-memory");
+
+ ret = fdt_setprop(fdt, offset, "ibm,lmb-size", prop_lmb_size,
+ sizeof(prop_lmb_size));
+ if (ret < 0) {
+ goto out;
+ }
+
+ ret = fdt_setprop_cell(fdt, offset, "ibm,memory-flags-mask", 0xff);
+ if (ret < 0) {
+ goto out;
+ }
+
+ ret = fdt_setprop_cell(fdt, offset, "ibm,memory-preservation-time", 0x0);
+ if (ret < 0) {
+ goto out;
+ }
+
+ /* ibm,dynamic-memory */
+ int_buf[0] = cpu_to_be32(nr_lmbs);
+ cur_index++;
+ for (i = 0; i < nr_lmbs; i++) {
+ sPAPRDRConnector *drc;
+ sPAPRDRConnectorClass *drck;
+ uint64_t addr = i * lmb_size + spapr->hotplug_memory.base;;
+ uint32_t *dynamic_memory = cur_index;
+
+ drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB,
+ addr/lmb_size);
+ g_assert(drc);
+ drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+
+ dynamic_memory[0] = cpu_to_be32(addr >> 32);
+ dynamic_memory[1] = cpu_to_be32(addr & 0xffffffff);
+ dynamic_memory[2] = cpu_to_be32(drck->get_index(drc));
+ dynamic_memory[3] = cpu_to_be32(0); /* reserved */
+ dynamic_memory[4] = cpu_to_be32(numa_get_node(addr, NULL));
+ if (addr < machine->ram_size ||
+ memory_region_present(get_system_memory(), addr)) {
+ dynamic_memory[5] = cpu_to_be32(SPAPR_LMB_FLAGS_ASSIGNED);
+ } else {
+ dynamic_memory[5] = cpu_to_be32(0);
+ }
+
+ cur_index += SPAPR_DR_LMB_LIST_ENTRY_SIZE;
+ }
+ ret = fdt_setprop(fdt, offset, "ibm,dynamic-memory", int_buf, buf_len);
+ if (ret < 0) {
+ goto out;
+ }
+
+ /* ibm,associativity-lookup-arrays */
+ cur_index = int_buf;
+ int_buf[0] = cpu_to_be32(nr_nodes);
+ int_buf[1] = cpu_to_be32(4); /* Number of entries per associativity list */
+ cur_index += 2;
+ for (i = 0; i < nr_nodes; i++) {
+ uint32_t associativity[] = {
+ cpu_to_be32(0x0),
+ cpu_to_be32(0x0),
+ cpu_to_be32(0x0),
+ cpu_to_be32(i)
+ };
+ memcpy(cur_index, associativity, sizeof(associativity));
+ cur_index += 4;
+ }
+ ret = fdt_setprop(fdt, offset, "ibm,associativity-lookup-arrays", int_buf,
+ (cur_index - int_buf) * sizeof(uint32_t));
+out:
+ g_free(int_buf);
+ return ret;
+}
+
+int spapr_h_cas_compose_response(sPAPRMachineState *spapr,
+ target_ulong addr, target_ulong size,
+ bool cpu_update, bool memory_update)
+{
+ void *fdt, *fdt_skel;
+ sPAPRDeviceTreeUpdateHeader hdr = { .version_id = 1 };
+ sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(qdev_get_machine());
+
+ size -= sizeof(hdr);
+
+ /* Create sceleton */
+ fdt_skel = g_malloc0(size);
+ _FDT((fdt_create(fdt_skel, size)));
+ _FDT((fdt_begin_node(fdt_skel, "")));
+ _FDT((fdt_end_node(fdt_skel)));
+ _FDT((fdt_finish(fdt_skel)));
+ fdt = g_malloc0(size);
+ _FDT((fdt_open_into(fdt_skel, fdt, size)));
+ g_free(fdt_skel);
+
+ /* Fixup cpu nodes */
+ if (cpu_update) {
+ _FDT((spapr_fixup_cpu_dt(fdt, spapr)));
+ }
+
+ /* Generate ibm,dynamic-reconfiguration-memory node if required */
+ if (memory_update && smc->dr_lmb_enabled) {
+ _FDT((spapr_populate_drconf_memory(spapr, fdt)));
+ }
+
+ /* Pack resulting tree */
+ _FDT((fdt_pack(fdt)));
+
+ if (fdt_totalsize(fdt) + sizeof(hdr) > size) {
+ trace_spapr_cas_failed(size);
+ return -1;
+ }
+
+ cpu_physical_memory_write(addr, &hdr, sizeof(hdr));
+ cpu_physical_memory_write(addr + sizeof(hdr), fdt, fdt_totalsize(fdt));
+ trace_spapr_cas_continue(fdt_totalsize(fdt) + sizeof(hdr));
+ g_free(fdt);
+
+ return 0;
+}
+
static void spapr_finalize_fdt(sPAPRMachineState *spapr,
hwaddr fdt_addr,
hwaddr rtas_addr,
hwaddr rtas_size)
{
MachineState *machine = MACHINE(qdev_get_machine());
+ sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine);
const char *boot_device = machine->boot_order;
int ret, i;
size_t cb = 0;
@@ -768,13 +930,20 @@ static void spapr_finalize_fdt(sPAPRMachineState *spapr,
exit(1);
}
- QLIST_FOREACH(phb, &spapr->phbs, list) {
- ret = spapr_populate_pci_dt(phb, PHANDLE_XICP, fdt);
+ if (object_resolve_path_type("", TYPE_SPAPR_RNG, NULL)) {
+ ret = spapr_rng_populate_dt(fdt);
+ if (ret < 0) {
+ fprintf(stderr, "could not set up rng device in the fdt\n");
+ exit(1);
+ }
}
- if (ret < 0) {
- fprintf(stderr, "couldn't setup PCI devices in fdt\n");
- exit(1);
+ QLIST_FOREACH(phb, &spapr->phbs, list) {
+ ret = spapr_populate_pci_dt(phb, PHANDLE_XICP, fdt);
+ if (ret < 0) {
+ error_report("couldn't setup PCI devices in fdt");
+ exit(1);
+ }
}
/* RTAS */
@@ -814,6 +983,10 @@ static void spapr_finalize_fdt(sPAPRMachineState *spapr,
spapr_populate_chosen_stdout(fdt, spapr->vio_bus);
}
+ if (smc->dr_lmb_enabled) {
+ _FDT(spapr_drc_populate_dt(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_LMB));
+ }
+
_FDT((fdt_pack(fdt)));
if (fdt_totalsize(fdt) > FDT_MAX_SIZE) {
@@ -822,6 +995,7 @@ static void spapr_finalize_fdt(sPAPRMachineState *spapr,
exit(1);
}
+ qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt));
cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt));
g_free(bootlist);
@@ -851,45 +1025,93 @@ static void emulate_spapr_hypercall(PowerPCCPU *cpu)
#define CLEAN_HPTE(_hpte) ((*(uint64_t *)(_hpte)) &= tswap64(~HPTE64_V_HPTE_DIRTY))
#define DIRTY_HPTE(_hpte) ((*(uint64_t *)(_hpte)) |= tswap64(HPTE64_V_HPTE_DIRTY))
-static void spapr_reset_htab(sPAPRMachineState *spapr)
+/*
+ * Get the fd to access the kernel htab, re-opening it if necessary
+ */
+static int get_htab_fd(sPAPRMachineState *spapr)
{
- long shift;
- int index;
+ if (spapr->htab_fd >= 0) {
+ return spapr->htab_fd;
+ }
+
+ spapr->htab_fd = kvmppc_get_htab_fd(false);
+ if (spapr->htab_fd < 0) {
+ error_report("Unable to open fd for reading hash table from KVM: %s",
+ strerror(errno));
+ }
- /* allocate hash page table. For now we always make this 16mb,
- * later we should probably make it scale to the size of guest
- * RAM */
+ return spapr->htab_fd;
+}
- shift = kvmppc_reset_htab(spapr->htab_shift);
+static void close_htab_fd(sPAPRMachineState *spapr)
+{
+ if (spapr->htab_fd >= 0) {
+ close(spapr->htab_fd);
+ }
+ spapr->htab_fd = -1;
+}
- if (shift > 0) {
- /* Kernel handles htab, we don't need to allocate one */
- spapr->htab_shift = shift;
- kvmppc_kern_htab = true;
+static int spapr_hpt_shift_for_ramsize(uint64_t ramsize)
+{
+ int shift;
+
+ /* We aim for a hash table of size 1/128 the size of RAM (rounded
+ * up). The PAPR recommendation is actually 1/64 of RAM size, but
+ * that's much more than is needed for Linux guests */
+ shift = ctz64(pow2ceil(ramsize)) - 7;
+ shift = MAX(shift, 18); /* Minimum architected size */
+ shift = MIN(shift, 46); /* Maximum architected size */
+ return shift;
+}
- /* Tell readers to update their file descriptor */
- if (spapr->htab_fd >= 0) {
- spapr->htab_fd_stale = true;
+static void spapr_reallocate_hpt(sPAPRMachineState *spapr, int shift,
+ Error **errp)
+{
+ long rc;
+
+ /* Clean up any HPT info from a previous boot */
+ g_free(spapr->htab);
+ spapr->htab = NULL;
+ spapr->htab_shift = 0;
+ close_htab_fd(spapr);
+
+ rc = kvmppc_reset_htab(shift);
+ if (rc < 0) {
+ /* kernel-side HPT needed, but couldn't allocate one */
+ error_setg_errno(errp, errno,
+ "Failed to allocate KVM HPT of order %d (try smaller maxmem?)",
+ shift);
+ /* This is almost certainly fatal, but if the caller really
+ * wants to carry on with shift == 0, it's welcome to try */
+ } else if (rc > 0) {
+ /* kernel-side HPT allocated */
+ if (rc != shift) {
+ error_setg(errp,
+ "Requested order %d HPT, but kernel allocated order %ld (try smaller maxmem?)",
+ shift, rc);
}
+
+ spapr->htab_shift = shift;
+ spapr->htab = NULL;
} else {
+ /* kernel-side HPT not needed, allocate in userspace instead */
+ size_t size = 1ULL << shift;
+ int i;
+
+ spapr->htab = qemu_memalign(size, size);
if (!spapr->htab) {
- /* Allocate an htab if we don't yet have one */
- spapr->htab = qemu_memalign(HTAB_SIZE(spapr), HTAB_SIZE(spapr));
+ error_setg_errno(errp, errno,
+ "Could not allocate HPT of order %d", shift);
+ return;
}
- /* And clear it */
- memset(spapr->htab, 0, HTAB_SIZE(spapr));
+ memset(spapr->htab, 0, size);
+ spapr->htab_shift = shift;
- for (index = 0; index < HTAB_SIZE(spapr) / HASH_PTE_SIZE_64; index++) {
- DIRTY_HPTE(HPTE(spapr->htab, index));
+ for (i = 0; i < size / HASH_PTE_SIZE_64; i++) {
+ DIRTY_HPTE(HPTE(spapr->htab, i));
}
}
-
- /* Update the RMA size if necessary */
- if (spapr->vrma_adjust) {
- spapr->rma_size = kvmppc_rma_size(spapr_node0_size(),
- spapr->htab_shift);
- }
}
static int find_unknown_sysbus_device(SysBusDevice *sbdev, void *opaque)
@@ -909,39 +1131,26 @@ static int find_unknown_sysbus_device(SysBusDevice *sbdev, void *opaque)
return 0;
}
-/*
- * A guest reset will cause spapr->htab_fd to become stale if being used.
- * Reopen the file descriptor to make sure the whole HTAB is properly read.
- */
-static int spapr_check_htab_fd(sPAPRMachineState *spapr)
-{
- int rc = 0;
-
- if (spapr->htab_fd_stale) {
- close(spapr->htab_fd);
- spapr->htab_fd = kvmppc_get_htab_fd(false);
- if (spapr->htab_fd < 0) {
- error_report("Unable to open fd for reading hash table from KVM: "
- "%s", strerror(errno));
- rc = -1;
- }
- spapr->htab_fd_stale = false;
- }
-
- return rc;
-}
-
static void ppc_spapr_reset(void)
{
- sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
+ MachineState *machine = MACHINE(qdev_get_machine());
+ sPAPRMachineState *spapr = SPAPR_MACHINE(machine);
PowerPCCPU *first_ppc_cpu;
uint32_t rtas_limit;
/* Check for unknown sysbus devices */
foreach_dynamic_sysbus_device(find_unknown_sysbus_device, NULL);
- /* Reset the hash table & recalc the RMA */
- spapr_reset_htab(spapr);
+ /* Allocate and/or reset the hash page table */
+ spapr_reallocate_hpt(spapr,
+ spapr_hpt_shift_for_ramsize(machine->maxram_size),
+ &error_fatal);
+
+ /* Update the RMA size if necessary */
+ if (spapr->vrma_adjust) {
+ spapr->rma_size = kvmppc_rma_size(spapr_node0_size(),
+ spapr->htab_shift);
+ }
qemu_devices_reset();
@@ -987,24 +1196,8 @@ static void spapr_cpu_reset(void *opaque)
env->spr[SPR_HIOR] = 0;
- env->external_htab = (uint8_t *)spapr->htab;
- if (kvm_enabled() && !env->external_htab) {
- /*
- * HV KVM, set external_htab to 1 so our ppc_hash64_load_hpte*
- * functions do the right thing.
- */
- env->external_htab = (void *)1;
- }
- env->htab_base = -1;
- /*
- * htab_mask is the mask used to normalize hash value to PTEG index.
- * htab_shift is log2 of hash table size.
- * We have 8 hpte per group, and each hpte is 16 bytes.
- * ie have 128 bytes per hpte entry.
- */
- env->htab_mask = (1ULL << (spapr->htab_shift - 7)) - 1;
- env->spr[SPR_SDR1] = (target_ulong)(uintptr_t)spapr->htab |
- (spapr->htab_shift - 18);
+ ppc_hash64_set_external_hpt(cpu, spapr->htab, spapr->htab_shift,
+ &error_fatal);
}
static void spapr_create_nvram(sPAPRMachineState *spapr)
@@ -1013,7 +1206,8 @@ static void spapr_create_nvram(sPAPRMachineState *spapr)
DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0);
if (dinfo) {
- qdev_prop_set_drive_nofail(dev, "drive", blk_by_legacy_dinfo(dinfo));
+ qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo),
+ &error_fatal);
}
qdev_init_nofail(dev);
@@ -1033,7 +1227,7 @@ static void spapr_rtc_create(sPAPRMachineState *spapr)
}
/* Returns whether we want to use VGA or not */
-static int spapr_vga_init(PCIBus *pci_bus)
+static bool spapr_vga_init(PCIBus *pci_bus, Error **errp)
{
switch (vga_interface_type) {
case VGA_NONE:
@@ -1041,11 +1235,12 @@ static int spapr_vga_init(PCIBus *pci_bus)
case VGA_DEVICE:
return true;
case VGA_STD:
+ case VGA_VIRTIO:
return pci_vga_init(pci_bus) != NULL;
default:
- fprintf(stderr, "This vga model is not supported,"
- "currently it only supports -vga std\n");
- exit(0);
+ error_setg(errp,
+ "Unsupported VGA mode, only -vga std or -vga virtio is supported");
+ return false;
}
}
@@ -1099,14 +1294,6 @@ static int htab_save_setup(QEMUFile *f, void *opaque)
spapr->htab_first_pass = true;
} else {
assert(kvm_enabled());
-
- spapr->htab_fd = kvmppc_get_htab_fd(false);
- spapr->htab_fd_stale = false;
- if (spapr->htab_fd < 0) {
- fprintf(stderr, "Unable to open fd for reading hash table from KVM: %s\n",
- strerror(errno));
- return -1;
- }
}
@@ -1116,6 +1303,7 @@ static int htab_save_setup(QEMUFile *f, void *opaque)
static void htab_save_first_pass(QEMUFile *f, sPAPRMachineState *spapr,
int64_t max_ns)
{
+ bool has_timeout = max_ns != -1;
int htabslots = HTAB_SIZE(spapr) / HASH_PTE_SIZE_64;
int index = spapr->htab_save_index;
int64_t starttime = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
@@ -1149,7 +1337,8 @@ static void htab_save_first_pass(QEMUFile *f, sPAPRMachineState *spapr,
qemu_put_buffer(f, HPTE(spapr->htab, chunkstart),
HASH_PTE_SIZE_64 * n_valid);
- if ((qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - starttime) > max_ns) {
+ if (has_timeout &&
+ (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - starttime) > max_ns) {
break;
}
}
@@ -1246,6 +1435,7 @@ static int htab_save_later_pass(QEMUFile *f, sPAPRMachineState *spapr,
static int htab_save_iterate(QEMUFile *f, void *opaque)
{
sPAPRMachineState *spapr = opaque;
+ int fd;
int rc = 0;
/* Iteration header */
@@ -1254,13 +1444,12 @@ static int htab_save_iterate(QEMUFile *f, void *opaque)
if (!spapr->htab) {
assert(kvm_enabled());
- rc = spapr_check_htab_fd(spapr);
- if (rc < 0) {
- return rc;
+ fd = get_htab_fd(spapr);
+ if (fd < 0) {
+ return fd;
}
- rc = kvmppc_save_htab(f, spapr->htab_fd,
- MAX_KVM_BUF_SIZE, MAX_ITERATION_NS);
+ rc = kvmppc_save_htab(f, fd, MAX_KVM_BUF_SIZE, MAX_ITERATION_NS);
if (rc < 0) {
return rc;
}
@@ -1281,6 +1470,7 @@ static int htab_save_iterate(QEMUFile *f, void *opaque)
static int htab_save_complete(QEMUFile *f, void *opaque)
{
sPAPRMachineState *spapr = opaque;
+ int fd;
/* Iteration header */
qemu_put_be32(f, 0);
@@ -1290,18 +1480,20 @@ static int htab_save_complete(QEMUFile *f, void *opaque)
assert(kvm_enabled());
- rc = spapr_check_htab_fd(spapr);
- if (rc < 0) {
- return rc;
+ fd = get_htab_fd(spapr);
+ if (fd < 0) {
+ return fd;
}
- rc = kvmppc_save_htab(f, spapr->htab_fd, MAX_KVM_BUF_SIZE, -1);
+ rc = kvmppc_save_htab(f, fd, MAX_KVM_BUF_SIZE, -1);
if (rc < 0) {
return rc;
}
- close(spapr->htab_fd);
- spapr->htab_fd = -1;
+ close_htab_fd(spapr);
} else {
+ if (spapr->htab_first_pass) {
+ htab_save_first_pass(f, spapr, -1);
+ }
htab_save_later_pass(f, spapr, -1);
}
@@ -1320,15 +1512,19 @@ static int htab_load(QEMUFile *f, void *opaque, int version_id)
int fd = -1;
if (version_id < 1 || version_id > 1) {
- fprintf(stderr, "htab_load() bad version\n");
+ error_report("htab_load() bad version");
return -EINVAL;
}
section_hdr = qemu_get_be32(f);
if (section_hdr) {
- /* First section, just the hash shift */
- if (spapr->htab_shift != section_hdr) {
+ Error *local_err = NULL;
+
+ /* First section gives the htab size */
+ spapr_reallocate_hpt(spapr, section_hdr, &local_err);
+ if (local_err) {
+ error_report_err(local_err);
return -EINVAL;
}
return 0;
@@ -1339,8 +1535,8 @@ static int htab_load(QEMUFile *f, void *opaque, int version_id)
fd = kvmppc_get_htab_fd(true);
if (fd < 0) {
- fprintf(stderr, "Unable to open fd to restore KVM hash table: %s\n",
- strerror(errno));
+ error_report("Unable to open fd to restore KVM hash table: %s",
+ strerror(errno));
}
}
@@ -1360,9 +1556,9 @@ static int htab_load(QEMUFile *f, void *opaque, int version_id)
if ((index + n_valid + n_invalid) >
(HTAB_SIZE(spapr) / HASH_PTE_SIZE_64)) {
/* Bad index in stream */
- fprintf(stderr, "htab_load() bad index %d (%hd+%hd entries) "
- "in htab stream (htab_shift=%d)\n", index, n_valid, n_invalid,
- spapr->htab_shift);
+ error_report(
+ "htab_load() bad index %d (%hd+%hd entries) in htab stream (htab_shift=%d)",
+ index, n_valid, n_invalid, spapr->htab_shift);
return -EINVAL;
}
@@ -1398,7 +1594,7 @@ static int htab_load(QEMUFile *f, void *opaque, int version_id)
static SaveVMHandlers savevm_htab_handlers = {
.save_live_setup = htab_save_setup,
.save_live_iterate = htab_save_iterate,
- .save_live_complete = htab_save_complete,
+ .save_live_complete_precopy = htab_save_complete,
.load_state = htab_load,
};
@@ -1409,26 +1605,24 @@ static void spapr_boot_set(void *opaque, const char *boot_device,
machine->boot_order = g_strdup(boot_device);
}
-static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu)
+static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu,
+ Error **errp)
{
CPUPPCState *env = &cpu->env;
/* Set time-base frequency to 512 MHz */
cpu_ppc_tb_init(env, TIMEBASE_FREQ);
- /* PAPR always has exception vectors in RAM not ROM. To ensure this,
- * MSR[IP] should never be set.
- */
- env->msr_mask &= ~(1 << 6);
-
- /* Tell KVM that we're in PAPR mode */
- if (kvm_enabled()) {
- kvmppc_set_papr(cpu);
- }
+ /* Enable PAPR mode in TCG or KVM */
+ cpu_ppc_set_papr(cpu);
if (cpu->max_compat) {
- if (ppc_set_compat(cpu, cpu->max_compat) < 0) {
- exit(1);
+ Error *local_err = NULL;
+
+ ppc_set_compat(cpu, cpu->max_compat, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
}
}
@@ -1437,10 +1631,84 @@ static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu)
qemu_register_reset(spapr_cpu_reset, cpu);
}
+/*
+ * Reset routine for LMB DR devices.
+ *
+ * Unlike PCI DR devices, LMB DR devices explicitly register this reset
+ * routine. Reset for PCI DR devices will be handled by PHB reset routine
+ * when it walks all its children devices. LMB devices reset occurs
+ * as part of spapr_ppc_reset().
+ */
+static void spapr_drc_reset(void *opaque)
+{
+ sPAPRDRConnector *drc = opaque;
+ DeviceState *d = DEVICE(drc);
+
+ if (d) {
+ device_reset(d);
+ }
+}
+
+static void spapr_create_lmb_dr_connectors(sPAPRMachineState *spapr)
+{
+ MachineState *machine = MACHINE(spapr);
+ uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE;
+ uint32_t nr_lmbs = (machine->maxram_size - machine->ram_size)/lmb_size;
+ int i;
+
+ for (i = 0; i < nr_lmbs; i++) {
+ sPAPRDRConnector *drc;
+ uint64_t addr;
+
+ addr = i * lmb_size + spapr->hotplug_memory.base;
+ drc = spapr_dr_connector_new(OBJECT(spapr), SPAPR_DR_CONNECTOR_TYPE_LMB,
+ addr/lmb_size);
+ qemu_register_reset(spapr_drc_reset, drc);
+ }
+}
+
+/*
+ * If RAM size, maxmem size and individual node mem sizes aren't aligned
+ * to SPAPR_MEMORY_BLOCK_SIZE(256MB), then refuse to start the guest
+ * since we can't support such unaligned sizes with DRCONF_MEMORY.
+ */
+static void spapr_validate_node_memory(MachineState *machine, Error **errp)
+{
+ int i;
+
+ if (machine->ram_size % SPAPR_MEMORY_BLOCK_SIZE) {
+ error_setg(errp, "Memory size 0x" RAM_ADDR_FMT
+ " is not aligned to %llu MiB",
+ machine->ram_size,
+ SPAPR_MEMORY_BLOCK_SIZE / M_BYTE);
+ return;
+ }
+
+ if (machine->maxram_size % SPAPR_MEMORY_BLOCK_SIZE) {
+ error_setg(errp, "Maximum memory size 0x" RAM_ADDR_FMT
+ " is not aligned to %llu MiB",
+ machine->ram_size,
+ SPAPR_MEMORY_BLOCK_SIZE / M_BYTE);
+ return;
+ }
+
+ for (i = 0; i < nb_numa_nodes; i++) {
+ if (numa_info[i].node_mem % SPAPR_MEMORY_BLOCK_SIZE) {
+ error_setg(errp,
+ "Node %d memory size 0x%" PRIx64
+ " is not aligned to %llu MiB",
+ i, numa_info[i].node_mem,
+ SPAPR_MEMORY_BLOCK_SIZE / M_BYTE);
+ return;
+ }
+ }
+}
+
/* pSeries LPAR / sPAPR hardware init */
static void ppc_spapr_init(MachineState *machine)
{
sPAPRMachineState *spapr = SPAPR_MACHINE(machine);
+ sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine);
const char *kernel_filename = machine->kernel_filename;
const char *kernel_cmdline = machine->kernel_cmdline;
const char *initrd_filename = machine->initrd_filename;
@@ -1459,7 +1727,7 @@ static void ppc_spapr_init(MachineState *machine)
bool kernel_le = false;
char *filename;
- msi_supported = true;
+ msi_nonbroken = true;
QLIST_INIT(&spapr->phbs);
@@ -1494,30 +1762,23 @@ static void ppc_spapr_init(MachineState *machine)
}
if (spapr->rma_size > node0_size) {
- fprintf(stderr, "Error: Numa node 0 has to span the RMA (%#08"HWADDR_PRIx")\n",
- spapr->rma_size);
+ error_report("Numa node 0 has to span the RMA (%#08"HWADDR_PRIx")",
+ spapr->rma_size);
exit(1);
}
/* Setup a load limit for the ramdisk leaving room for SLOF and FDT */
load_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FW_OVERHEAD;
- /* We aim for a hash table of size 1/128 the size of RAM. The
- * normal rule of thumb is 1/64 the size of RAM, but that's much
- * more than needed for the Linux guests we support. */
- spapr->htab_shift = 18; /* Minimum architected size */
- while (spapr->htab_shift <= 46) {
- if ((1ULL << (spapr->htab_shift + 7)) >= machine->ram_size) {
- break;
- }
- spapr->htab_shift++;
- }
-
/* Set up Interrupt Controller before we create the VCPUs */
spapr->icp = xics_system_init(machine,
DIV_ROUND_UP(max_cpus * kvmppc_smt_threads(),
smp_threads),
- XICS_IRQS);
+ XICS_IRQS, &error_fatal);
+
+ if (smc->dr_lmb_enabled) {
+ spapr_validate_node_memory(machine, &error_fatal);
+ }
/* init CPUs */
if (machine->cpu_model == NULL) {
@@ -1526,15 +1787,16 @@ static void ppc_spapr_init(MachineState *machine)
for (i = 0; i < smp_cpus; i++) {
cpu = cpu_ppc_init(machine->cpu_model);
if (cpu == NULL) {
- fprintf(stderr, "Unable to find PowerPC CPU definition\n");
+ error_report("Unable to find PowerPC CPU definition");
exit(1);
}
- spapr_cpu_init(spapr, cpu);
+ spapr_cpu_init(spapr, cpu, &error_fatal);
}
if (kvm_enabled()) {
/* Enable H_LOGICAL_CI_* so SLOF can talk to in-kernel devices */
kvmppc_enable_logical_ci_hcalls();
+ kvmppc_enable_set_mode_hcall();
}
/* allocate RAM */
@@ -1550,6 +1812,29 @@ static void ppc_spapr_init(MachineState *machine)
memory_region_add_subregion(sysmem, 0, rma_region);
}
+ /* initialize hotplug memory address space */
+ if (machine->ram_size < machine->maxram_size) {
+ ram_addr_t hotplug_mem_size = machine->maxram_size - machine->ram_size;
+
+ if (machine->ram_slots > SPAPR_MAX_RAM_SLOTS) {
+ error_report("Specified number of memory slots %"
+ PRIu64" exceeds max supported %d",
+ machine->ram_slots, SPAPR_MAX_RAM_SLOTS);
+ exit(1);
+ }
+
+ spapr->hotplug_memory.base = ROUND_UP(machine->ram_size,
+ SPAPR_HOTPLUG_MEM_ALIGN);
+ memory_region_init(&spapr->hotplug_memory.mr, OBJECT(spapr),
+ "hotplug-memory", hotplug_mem_size);
+ memory_region_add_subregion(sysmem, spapr->hotplug_memory.base,
+ &spapr->hotplug_memory.mr);
+ }
+
+ if (smc->dr_lmb_enabled) {
+ spapr_create_lmb_dr_connectors(spapr);
+ }
+
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "spapr-rtas.bin");
if (!filename) {
error_report("Could not find LPAR rtas '%s'", "spapr-rtas.bin");
@@ -1610,13 +1895,17 @@ static void ppc_spapr_init(MachineState *machine)
}
/* Graphics */
- if (spapr_vga_init(phb->bus)) {
+ if (spapr_vga_init(phb->bus, &error_fatal)) {
spapr->has_graphics = true;
machine->usb |= defaults_enabled() && !machine->usb_disabled;
}
if (machine->usb) {
- pci_create_simple(phb->bus, -1, "pci-ohci");
+ if (smc->use_ohci_by_default) {
+ pci_create_simple(phb->bus, -1, "pci-ohci");
+ } else {
+ pci_create_simple(phb->bus, -1, "nec-usb-xhci");
+ }
if (spapr->has_graphics) {
USBBus *usb_bus = usb_bus_find(-1);
@@ -1627,8 +1916,9 @@ static void ppc_spapr_init(MachineState *machine)
}
if (spapr->rma_size < (MIN_RMA_SLOF << 20)) {
- fprintf(stderr, "qemu: pSeries SLOF firmware requires >= "
- "%ldM guest RMA (Real Mode Area memory)\n", MIN_RMA_SLOF);
+ error_report(
+ "pSeries SLOF firmware requires >= %ldM guest RMA (Real Mode Area memory)",
+ MIN_RMA_SLOF);
exit(1);
}
@@ -1636,16 +1926,18 @@ static void ppc_spapr_init(MachineState *machine)
uint64_t lowaddr = 0;
kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL,
- NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0);
+ NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE,
+ 0, 0);
if (kernel_size == ELF_LOAD_WRONG_ENDIAN) {
kernel_size = load_elf(kernel_filename,
translate_kernel_address, NULL,
- NULL, &lowaddr, NULL, 0, ELF_MACHINE, 0);
+ NULL, &lowaddr, NULL, 0, PPC_ELF_MACHINE,
+ 0, 0);
kernel_le = kernel_size > 0;
}
if (kernel_size < 0) {
- fprintf(stderr, "qemu: error loading %s: %s\n",
- kernel_filename, load_elf_strerror(kernel_size));
+ error_report("error loading %s: %s",
+ kernel_filename, load_elf_strerror(kernel_size));
exit(1);
}
@@ -1658,8 +1950,8 @@ static void ppc_spapr_init(MachineState *machine)
initrd_size = load_image_targphys(initrd_filename, initrd_base,
load_limit - initrd_base);
if (initrd_size < 0) {
- fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
- initrd_filename);
+ error_report("could not load initial ram disk '%s'",
+ initrd_filename);
exit(1);
}
} else {
@@ -1796,6 +2088,9 @@ static void spapr_set_kvm_type(Object *obj, const char *value, Error **errp)
static void spapr_machine_initfn(Object *obj)
{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
+
+ spapr->htab_fd = -1;
object_property_add_str(obj, "kvm-type",
spapr_get_kvm_type, spapr_set_kvm_type, NULL);
object_property_set_description(obj, "kvm-type",
@@ -1803,6 +2098,13 @@ static void spapr_machine_initfn(Object *obj)
NULL);
}
+static void spapr_machine_finalizefn(Object *obj)
+{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
+
+ g_free(spapr->kvm_type);
+}
+
static void ppc_cpu_do_nmi_on_cpu(void *arg)
{
CPUState *cs = arg;
@@ -1820,22 +2122,177 @@ static void spapr_nmi(NMIState *n, int cpu_index, Error **errp)
}
}
+static void spapr_add_lmbs(DeviceState *dev, uint64_t addr, uint64_t size,
+ uint32_t node, Error **errp)
+{
+ sPAPRDRConnector *drc;
+ sPAPRDRConnectorClass *drck;
+ uint32_t nr_lmbs = size/SPAPR_MEMORY_BLOCK_SIZE;
+ int i, fdt_offset, fdt_size;
+ void *fdt;
+
+ /*
+ * Check for DRC connectors and send hotplug notification to the
+ * guest only in case of hotplugged memory. This allows cold plugged
+ * memory to be specified at boot time.
+ */
+ if (!dev->hotplugged) {
+ return;
+ }
+
+ for (i = 0; i < nr_lmbs; i++) {
+ drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB,
+ addr/SPAPR_MEMORY_BLOCK_SIZE);
+ g_assert(drc);
+
+ fdt = create_device_tree(&fdt_size);
+ fdt_offset = spapr_populate_memory_node(fdt, node, addr,
+ SPAPR_MEMORY_BLOCK_SIZE);
+
+ drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ drck->attach(drc, dev, fdt, fdt_offset, !dev->hotplugged, errp);
+ addr += SPAPR_MEMORY_BLOCK_SIZE;
+ }
+ spapr_hotplug_req_add_by_count(SPAPR_DR_CONNECTOR_TYPE_LMB, nr_lmbs);
+}
+
+static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
+ uint32_t node, Error **errp)
+{
+ Error *local_err = NULL;
+ sPAPRMachineState *ms = SPAPR_MACHINE(hotplug_dev);
+ PCDIMMDevice *dimm = PC_DIMM(dev);
+ PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
+ MemoryRegion *mr = ddc->get_memory_region(dimm);
+ uint64_t align = memory_region_get_alignment(mr);
+ uint64_t size = memory_region_size(mr);
+ uint64_t addr;
+
+ if (size % SPAPR_MEMORY_BLOCK_SIZE) {
+ error_setg(&local_err, "Hotplugged memory size must be a multiple of "
+ "%lld MB", SPAPR_MEMORY_BLOCK_SIZE/M_BYTE);
+ goto out;
+ }
+
+ pc_dimm_memory_plug(dev, &ms->hotplug_memory, mr, align, &local_err);
+ if (local_err) {
+ goto out;
+ }
+
+ addr = object_property_get_int(OBJECT(dimm), PC_DIMM_ADDR_PROP, &local_err);
+ if (local_err) {
+ pc_dimm_memory_unplug(dev, &ms->hotplug_memory, mr);
+ goto out;
+ }
+
+ spapr_add_lmbs(dev, addr, size, node, &error_abort);
+
+out:
+ error_propagate(errp, local_err);
+}
+
+static void spapr_machine_device_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(qdev_get_machine());
+
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ int node;
+
+ if (!smc->dr_lmb_enabled) {
+ error_setg(errp, "Memory hotplug not supported for this machine");
+ return;
+ }
+ node = object_property_get_int(OBJECT(dev), PC_DIMM_NODE_PROP, errp);
+ if (*errp) {
+ return;
+ }
+ if (node < 0 || node >= MAX_NODES) {
+ error_setg(errp, "Invaild node %d", node);
+ return;
+ }
+
+ /*
+ * Currently PowerPC kernel doesn't allow hot-adding memory to
+ * memory-less node, but instead will silently add the memory
+ * to the first node that has some memory. This causes two
+ * unexpected behaviours for the user.
+ *
+ * - Memory gets hotplugged to a different node than what the user
+ * specified.
+ * - Since pc-dimm subsystem in QEMU still thinks that memory belongs
+ * to memory-less node, a reboot will set things accordingly
+ * and the previously hotplugged memory now ends in the right node.
+ * This appears as if some memory moved from one node to another.
+ *
+ * So until kernel starts supporting memory hotplug to memory-less
+ * nodes, just prevent such attempts upfront in QEMU.
+ */
+ if (nb_numa_nodes && !numa_info[node].node_mem) {
+ error_setg(errp, "Can't hotplug memory to memory-less node %d",
+ node);
+ return;
+ }
+
+ spapr_memory_plug(hotplug_dev, dev, node, errp);
+ }
+}
+
+static void spapr_machine_device_unplug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ error_setg(errp, "Memory hot unplug not supported by sPAPR");
+ }
+}
+
+static HotplugHandler *spapr_get_hotpug_handler(MachineState *machine,
+ DeviceState *dev)
+{
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ return HOTPLUG_HANDLER(machine);
+ }
+ return NULL;
+}
+
+static unsigned spapr_cpu_index_to_socket_id(unsigned cpu_index)
+{
+ /* Allocate to NUMA nodes on a "socket" basis (not that concept of
+ * socket means much for the paravirtualized PAPR platform) */
+ return cpu_index / smp_threads / smp_cores;
+}
+
static void spapr_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
+ sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(oc);
FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc);
NMIClass *nc = NMI_CLASS(oc);
+ HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
+
+ mc->desc = "pSeries Logical Partition (PAPR compliant)";
+ /*
+ * We set up the default / latest behaviour here. The class_init
+ * functions for the specific versioned machine types can override
+ * these details for backwards compatibility
+ */
mc->init = ppc_spapr_init;
mc->reset = ppc_spapr_reset;
mc->block_default_type = IF_SCSI;
- mc->max_cpus = MAX_CPUS;
+ mc->max_cpus = MAX_CPUMASK_BITS;
mc->no_parallel = 1;
mc->default_boot_order = "";
mc->default_ram_size = 512 * M_BYTE;
mc->kvm_type = spapr_kvm_type;
mc->has_dynamic_sysbus = true;
+ mc->pci_allow_0_address = true;
+ mc->get_hotplug_handler = spapr_get_hotpug_handler;
+ hc->plug = spapr_machine_device_plug;
+ hc->unplug = spapr_machine_device_unplug;
+ mc->cpu_index_to_socket_id = spapr_cpu_index_to_socket_id;
+ smc->dr_lmb_enabled = true;
fwc->get_dev_path = spapr_get_fw_dev_path;
nc->nmi_monitor_handler = spapr_nmi;
}
@@ -1846,153 +2303,183 @@ static const TypeInfo spapr_machine_info = {
.abstract = true,
.instance_size = sizeof(sPAPRMachineState),
.instance_init = spapr_machine_initfn,
+ .instance_finalize = spapr_machine_finalizefn,
.class_size = sizeof(sPAPRMachineClass),
.class_init = spapr_machine_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_FW_PATH_PROVIDER },
{ TYPE_NMI },
+ { TYPE_HOTPLUG_HANDLER },
{ }
},
};
-#define SPAPR_COMPAT_2_3 \
- HW_COMPAT_2_3 \
- {\
- .driver = "spapr-pci-host-bridge",\
- .property = "dynamic-reconfiguration",\
- .value = "off",\
- },
-
-#define SPAPR_COMPAT_2_2 \
- SPAPR_COMPAT_2_3 \
- HW_COMPAT_2_2 \
- {\
- .driver = TYPE_SPAPR_PCI_HOST_BRIDGE,\
- .property = "mem_win_size",\
- .value = "0x20000000",\
- },
-
-#define SPAPR_COMPAT_2_1 \
- SPAPR_COMPAT_2_2 \
- HW_COMPAT_2_1
+#define DEFINE_SPAPR_MACHINE(suffix, verstr, latest) \
+ static void spapr_machine_##suffix##_class_init(ObjectClass *oc, \
+ void *data) \
+ { \
+ MachineClass *mc = MACHINE_CLASS(oc); \
+ spapr_machine_##suffix##_class_options(mc); \
+ if (latest) { \
+ mc->alias = "pseries"; \
+ mc->is_default = 1; \
+ } \
+ } \
+ static void spapr_machine_##suffix##_instance_init(Object *obj) \
+ { \
+ MachineState *machine = MACHINE(obj); \
+ spapr_machine_##suffix##_instance_options(machine); \
+ } \
+ static const TypeInfo spapr_machine_##suffix##_info = { \
+ .name = MACHINE_TYPE_NAME("pseries-" verstr), \
+ .parent = TYPE_SPAPR_MACHINE, \
+ .class_init = spapr_machine_##suffix##_class_init, \
+ .instance_init = spapr_machine_##suffix##_instance_init, \
+ }; \
+ static void spapr_machine_register_##suffix(void) \
+ { \
+ type_register(&spapr_machine_##suffix##_info); \
+ } \
+ type_init(spapr_machine_register_##suffix)
-static void spapr_compat_2_3(Object *obj)
+/*
+ * pseries-2.6
+ */
+static void spapr_machine_2_6_instance_options(MachineState *machine)
{
- savevm_skip_section_footers();
- global_state_set_optional();
}
-static void spapr_compat_2_2(Object *obj)
+static void spapr_machine_2_6_class_options(MachineClass *mc)
{
- spapr_compat_2_3(obj);
+ /* Defaults for the latest behaviour inherited from the base class */
}
-static void spapr_compat_2_1(Object *obj)
-{
- spapr_compat_2_2(obj);
-}
+DEFINE_SPAPR_MACHINE(2_6, "2.6", true);
+
+/*
+ * pseries-2.5
+ */
+#define SPAPR_COMPAT_2_5 \
+ HW_COMPAT_2_5 \
+ { \
+ .driver = "spapr-vlan", \
+ .property = "use-rx-buffer-pools", \
+ .value = "off", \
+ },
-static void spapr_machine_2_3_instance_init(Object *obj)
+static void spapr_machine_2_5_instance_options(MachineState *machine)
{
- spapr_compat_2_3(obj);
- spapr_machine_initfn(obj);
}
-static void spapr_machine_2_2_instance_init(Object *obj)
+static void spapr_machine_2_5_class_options(MachineClass *mc)
{
- spapr_compat_2_2(obj);
- spapr_machine_initfn(obj);
+ sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc);
+
+ spapr_machine_2_6_class_options(mc);
+ smc->use_ohci_by_default = true;
+ SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_5);
}
-static void spapr_machine_2_1_instance_init(Object *obj)
+DEFINE_SPAPR_MACHINE(2_5, "2.5", false);
+
+/*
+ * pseries-2.4
+ */
+#define SPAPR_COMPAT_2_4 \
+ SPAPR_COMPAT_2_5 \
+ HW_COMPAT_2_4
+
+static void spapr_machine_2_4_instance_options(MachineState *machine)
{
- spapr_compat_2_1(obj);
- spapr_machine_initfn(obj);
+ spapr_machine_2_5_instance_options(machine);
}
-static void spapr_machine_2_1_class_init(ObjectClass *oc, void *data)
+static void spapr_machine_2_4_class_options(MachineClass *mc)
{
- MachineClass *mc = MACHINE_CLASS(oc);
- static GlobalProperty compat_props[] = {
- SPAPR_COMPAT_2_1
- { /* end of list */ }
- };
+ sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc);
- mc->name = "pseries-2.1";
- mc->desc = "pSeries Logical Partition (PAPR compliant) v2.1";
- mc->compat_props = compat_props;
+ spapr_machine_2_5_class_options(mc);
+ smc->dr_lmb_enabled = false;
+ SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_4);
}
-static const TypeInfo spapr_machine_2_1_info = {
- .name = TYPE_SPAPR_MACHINE "2.1",
- .parent = TYPE_SPAPR_MACHINE,
- .class_init = spapr_machine_2_1_class_init,
- .instance_init = spapr_machine_2_1_instance_init,
-};
+DEFINE_SPAPR_MACHINE(2_4, "2.4", false);
+
+/*
+ * pseries-2.3
+ */
+#define SPAPR_COMPAT_2_3 \
+ SPAPR_COMPAT_2_4 \
+ HW_COMPAT_2_3 \
+ {\
+ .driver = "spapr-pci-host-bridge",\
+ .property = "dynamic-reconfiguration",\
+ .value = "off",\
+ },
-static void spapr_machine_2_2_class_init(ObjectClass *oc, void *data)
+static void spapr_machine_2_3_instance_options(MachineState *machine)
{
- static GlobalProperty compat_props[] = {
- SPAPR_COMPAT_2_2
- { /* end of list */ }
- };
- MachineClass *mc = MACHINE_CLASS(oc);
+ spapr_machine_2_4_instance_options(machine);
+ savevm_skip_section_footers();
+ global_state_set_optional();
+ savevm_skip_configuration();
+}
- mc->name = "pseries-2.2";
- mc->desc = "pSeries Logical Partition (PAPR compliant) v2.2";
- mc->compat_props = compat_props;
+static void spapr_machine_2_3_class_options(MachineClass *mc)
+{
+ spapr_machine_2_4_class_options(mc);
+ SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_3);
}
+DEFINE_SPAPR_MACHINE(2_3, "2.3", false);
-static const TypeInfo spapr_machine_2_2_info = {
- .name = TYPE_SPAPR_MACHINE "2.2",
- .parent = TYPE_SPAPR_MACHINE,
- .class_init = spapr_machine_2_2_class_init,
- .instance_init = spapr_machine_2_2_instance_init,
-};
+/*
+ * pseries-2.2
+ */
-static void spapr_machine_2_3_class_init(ObjectClass *oc, void *data)
+#define SPAPR_COMPAT_2_2 \
+ SPAPR_COMPAT_2_3 \
+ HW_COMPAT_2_2 \
+ {\
+ .driver = TYPE_SPAPR_PCI_HOST_BRIDGE,\
+ .property = "mem_win_size",\
+ .value = "0x20000000",\
+ },
+
+static void spapr_machine_2_2_instance_options(MachineState *machine)
{
- static GlobalProperty compat_props[] = {
- SPAPR_COMPAT_2_3
- { /* end of list */ }
- };
- MachineClass *mc = MACHINE_CLASS(oc);
+ spapr_machine_2_3_instance_options(machine);
+ machine->suppress_vmdesc = true;
+}
- mc->name = "pseries-2.3";
- mc->desc = "pSeries Logical Partition (PAPR compliant) v2.3";
- mc->compat_props = compat_props;
+static void spapr_machine_2_2_class_options(MachineClass *mc)
+{
+ spapr_machine_2_3_class_options(mc);
+ SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_2);
}
+DEFINE_SPAPR_MACHINE(2_2, "2.2", false);
-static const TypeInfo spapr_machine_2_3_info = {
- .name = TYPE_SPAPR_MACHINE "2.3",
- .parent = TYPE_SPAPR_MACHINE,
- .class_init = spapr_machine_2_3_class_init,
- .instance_init = spapr_machine_2_3_instance_init,
-};
+/*
+ * pseries-2.1
+ */
+#define SPAPR_COMPAT_2_1 \
+ SPAPR_COMPAT_2_2 \
+ HW_COMPAT_2_1
-static void spapr_machine_2_4_class_init(ObjectClass *oc, void *data)
+static void spapr_machine_2_1_instance_options(MachineState *machine)
{
- MachineClass *mc = MACHINE_CLASS(oc);
-
- mc->name = "pseries-2.4";
- mc->desc = "pSeries Logical Partition (PAPR compliant) v2.4";
- mc->alias = "pseries";
- mc->is_default = 1;
+ spapr_machine_2_2_instance_options(machine);
}
-static const TypeInfo spapr_machine_2_4_info = {
- .name = TYPE_SPAPR_MACHINE "2.4",
- .parent = TYPE_SPAPR_MACHINE,
- .class_init = spapr_machine_2_4_class_init,
-};
+static void spapr_machine_2_1_class_options(MachineClass *mc)
+{
+ spapr_machine_2_2_class_options(mc);
+ SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_1);
+}
+DEFINE_SPAPR_MACHINE(2_1, "2.1", false);
static void spapr_machine_register_types(void)
{
type_register_static(&spapr_machine_info);
- type_register_static(&spapr_machine_2_1_info);
- type_register_static(&spapr_machine_2_2_info);
- type_register_static(&spapr_machine_2_3_info);
- type_register_static(&spapr_machine_2_4_info);
}
type_init(spapr_machine_register_types)
diff --git a/qemu/hw/ppc/spapr_drc.c b/qemu/hw/ppc/spapr_drc.c
index ee874326e..1f5f1d790 100644
--- a/qemu/hw/ppc/spapr_drc.c
+++ b/qemu/hw/ppc/spapr_drc.c
@@ -10,11 +10,16 @@
* See the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "cpu.h"
+#include "qemu/cutils.h"
#include "hw/ppc/spapr_drc.h"
#include "qom/object.h"
#include "hw/qdev.h"
#include "qapi/visitor.h"
#include "qemu/error-report.h"
+#include "hw/ppc/spapr.h" /* for RTAS return codes */
/* #define DEBUG_SPAPR_DRC */
@@ -32,7 +37,7 @@
#define DRC_CONTAINER_PATH "/dr-connector"
#define DRC_INDEX_TYPE_SHIFT 28
-#define DRC_INDEX_ID_MASK (~(~0 << DRC_INDEX_TYPE_SHIFT))
+#define DRC_INDEX_ID_MASK ((1ULL << DRC_INDEX_TYPE_SHIFT) - 1)
static sPAPRDRConnectorTypeShift get_type_shift(sPAPRDRConnectorType type)
{
@@ -59,13 +64,23 @@ static uint32_t get_index(sPAPRDRConnector *drc)
(drc->id & DRC_INDEX_ID_MASK);
}
-static int set_isolation_state(sPAPRDRConnector *drc,
- sPAPRDRIsolationState state)
+static uint32_t set_isolation_state(sPAPRDRConnector *drc,
+ sPAPRDRIsolationState state)
{
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
DPRINTFN("drc: %x, set_isolation_state: %x", get_index(drc), state);
+ if (state == SPAPR_DR_ISOLATION_STATE_UNISOLATED) {
+ /* cannot unisolate a non-existant resource, and, or resources
+ * which are in an 'UNUSABLE' allocation state. (PAPR 2.7, 13.5.3.5)
+ */
+ if (!drc->dev ||
+ drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE) {
+ return RTAS_OUT_NO_SUCH_INDICATOR;
+ }
+ }
+
drc->isolation_state = state;
if (drc->isolation_state == SPAPR_DR_ISOLATION_STATE_ISOLATED) {
@@ -89,24 +104,35 @@ static int set_isolation_state(sPAPRDRConnector *drc,
drc->configured = false;
}
- return 0;
+ return RTAS_OUT_SUCCESS;
}
-static int set_indicator_state(sPAPRDRConnector *drc,
- sPAPRDRIndicatorState state)
+static uint32_t set_indicator_state(sPAPRDRConnector *drc,
+ sPAPRDRIndicatorState state)
{
DPRINTFN("drc: %x, set_indicator_state: %x", get_index(drc), state);
drc->indicator_state = state;
- return 0;
+ return RTAS_OUT_SUCCESS;
}
-static int set_allocation_state(sPAPRDRConnector *drc,
- sPAPRDRAllocationState state)
+static uint32_t set_allocation_state(sPAPRDRConnector *drc,
+ sPAPRDRAllocationState state)
{
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
DPRINTFN("drc: %x, set_allocation_state: %x", get_index(drc), state);
+ if (state == SPAPR_DR_ALLOCATION_STATE_USABLE) {
+ /* if there's no resource/device associated with the DRC, there's
+ * no way for us to put it in an allocation state consistent with
+ * being 'USABLE'. PAPR 2.7, 13.5.3.4 documents that this should
+ * result in an RTAS return code of -3 / "no such indicator"
+ */
+ if (!drc->dev) {
+ return RTAS_OUT_NO_SUCH_INDICATOR;
+ }
+ }
+
if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI) {
drc->allocation_state = state;
if (drc->awaiting_release &&
@@ -116,7 +142,7 @@ static int set_allocation_state(sPAPRDRConnector *drc,
drc->detach_cb_opaque, NULL);
}
}
- return 0;
+ return RTAS_OUT_SUCCESS;
}
static uint32_t get_type(sPAPRDRConnector *drc)
@@ -150,6 +176,12 @@ static void set_configured(sPAPRDRConnector *drc)
drc->configured = true;
}
+/* has the guest been notified of device attachment? */
+static void set_signalled(sPAPRDRConnector *drc)
+{
+ drc->signalled = true;
+}
+
/*
* dr-entity-sense sensor value
* returned via get-sensor-state RTAS calls
@@ -157,10 +189,8 @@ static void set_configured(sPAPRDRConnector *drc)
* based on the current allocation/indicator/power states
* for the DR connector.
*/
-static sPAPRDREntitySense entity_sense(sPAPRDRConnector *drc)
+static uint32_t entity_sense(sPAPRDRConnector *drc, sPAPRDREntitySense *state)
{
- sPAPRDREntitySense state;
-
if (drc->dev) {
if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI &&
drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE) {
@@ -169,7 +199,7 @@ static sPAPRDREntitySense entity_sense(sPAPRDRConnector *drc)
* Otherwise, report the state as USABLE/PRESENT,
* as we would for PCI.
*/
- state = SPAPR_DR_ENTITY_SENSE_UNUSABLE;
+ *state = SPAPR_DR_ENTITY_SENSE_UNUSABLE;
} else {
/* this assumes all PCI devices are assigned to
* a 'live insertion' power domain, where QEMU
@@ -177,39 +207,39 @@ static sPAPRDREntitySense entity_sense(sPAPRDRConnector *drc)
* to the guest. present, non-PCI resources are
* unaffected by power state.
*/
- state = SPAPR_DR_ENTITY_SENSE_PRESENT;
+ *state = SPAPR_DR_ENTITY_SENSE_PRESENT;
}
} else {
if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) {
/* PCI devices, and only PCI devices, use EMPTY
* in cases where we'd otherwise use UNUSABLE
*/
- state = SPAPR_DR_ENTITY_SENSE_EMPTY;
+ *state = SPAPR_DR_ENTITY_SENSE_EMPTY;
} else {
- state = SPAPR_DR_ENTITY_SENSE_UNUSABLE;
+ *state = SPAPR_DR_ENTITY_SENSE_UNUSABLE;
}
}
DPRINTFN("drc: %x, entity_sense: %x", get_index(drc), state);
- return state;
+ return RTAS_OUT_SUCCESS;
}
-static void prop_get_index(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void prop_get_index(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
uint32_t value = (uint32_t)drck->get_index(drc);
- visit_type_uint32(v, &value, name, errp);
+ visit_type_uint32(v, name, &value, errp);
}
-static void prop_get_type(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void prop_get_type(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
uint32_t value = (uint32_t)drck->get_type(drc);
- visit_type_uint32(v, &value, name, errp);
+ visit_type_uint32(v, name, &value, errp);
}
static char *prop_get_name(Object *obj, Error **errp)
@@ -219,23 +249,31 @@ static char *prop_get_name(Object *obj, Error **errp)
return g_strdup(drck->get_name(drc));
}
-static void prop_get_entity_sense(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void prop_get_entity_sense(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
- uint32_t value = (uint32_t)drck->entity_sense(drc);
- visit_type_uint32(v, &value, name, errp);
+ uint32_t value;
+
+ drck->entity_sense(drc, &value);
+ visit_type_uint32(v, name, &value, errp);
}
-static void prop_get_fdt(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
+ Error *err = NULL;
int fdt_offset_next, fdt_offset, fdt_depth;
void *fdt;
if (!drc->fdt) {
+ visit_start_struct(v, name, NULL, 0, &err);
+ if (!err) {
+ visit_end_struct(v, &err);
+ }
+ error_propagate(errp, err);
return;
}
@@ -254,24 +292,39 @@ static void prop_get_fdt(Object *obj, Visitor *v, void *opaque,
case FDT_BEGIN_NODE:
fdt_depth++;
name = fdt_get_name(fdt, fdt_offset, &name_len);
- visit_start_struct(v, NULL, NULL, name, 0, NULL);
+ visit_start_struct(v, name, NULL, 0, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
break;
case FDT_END_NODE:
/* shouldn't ever see an FDT_END_NODE before FDT_BEGIN_NODE */
g_assert(fdt_depth > 0);
- visit_end_struct(v, NULL);
+ visit_end_struct(v, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
fdt_depth--;
break;
case FDT_PROP: {
int i;
prop = fdt_get_property_by_offset(fdt, fdt_offset, &prop_len);
name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
- visit_start_list(v, name, NULL);
+ visit_start_list(v, name, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
for (i = 0; i < prop_len; i++) {
- visit_type_uint8(v, (uint8_t *)&prop->data[i], NULL, NULL);
-
+ visit_type_uint8(v, NULL, (uint8_t *)&prop->data[i], &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
}
- visit_end_list(v, NULL);
+ visit_end_list(v);
break;
}
default:
@@ -310,7 +363,18 @@ static void attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt,
drc->dev = d;
drc->fdt = fdt;
drc->fdt_start_offset = fdt_start_offset;
- drc->configured = false;
+ drc->configured = coldplug;
+ /* 'logical' DR resources such as memory/cpus are in some cases treated
+ * as a pool of resources from which the guest is free to choose from
+ * based on only a count. for resources that can be assigned in this
+ * fashion, we must assume the resource is signalled immediately
+ * since a single hotplug request might make an arbitrary number of
+ * such attached resources available to the guest, as opposed to
+ * 'physical' DR resources such as PCI where each device/resource is
+ * signalled individually.
+ */
+ drc->signalled = (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI)
+ ? true : coldplug;
object_property_add_link(OBJECT(drc), "device",
object_get_typename(OBJECT(drc->dev)),
@@ -327,6 +391,26 @@ static void detach(sPAPRDRConnector *drc, DeviceState *d,
drc->detach_cb = detach_cb;
drc->detach_cb_opaque = detach_cb_opaque;
+ /* if we've signalled device presence to the guest, or if the guest
+ * has gone ahead and configured the device (via manually-executed
+ * device add via drmgr in guest, namely), we need to wait
+ * for the guest to quiesce the device before completing detach.
+ * Otherwise, we can assume the guest hasn't seen it and complete the
+ * detach immediately. Note that there is a small race window
+ * just before, or during, configuration, which is this context
+ * refers mainly to fetching the device tree via RTAS.
+ * During this window the device access will be arbitrated by
+ * associated DRC, which will simply fail the RTAS calls as invalid.
+ * This is recoverable within guest and current implementations of
+ * drmgr should be able to cope.
+ */
+ if (!drc->signalled && !drc->configured) {
+ /* if the guest hasn't seen the device we can't rely on it to
+ * set it back to an isolated state via RTAS, so do it here manually
+ */
+ drc->isolation_state = SPAPR_DR_ISOLATION_STATE_ISOLATED;
+ }
+
if (drc->isolation_state != SPAPR_DR_ISOLATION_STATE_ISOLATED) {
DPRINTFN("awaiting transition to isolated state before removal");
drc->awaiting_release = true;
@@ -365,6 +449,7 @@ static void reset(DeviceState *d)
{
sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d);
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ sPAPRDREntitySense state;
DPRINTFN("drc reset: %x", drck->get_index(drc));
/* immediately upon reset we can safely assume DRCs whose devices
@@ -392,6 +477,11 @@ static void reset(DeviceState *d)
drck->set_allocation_state(drc, SPAPR_DR_ALLOCATION_STATE_UNUSABLE);
}
}
+
+ drck->entity_sense(drc, &state);
+ if (state == SPAPR_DR_ENTITY_SENSE_PRESENT) {
+ drck->set_signalled(drc);
+ }
}
static void realize(DeviceState *d, Error **errp)
@@ -418,8 +508,7 @@ static void realize(DeviceState *d, Error **errp)
object_property_add_alias(root_container, link_name,
drc->owner, child_name, &err);
if (err) {
- error_report("%s", error_get_pretty(err));
- error_free(err);
+ error_report_err(err);
object_unref(OBJECT(drc));
}
g_free(child_name);
@@ -439,8 +528,7 @@ static void unrealize(DeviceState *d, Error **errp)
snprintf(name, sizeof(name), "%x", drck->get_index(drc));
object_property_del(root_container, name, &err);
if (err) {
- error_report("%s", error_get_pretty(err));
- error_free(err);
+ error_report_err(err);
object_unref(OBJECT(drc));
}
}
@@ -451,14 +539,17 @@ sPAPRDRConnector *spapr_dr_connector_new(Object *owner,
{
sPAPRDRConnector *drc =
SPAPR_DR_CONNECTOR(object_new(TYPE_SPAPR_DR_CONNECTOR));
+ char *prop_name;
g_assert(type);
drc->type = type;
drc->id = id;
drc->owner = owner;
- object_property_add_child(owner, "dr-connector[*]", OBJECT(drc), NULL);
+ prop_name = g_strdup_printf("dr-connector[%"PRIu32"]", get_index(drc));
+ object_property_add_child(owner, prop_name, OBJECT(drc), NULL);
object_property_set_bool(OBJECT(drc), true, "realized", NULL);
+ g_free(prop_name);
/* human-readable name for a DRC to encode into the DT
* description. this is mainly only used within a guest in place
@@ -549,6 +640,11 @@ static void spapr_dr_connector_class_init(ObjectClass *k, void *data)
drck->attach = attach;
drck->detach = detach;
drck->release_pending = release_pending;
+ drck->set_signalled = set_signalled;
+ /*
+ * Reason: it crashes FIXME find and document the real reason
+ */
+ dk->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo spapr_dr_connector_info = {
@@ -632,6 +728,7 @@ int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner,
{
Object *root_container;
ObjectProperty *prop;
+ ObjectPropertyIterator iter;
uint32_t drc_count = 0;
GArray *drc_indexes, *drc_power_domains;
GString *drc_names, *drc_types;
@@ -655,7 +752,8 @@ int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner,
*/
root_container = container_get(object_get_root(), DRC_CONTAINER_PATH);
- QTAILQ_FOREACH(prop, &root_container->properties, node) {
+ object_property_iter_init(&iter, root_container);
+ while ((prop = object_property_iter_next(&iter))) {
Object *obj;
sPAPRDRConnector *drc;
sPAPRDRConnectorClass *drck;
diff --git a/qemu/hw/ppc/spapr_events.c b/qemu/hw/ppc/spapr_events.c
index f626eb7b3..049fb1b32 100644
--- a/qemu/hw/ppc/spapr_events.c
+++ b/qemu/hw/ppc/spapr_events.c
@@ -24,6 +24,8 @@
* THE SOFTWARE.
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "cpu.h"
#include "sysemu/sysemu.h"
#include "sysemu/char.h"
@@ -35,7 +37,8 @@
#include "hw/pci/pci.h"
#include "hw/pci-host/spapr.h"
#include "hw/ppc/spapr_drc.h"
-
+#include "qemu/help_option.h"
+#include "qemu/bcd.h"
#include <libfdt.h>
struct rtas_error_log {
@@ -386,7 +389,16 @@ static void spapr_powerdown_req(Notifier *n, void *opaque)
qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
}
-static void spapr_hotplug_req_event(sPAPRDRConnector *drc, uint8_t hp_action)
+static void spapr_hotplug_set_signalled(uint32_t drc_index)
+{
+ sPAPRDRConnector *drc = spapr_dr_connector_by_index(drc_index);
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ drck->set_signalled(drc);
+}
+
+static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action,
+ sPAPRDRConnectorType drc_type,
+ uint32_t drc)
{
sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
struct hp_log_full *new_hp;
@@ -395,8 +407,6 @@ static void spapr_hotplug_req_event(sPAPRDRConnector *drc, uint8_t hp_action)
struct rtas_event_log_v6_maina *maina;
struct rtas_event_log_v6_mainb *mainb;
struct rtas_event_log_v6_hp *hp;
- sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
- sPAPRDRConnectorType drc_type = drck->get_type(drc);
new_hp = g_malloc0(sizeof(struct hp_log_full));
hdr = &new_hp->hdr;
@@ -427,13 +437,17 @@ static void spapr_hotplug_req_event(sPAPRDRConnector *drc, uint8_t hp_action)
hp->hdr.section_length = cpu_to_be16(sizeof(*hp));
hp->hdr.section_version = 1; /* includes extended modifier */
hp->hotplug_action = hp_action;
-
+ hp->hotplug_identifier = hp_id;
switch (drc_type) {
case SPAPR_DR_CONNECTOR_TYPE_PCI:
- hp->drc.index = cpu_to_be32(drck->get_index(drc));
- hp->hotplug_identifier = RTAS_LOG_V6_HP_ID_DRC_INDEX;
hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PCI;
+ if (hp->hotplug_action == RTAS_LOG_V6_HP_ACTION_ADD) {
+ spapr_hotplug_set_signalled(drc);
+ }
+ break;
+ case SPAPR_DR_CONNECTOR_TYPE_LMB:
+ hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_MEMORY;
break;
default:
/* we shouldn't be signaling hotplug events for resources
@@ -443,19 +457,49 @@ static void spapr_hotplug_req_event(sPAPRDRConnector *drc, uint8_t hp_action)
return;
}
+ if (hp_id == RTAS_LOG_V6_HP_ID_DRC_COUNT) {
+ hp->drc.count = cpu_to_be32(drc);
+ } else if (hp_id == RTAS_LOG_V6_HP_ID_DRC_INDEX) {
+ hp->drc.index = cpu_to_be32(drc);
+ }
+
rtas_event_log_queue(RTAS_LOG_TYPE_HOTPLUG, new_hp, true);
qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
}
-void spapr_hotplug_req_add_event(sPAPRDRConnector *drc)
+void spapr_hotplug_req_add_by_index(sPAPRDRConnector *drc)
+{
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ sPAPRDRConnectorType drc_type = drck->get_type(drc);
+ uint32_t index = drck->get_index(drc);
+
+ spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_INDEX,
+ RTAS_LOG_V6_HP_ACTION_ADD, drc_type, index);
+}
+
+void spapr_hotplug_req_remove_by_index(sPAPRDRConnector *drc)
+{
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ sPAPRDRConnectorType drc_type = drck->get_type(drc);
+ uint32_t index = drck->get_index(drc);
+
+ spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_INDEX,
+ RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, index);
+}
+
+void spapr_hotplug_req_add_by_count(sPAPRDRConnectorType drc_type,
+ uint32_t count)
{
- spapr_hotplug_req_event(drc, RTAS_LOG_V6_HP_ACTION_ADD);
+ spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_COUNT,
+ RTAS_LOG_V6_HP_ACTION_ADD, drc_type, count);
}
-void spapr_hotplug_req_remove_event(sPAPRDRConnector *drc)
+void spapr_hotplug_req_remove_by_count(sPAPRDRConnectorType drc_type,
+ uint32_t count)
{
- spapr_hotplug_req_event(drc, RTAS_LOG_V6_HP_ACTION_REMOVE);
+ spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_COUNT,
+ RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, count);
}
static void check_exception(PowerPCCPU *cpu, sPAPRMachineState *spapr,
@@ -556,7 +600,8 @@ out_no_events:
void spapr_events_init(sPAPRMachineState *spapr)
{
QTAILQ_INIT(&spapr->pending_events);
- spapr->check_exception_irq = xics_alloc(spapr->icp, 0, 0, false);
+ spapr->check_exception_irq = xics_alloc(spapr->icp, 0, 0, false,
+ &error_fatal);
spapr->epow_notifier.notify = spapr_powerdown_req;
qemu_register_powerdown_notifier(&spapr->epow_notifier);
spapr_rtas_register(RTAS_CHECK_EXCEPTION, "check-exception",
diff --git a/qemu/hw/ppc/spapr_hcall.c b/qemu/hw/ppc/spapr_hcall.c
index 652ddf6e3..8f40602a5 100644
--- a/qemu/hw/ppc/spapr_hcall.c
+++ b/qemu/hw/ppc/spapr_hcall.c
@@ -1,3 +1,5 @@
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "sysemu/sysemu.h"
#include "cpu.h"
#include "helper_regs.h"
@@ -37,40 +39,10 @@ static void set_spr(CPUState *cs, int spr, target_ulong value,
run_on_cpu(cs, do_spr_sync, &s);
}
-static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r,
- target_ulong pte_index)
+static bool has_spr(PowerPCCPU *cpu, int spr)
{
- target_ulong rb, va_low;
-
- rb = (v & ~0x7fULL) << 16; /* AVA field */
- va_low = pte_index >> 3;
- if (v & HPTE64_V_SECONDARY) {
- va_low = ~va_low;
- }
- /* xor vsid from AVA */
- if (!(v & HPTE64_V_1TB_SEG)) {
- va_low ^= v >> 12;
- } else {
- va_low ^= v >> 24;
- }
- va_low &= 0x7ff;
- if (v & HPTE64_V_LARGE) {
- rb |= 1; /* L field */
-#if 0 /* Disable that P7 specific bit for now */
- if (r & 0xff000) {
- /* non-16MB large page, must be 64k */
- /* (masks depend on page size) */
- rb |= 0x1000; /* page encoding in LP field */
- rb |= (va_low & 0x7f) << 16; /* 7b of VA in AVA/LP field */
- rb |= (va_low & 0xfe); /* AVAL field */
- }
-#endif
- } else {
- /* 4kB page */
- rb |= (va_low & 0x7ff) << 12; /* remaining 11b of AVA */
- }
- rb |= (v >> 54) & 0x300; /* B field */
- return rb;
+ /* We can test whether the SPR is defined by checking for a valid name */
+ return cpu->env.spr_cb[spr].name != NULL;
}
static inline bool valid_pte_index(CPUPPCState *env, target_ulong pte_index)
@@ -84,42 +56,44 @@ static inline bool valid_pte_index(CPUPPCState *env, target_ulong pte_index)
return true;
}
+static bool is_ram_address(sPAPRMachineState *spapr, hwaddr addr)
+{
+ MachineState *machine = MACHINE(spapr);
+ MemoryHotplugState *hpms = &spapr->hotplug_memory;
+
+ if (addr < machine->ram_size) {
+ return true;
+ }
+ if ((addr >= hpms->base)
+ && ((addr - hpms->base) < memory_region_size(&hpms->mr))) {
+ return true;
+ }
+
+ return false;
+}
+
static target_ulong h_enter(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
- MachineState *machine = MACHINE(spapr);
CPUPPCState *env = &cpu->env;
target_ulong flags = args[0];
target_ulong pte_index = args[1];
target_ulong pteh = args[2];
target_ulong ptel = args[3];
- target_ulong page_shift = 12;
+ unsigned apshift, spshift;
target_ulong raddr;
target_ulong index;
uint64_t token;
- /* only handle 4k and 16M pages for now */
- if (pteh & HPTE64_V_LARGE) {
-#if 0 /* We don't support 64k pages yet */
- if ((ptel & 0xf000) == 0x1000) {
- /* 64k page */
- } else
-#endif
- if ((ptel & 0xff000) == 0) {
- /* 16M page */
- page_shift = 24;
- /* lowest AVA bit must be 0 for 16M pages */
- if (pteh & 0x80) {
- return H_PARAMETER;
- }
- } else {
- return H_PARAMETER;
- }
+ apshift = ppc_hash64_hpte_page_shift_noslb(cpu, pteh, ptel, &spshift);
+ if (!apshift) {
+ /* Bad page size encoding */
+ return H_PARAMETER;
}
- raddr = (ptel & HPTE64_R_RPN) & ~((1ULL << page_shift) - 1);
+ raddr = (ptel & HPTE64_R_RPN) & ~((1ULL << apshift) - 1);
- if (raddr < machine->ram_size) {
+ if (is_ram_address(spapr, raddr)) {
/* Regular RAM - should have WIMG=0010 */
if ((ptel & HPTE64_R_WIMG) != HPTE64_R_M) {
return H_PARAMETER;
@@ -145,24 +119,24 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPRMachineState *spapr,
pte_index &= ~7ULL;
token = ppc_hash64_start_access(cpu, pte_index);
for (; index < 8; index++) {
- if ((ppc_hash64_load_hpte0(env, token, index) & HPTE64_V_VALID) == 0) {
+ if (!(ppc_hash64_load_hpte0(cpu, token, index) & HPTE64_V_VALID)) {
break;
}
}
- ppc_hash64_stop_access(token);
+ ppc_hash64_stop_access(cpu, token);
if (index == 8) {
return H_PTEG_FULL;
}
} else {
token = ppc_hash64_start_access(cpu, pte_index);
- if (ppc_hash64_load_hpte0(env, token, 0) & HPTE64_V_VALID) {
- ppc_hash64_stop_access(token);
+ if (ppc_hash64_load_hpte0(cpu, token, 0) & HPTE64_V_VALID) {
+ ppc_hash64_stop_access(cpu, token);
return H_PTEG_FULL;
}
- ppc_hash64_stop_access(token);
+ ppc_hash64_stop_access(cpu, token);
}
- ppc_hash64_store_hpte(env, pte_index + index,
+ ppc_hash64_store_hpte(cpu, pte_index + index,
pteh | HPTE64_V_HPTE_DIRTY, ptel);
args[0] = pte_index + index;
@@ -176,22 +150,23 @@ typedef enum {
REMOVE_HW = 3,
} RemoveResult;
-static RemoveResult remove_hpte(CPUPPCState *env, target_ulong ptex,
+static RemoveResult remove_hpte(PowerPCCPU *cpu, target_ulong ptex,
target_ulong avpn,
target_ulong flags,
target_ulong *vp, target_ulong *rp)
{
+ CPUPPCState *env = &cpu->env;
uint64_t token;
- target_ulong v, r, rb;
+ target_ulong v, r;
if (!valid_pte_index(env, ptex)) {
return REMOVE_PARM;
}
- token = ppc_hash64_start_access(ppc_env_get_cpu(env), ptex);
- v = ppc_hash64_load_hpte0(env, token, 0);
- r = ppc_hash64_load_hpte1(env, token, 0);
- ppc_hash64_stop_access(token);
+ token = ppc_hash64_start_access(cpu, ptex);
+ v = ppc_hash64_load_hpte0(cpu, token, 0);
+ r = ppc_hash64_load_hpte1(cpu, token, 0);
+ ppc_hash64_stop_access(cpu, token);
if ((v & HPTE64_V_VALID) == 0 ||
((flags & H_AVPN) && (v & ~0x7fULL) != avpn) ||
@@ -200,22 +175,20 @@ static RemoveResult remove_hpte(CPUPPCState *env, target_ulong ptex,
}
*vp = v;
*rp = r;
- ppc_hash64_store_hpte(env, ptex, HPTE64_V_HPTE_DIRTY, 0);
- rb = compute_tlbie_rb(v, r, ptex);
- ppc_tlb_invalidate_one(env, rb);
+ ppc_hash64_store_hpte(cpu, ptex, HPTE64_V_HPTE_DIRTY, 0);
+ ppc_hash64_tlb_flush_hpte(cpu, ptex, v, r);
return REMOVE_SUCCESS;
}
static target_ulong h_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
- CPUPPCState *env = &cpu->env;
target_ulong flags = args[0];
target_ulong pte_index = args[1];
target_ulong avpn = args[2];
RemoveResult ret;
- ret = remove_hpte(env, pte_index, avpn, flags,
+ ret = remove_hpte(cpu, pte_index, avpn, flags,
&args[0], &args[1]);
switch (ret) {
@@ -256,7 +229,6 @@ static target_ulong h_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr,
static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
- CPUPPCState *env = &cpu->env;
int i;
for (i = 0; i < H_BULK_REMOVE_MAX_BATCH; i++) {
@@ -278,7 +250,7 @@ static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr,
return H_PARAMETER;
}
- ret = remove_hpte(env, *tsh & H_BULK_REMOVE_PTEX, tsl,
+ ret = remove_hpte(cpu, *tsh & H_BULK_REMOVE_PTEX, tsl,
(*tsh & H_BULK_REMOVE_FLAGS) >> 26,
&v, &r);
@@ -308,16 +280,16 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong pte_index = args[1];
target_ulong avpn = args[2];
uint64_t token;
- target_ulong v, r, rb;
+ target_ulong v, r;
if (!valid_pte_index(env, pte_index)) {
return H_PARAMETER;
}
token = ppc_hash64_start_access(cpu, pte_index);
- v = ppc_hash64_load_hpte0(env, token, 0);
- r = ppc_hash64_load_hpte1(env, token, 0);
- ppc_hash64_stop_access(token);
+ v = ppc_hash64_load_hpte0(cpu, token, 0);
+ r = ppc_hash64_load_hpte1(cpu, token, 0);
+ ppc_hash64_stop_access(cpu, token);
if ((v & HPTE64_V_VALID) == 0 ||
((flags & H_AVPN) && (v & ~0x7fULL) != avpn)) {
@@ -329,12 +301,11 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPRMachineState *spapr,
r |= (flags << 55) & HPTE64_R_PP0;
r |= (flags << 48) & HPTE64_R_KEY_HI;
r |= flags & (HPTE64_R_PP | HPTE64_R_N | HPTE64_R_KEY_LO);
- rb = compute_tlbie_rb(v, r, pte_index);
- ppc_hash64_store_hpte(env, pte_index,
+ ppc_hash64_store_hpte(cpu, pte_index,
(v & ~HPTE64_V_VALID) | HPTE64_V_HPTE_DIRTY, 0);
- ppc_tlb_invalidate_one(env, rb);
+ ppc_hash64_tlb_flush_hpte(cpu, pte_index, v, r);
/* Don't need a memory barrier, due to qemu's global lock */
- ppc_hash64_store_hpte(env, pte_index, v | HPTE64_V_HPTE_DIRTY, r);
+ ppc_hash64_store_hpte(cpu, pte_index, v | HPTE64_V_HPTE_DIRTY, r);
return H_SUCCESS;
}
@@ -368,11 +339,111 @@ static target_ulong h_read(PowerPCCPU *cpu, sPAPRMachineState *spapr,
return H_SUCCESS;
}
+static target_ulong h_set_sprg0(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ cpu_synchronize_state(CPU(cpu));
+ cpu->env.spr[SPR_SPRG0] = args[0];
+
+ return H_SUCCESS;
+}
+
static target_ulong h_set_dabr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
- /* FIXME: actually implement this */
- return H_HARDWARE;
+ if (!has_spr(cpu, SPR_DABR)) {
+ return H_HARDWARE; /* DABR register not available */
+ }
+ cpu_synchronize_state(CPU(cpu));
+
+ if (has_spr(cpu, SPR_DABRX)) {
+ cpu->env.spr[SPR_DABRX] = 0x3; /* Use Problem and Privileged state */
+ } else if (!(args[0] & 0x4)) { /* Breakpoint Translation set? */
+ return H_RESERVED_DABR;
+ }
+
+ cpu->env.spr[SPR_DABR] = args[0];
+ return H_SUCCESS;
+}
+
+static target_ulong h_set_xdabr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ target_ulong dabrx = args[1];
+
+ if (!has_spr(cpu, SPR_DABR) || !has_spr(cpu, SPR_DABRX)) {
+ return H_HARDWARE;
+ }
+
+ if ((dabrx & ~0xfULL) != 0 || (dabrx & H_DABRX_HYPERVISOR) != 0
+ || (dabrx & (H_DABRX_KERNEL | H_DABRX_USER)) == 0) {
+ return H_PARAMETER;
+ }
+
+ cpu_synchronize_state(CPU(cpu));
+ cpu->env.spr[SPR_DABRX] = dabrx;
+ cpu->env.spr[SPR_DABR] = args[0];
+
+ return H_SUCCESS;
+}
+
+static target_ulong h_page_init(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ target_ulong flags = args[0];
+ hwaddr dst = args[1];
+ hwaddr src = args[2];
+ hwaddr len = TARGET_PAGE_SIZE;
+ uint8_t *pdst, *psrc;
+ target_long ret = H_SUCCESS;
+
+ if (flags & ~(H_ICACHE_SYNCHRONIZE | H_ICACHE_INVALIDATE
+ | H_COPY_PAGE | H_ZERO_PAGE)) {
+ qemu_log_mask(LOG_UNIMP, "h_page_init: Bad flags (" TARGET_FMT_lx "\n",
+ flags);
+ return H_PARAMETER;
+ }
+
+ /* Map-in destination */
+ if (!is_ram_address(spapr, dst) || (dst & ~TARGET_PAGE_MASK) != 0) {
+ return H_PARAMETER;
+ }
+ pdst = cpu_physical_memory_map(dst, &len, 1);
+ if (!pdst || len != TARGET_PAGE_SIZE) {
+ return H_PARAMETER;
+ }
+
+ if (flags & H_COPY_PAGE) {
+ /* Map-in source, copy to destination, and unmap source again */
+ if (!is_ram_address(spapr, src) || (src & ~TARGET_PAGE_MASK) != 0) {
+ ret = H_PARAMETER;
+ goto unmap_out;
+ }
+ psrc = cpu_physical_memory_map(src, &len, 0);
+ if (!psrc || len != TARGET_PAGE_SIZE) {
+ ret = H_PARAMETER;
+ goto unmap_out;
+ }
+ memcpy(pdst, psrc, len);
+ cpu_physical_memory_unmap(psrc, len, 0, len);
+ } else if (flags & H_ZERO_PAGE) {
+ memset(pdst, 0, len); /* Just clear the destination page */
+ }
+
+ if (kvm_enabled() && (flags & H_ICACHE_SYNCHRONIZE) != 0) {
+ kvmppc_dcbst_range(cpu, pdst, len);
+ }
+ if (flags & (H_ICACHE_SYNCHRONIZE | H_ICACHE_INVALIDATE)) {
+ if (kvm_enabled()) {
+ kvmppc_icbi_range(cpu, pdst, len);
+ } else {
+ tb_flush(CPU(cpu));
+ }
+ }
+
+unmap_out:
+ cpu_physical_memory_unmap(pdst, TARGET_PAGE_SIZE, 1, len);
+ return ret;
}
#define FLAGS_REGISTER_VPA 0x0000200000000000ULL
@@ -753,7 +824,6 @@ static target_ulong h_set_mode_resource_addr_trans_mode(PowerPCCPU *cpu,
{
CPUState *cs;
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
- target_ulong prefix;
if (!(pcc->insns_flags2 & PPC2_ISA207S)) {
return H_P2;
@@ -765,25 +835,12 @@ static target_ulong h_set_mode_resource_addr_trans_mode(PowerPCCPU *cpu,
return H_P4;
}
- switch (mflags) {
- case H_SET_MODE_ADDR_TRANS_NONE:
- prefix = 0;
- break;
- case H_SET_MODE_ADDR_TRANS_0001_8000:
- prefix = 0x18000;
- break;
- case H_SET_MODE_ADDR_TRANS_C000_0000_0000_4000:
- prefix = 0xC000000000004000ULL;
- break;
- default:
+ if (mflags == AIL_RESERVED) {
return H_UNSUPPORTED_FLAG;
}
CPU_FOREACH(cs) {
- CPUPPCState *env = &POWERPC_CPU(cpu)->env;
-
set_spr(cs, SPR_LPCR, mflags << LPCR_AIL_SHIFT, LPCR_AIL);
- env->excp_prefix = prefix;
}
return H_SUCCESS;
@@ -808,10 +865,36 @@ static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPRMachineState *spapr,
return ret;
}
+/*
+ * Return the offset to the requested option vector @vector in the
+ * option vector table @table.
+ */
+static target_ulong cas_get_option_vector(int vector, target_ulong table)
+{
+ int i;
+ char nr_vectors, nr_entries;
+
+ if (!table) {
+ return 0;
+ }
+
+ nr_vectors = (ldl_phys(&address_space_memory, table) >> 24) + 1;
+ if (!vector || vector > nr_vectors) {
+ return 0;
+ }
+ table++; /* skip nr option vectors */
+
+ for (i = 0; i < vector - 1; i++) {
+ nr_entries = ldl_phys(&address_space_memory, table) >> 24;
+ table += nr_entries + 2;
+ }
+ return table;
+}
+
typedef struct {
PowerPCCPU *cpu;
uint32_t cpu_version;
- int ret;
+ Error *err;
} SetCompatState;
static void do_set_compat(void *arg)
@@ -819,7 +902,7 @@ static void do_set_compat(void *arg)
SetCompatState *s = arg;
cpu_synchronize_state(CPU(s->cpu));
- s->ret = ppc_set_compat(s->cpu, s->cpu_version);
+ ppc_set_compat(s->cpu, s->cpu_version, &s->err);
}
#define get_compat_level(cpuver) ( \
@@ -828,27 +911,31 @@ static void do_set_compat(void *arg)
((cpuver) == CPU_POWERPC_LOGICAL_2_06_PLUS) ? 2061 : \
((cpuver) == CPU_POWERPC_LOGICAL_2_07) ? 2070 : 0)
+#define OV5_DRCONF_MEMORY 0x20
+
static target_ulong h_client_architecture_support(PowerPCCPU *cpu_,
sPAPRMachineState *spapr,
target_ulong opcode,
target_ulong *args)
{
- target_ulong list = args[0];
+ target_ulong list = ppc64_phys_to_real(args[0]);
+ target_ulong ov_table, ov5;
PowerPCCPUClass *pcc_ = POWERPC_CPU_GET_CLASS(cpu_);
CPUState *cs;
- bool cpu_match = false;
+ bool cpu_match = false, cpu_update = true, memory_update = false;
unsigned old_cpu_version = cpu_->cpu_version;
unsigned compat_lvl = 0, cpu_version = 0;
unsigned max_lvl = get_compat_level(cpu_->max_compat);
int counter;
+ char ov5_byte2;
/* Parse PVR list */
for (counter = 0; counter < 512; ++counter) {
uint32_t pvr, pvr_mask;
- pvr_mask = rtas_ld(list, 0);
+ pvr_mask = ldl_be_phys(&address_space_memory, list);
list += 4;
- pvr = rtas_ld(list, 0);
+ pvr = ldl_be_phys(&address_space_memory, list);
list += 4;
trace_spapr_cas_pvr_try(pvr);
@@ -890,8 +977,6 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu_,
}
}
- /* For the future use: here @list points to the first capability */
-
/* Parsing finished */
trace_spapr_cas_pvr(cpu_->cpu_version, cpu_match,
cpu_version, pcc_->pcr_mask);
@@ -902,27 +987,38 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu_,
SetCompatState s = {
.cpu = POWERPC_CPU(cs),
.cpu_version = cpu_version,
- .ret = 0
+ .err = NULL,
};
run_on_cpu(cs, do_set_compat, &s);
- if (s.ret < 0) {
- fprintf(stderr, "Unable to set compatibility mode\n");
+ if (s.err) {
+ error_report_err(s.err);
return H_HARDWARE;
}
}
}
if (!cpu_version) {
- return H_SUCCESS;
+ cpu_update = false;
}
- if (!list) {
+ /* For the future use: here @ov_table points to the first option vector */
+ ov_table = list;
+
+ ov5 = cas_get_option_vector(5, ov_table);
+ if (!ov5) {
return H_SUCCESS;
}
- if (spapr_h_cas_compose_response(spapr, args[1], args[2])) {
+ /* @list now points to OV 5 */
+ ov5_byte2 = ldub_phys(&address_space_memory, ov5 + 2);
+ if (ov5_byte2 & OV5_DRCONF_MEMORY) {
+ memory_update = true;
+ }
+
+ if (spapr_h_cas_compose_response(spapr, args[1], args[2],
+ cpu_update, memory_update)) {
qemu_system_reset_request();
}
@@ -971,7 +1067,8 @@ target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode,
}
}
- hcall_dprintf("Unimplemented hcall 0x" TARGET_FMT_lx "\n", opcode);
+ qemu_log_mask(LOG_UNIMP, "Unimplemented SPAPR hcall 0x" TARGET_FMT_lx "\n",
+ opcode);
return H_FUNCTION;
}
@@ -986,13 +1083,17 @@ static void hypercall_register_types(void)
/* hcall-bulk */
spapr_register_hypercall(H_BULK_REMOVE, h_bulk_remove);
- /* hcall-dabr */
- spapr_register_hypercall(H_SET_DABR, h_set_dabr);
-
/* hcall-splpar */
spapr_register_hypercall(H_REGISTER_VPA, h_register_vpa);
spapr_register_hypercall(H_CEDE, h_cede);
+ /* processor register resource access h-calls */
+ spapr_register_hypercall(H_SET_SPRG0, h_set_sprg0);
+ spapr_register_hypercall(H_SET_DABR, h_set_dabr);
+ spapr_register_hypercall(H_SET_XDABR, h_set_xdabr);
+ spapr_register_hypercall(H_PAGE_INIT, h_page_init);
+ spapr_register_hypercall(H_SET_MODE, h_set_mode);
+
/* "debugger" hcalls (also used by SLOF). Note: We do -not- differenciate
* here between the "CI" and the "CACHE" variants, they will use whatever
* mapping attributes qemu is using. When using KVM, the kernel will
@@ -1009,8 +1110,6 @@ static void hypercall_register_types(void)
/* qemu/KVM-PPC specific hcalls */
spapr_register_hypercall(KVMPPC_H_RTAS, h_rtas);
- spapr_register_hypercall(H_SET_MODE, h_set_mode);
-
/* ibm,client-architecture-support support */
spapr_register_hypercall(KVMPPC_H_CAS, h_client_architecture_support);
}
diff --git a/qemu/hw/ppc/spapr_iommu.c b/qemu/hw/ppc/spapr_iommu.c
index f61504e0c..7dd458846 100644
--- a/qemu/hw/ppc/spapr_iommu.c
+++ b/qemu/hw/ppc/spapr_iommu.c
@@ -16,6 +16,7 @@
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "sysemu/kvm.h"
#include "hw/qdev.h"
@@ -146,7 +147,7 @@ static int spapr_tce_table_realize(DeviceState *dev)
tcet->table = kvmppc_create_spapr_tce(tcet->liobn,
window_size,
&tcet->fd,
- tcet->vfio_accel);
+ tcet->need_vfio);
}
if (!tcet->table) {
@@ -168,11 +169,43 @@ static int spapr_tce_table_realize(DeviceState *dev)
return 0;
}
+void spapr_tce_set_need_vfio(sPAPRTCETable *tcet, bool need_vfio)
+{
+ size_t table_size = tcet->nb_table * sizeof(uint64_t);
+ void *newtable;
+
+ if (need_vfio == tcet->need_vfio) {
+ /* Nothing to do */
+ return;
+ }
+
+ if (!need_vfio) {
+ /* FIXME: We don't support transition back to KVM accelerated
+ * TCEs yet */
+ return;
+ }
+
+ tcet->need_vfio = true;
+
+ if (tcet->fd < 0) {
+ /* Table is already in userspace, nothing to be do */
+ return;
+ }
+
+ newtable = g_malloc(table_size);
+ memcpy(newtable, tcet->table, table_size);
+
+ kvmppc_remove_spapr_tce(tcet->table, tcet->fd, tcet->nb_table);
+
+ tcet->fd = -1;
+ tcet->table = newtable;
+}
+
sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn,
uint64_t bus_offset,
uint32_t page_shift,
uint32_t nb_table,
- bool vfio_accel)
+ bool need_vfio)
{
sPAPRTCETable *tcet;
char tmp[64];
@@ -192,7 +225,7 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn,
tcet->bus_offset = bus_offset;
tcet->page_shift = page_shift;
tcet->nb_table = nb_table;
- tcet->vfio_accel = vfio_accel;
+ tcet->need_vfio = need_vfio;
snprintf(tmp, sizeof(tmp), "tce-table-%x", liobn);
object_property_add_child(OBJECT(owner), tmp, OBJECT(tcet), NULL);
diff --git a/qemu/hw/ppc/spapr_pci.c b/qemu/hw/ppc/spapr_pci.c
index a8f79d800..573e635bf 100644
--- a/qemu/hw/ppc/spapr_pci.c
+++ b/qemu/hw/ppc/spapr_pci.c
@@ -22,6 +22,10 @@
* 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-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "hw/pci/pci.h"
@@ -41,6 +45,8 @@
#include "hw/ppc/spapr_drc.h"
#include "sysemu/device_tree.h"
+#include "hw/vfio/vfio.h"
+
/* Copied from the kernel arch/powerpc/platforms/pseries/msi.c */
#define RTAS_QUERY_FN 0
#define RTAS_CHANGE_FN 1
@@ -140,7 +146,7 @@ static void rtas_ibm_read_pci_config(PowerPCCPU *cpu, sPAPRMachineState *spapr,
return;
}
- buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ buid = rtas_ldq(args, 1);
size = rtas_ld(args, 3);
addr = rtas_ld(args, 0);
@@ -206,7 +212,7 @@ static void rtas_ibm_write_pci_config(PowerPCCPU *cpu, sPAPRMachineState *spapr,
return;
}
- buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ buid = rtas_ldq(args, 1);
val = rtas_ld(args, 4);
size = rtas_ld(args, 3);
addr = rtas_ld(args, 0);
@@ -269,16 +275,17 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong rets)
{
uint32_t config_addr = rtas_ld(args, 0);
- uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ uint64_t buid = rtas_ldq(args, 1);
unsigned int func = rtas_ld(args, 3);
unsigned int req_num = rtas_ld(args, 4); /* 0 == remove all */
unsigned int seq_num = rtas_ld(args, 5);
unsigned int ret_intr_type;
- unsigned int irq, max_irqs = 0, num = 0;
+ unsigned int irq, max_irqs = 0;
sPAPRPHBState *phb = NULL;
PCIDevice *pdev = NULL;
spapr_pci_msi *msi;
int *config_addr_key;
+ Error *err = NULL;
switch (func) {
case RTAS_CHANGE_MSI_FN:
@@ -304,9 +311,10 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
return;
}
+ msi = (spapr_pci_msi *) g_hash_table_lookup(phb->msi, &config_addr);
+
/* Releasing MSIs */
if (!req_num) {
- msi = (spapr_pci_msi *) g_hash_table_lookup(phb->msi, &config_addr);
if (!msi) {
trace_spapr_pci_msi("Releasing wrong config", config_addr);
rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
@@ -315,10 +323,10 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
xics_free(spapr->icp, msi->first_irq, msi->num);
if (msi_present(pdev)) {
- spapr_msi_setmsg(pdev, 0, false, 0, num);
+ spapr_msi_setmsg(pdev, 0, false, 0, 0);
}
if (msix_present(pdev)) {
- spapr_msi_setmsg(pdev, 0, true, 0, num);
+ spapr_msi_setmsg(pdev, 0, true, 0, 0);
}
g_hash_table_remove(phb->msi, &config_addr);
@@ -352,13 +360,20 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
/* Allocate MSIs */
irq = xics_alloc_block(spapr->icp, 0, req_num, false,
- ret_intr_type == RTAS_TYPE_MSI);
- if (!irq) {
- error_report("Cannot allocate MSIs for device %x", config_addr);
+ ret_intr_type == RTAS_TYPE_MSI, &err);
+ if (err) {
+ error_reportf_err(err, "Can't allocate MSIs for device %x: ",
+ config_addr);
rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
return;
}
+ /* Release previous MSIs */
+ if (msi) {
+ xics_free(spapr->icp, msi->first_irq, msi->num);
+ g_hash_table_remove(phb->msi, &config_addr);
+ }
+
/* Setup MSI/MSIX vectors in the device (via cfgspace or MSIX BAR) */
spapr_msi_setmsg(pdev, SPAPR_PCI_MSI_WINDOW, ret_intr_type == RTAS_TYPE_MSIX,
irq, req_num);
@@ -375,7 +390,9 @@ out:
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
rtas_st(rets, 1, req_num);
rtas_st(rets, 2, ++seq_num);
- rtas_st(rets, 3, ret_intr_type);
+ if (nret > 3) {
+ rtas_st(rets, 3, ret_intr_type);
+ }
trace_spapr_pci_rtas_ibm_change_msi(config_addr, func, req_num, irq);
}
@@ -389,7 +406,7 @@ static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu,
target_ulong rets)
{
uint32_t config_addr = rtas_ld(args, 0);
- uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ uint64_t buid = rtas_ldq(args, 1);
unsigned int intr_src_num = -1, ioa_intr_num = rtas_ld(args, 3);
sPAPRPHBState *phb = NULL;
PCIDevice *pdev = NULL;
@@ -428,8 +445,6 @@ static void rtas_ibm_set_eeh_option(PowerPCCPU *cpu,
target_ulong rets)
{
sPAPRPHBState *sphb;
- sPAPRPHBClass *spc;
- PCIDevice *pdev;
uint32_t addr, option;
uint64_t buid;
int ret;
@@ -438,7 +453,7 @@ static void rtas_ibm_set_eeh_option(PowerPCCPU *cpu,
goto param_error_exit;
}
- buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ buid = rtas_ldq(args, 1);
addr = rtas_ld(args, 0);
option = rtas_ld(args, 3);
@@ -447,18 +462,11 @@ static void rtas_ibm_set_eeh_option(PowerPCCPU *cpu,
goto param_error_exit;
}
- pdev = pci_find_device(PCI_HOST_BRIDGE(sphb)->bus,
- (addr >> 16) & 0xFF, (addr >> 8) & 0xFF);
- if (!pdev || !object_dynamic_cast(OBJECT(pdev), "vfio-pci")) {
+ if (!spapr_phb_eeh_available(sphb)) {
goto param_error_exit;
}
- spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb);
- if (!spc->eeh_set_option) {
- goto param_error_exit;
- }
-
- ret = spc->eeh_set_option(sphb, addr, option);
+ ret = spapr_phb_vfio_eeh_set_option(sphb, addr, option);
rtas_st(rets, 0, ret);
return;
@@ -473,7 +481,6 @@ static void rtas_ibm_get_config_addr_info2(PowerPCCPU *cpu,
target_ulong rets)
{
sPAPRPHBState *sphb;
- sPAPRPHBClass *spc;
PCIDevice *pdev;
uint32_t addr, option;
uint64_t buid;
@@ -482,14 +489,13 @@ static void rtas_ibm_get_config_addr_info2(PowerPCCPU *cpu,
goto param_error_exit;
}
- buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ buid = rtas_ldq(args, 1);
sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
}
- spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb);
- if (!spc->eeh_set_option) {
+ if (!spapr_phb_eeh_available(sphb)) {
goto param_error_exit;
}
@@ -529,7 +535,6 @@ static void rtas_ibm_read_slot_reset_state2(PowerPCCPU *cpu,
target_ulong rets)
{
sPAPRPHBState *sphb;
- sPAPRPHBClass *spc;
uint64_t buid;
int state, ret;
@@ -537,18 +542,17 @@ static void rtas_ibm_read_slot_reset_state2(PowerPCCPU *cpu,
goto param_error_exit;
}
- buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ buid = rtas_ldq(args, 1);
sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
}
- spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb);
- if (!spc->eeh_get_state) {
+ if (!spapr_phb_eeh_available(sphb)) {
goto param_error_exit;
}
- ret = spc->eeh_get_state(sphb, &state);
+ ret = spapr_phb_vfio_eeh_get_state(sphb, &state);
rtas_st(rets, 0, ret);
if (ret != RTAS_OUT_SUCCESS) {
return;
@@ -573,7 +577,6 @@ static void rtas_ibm_set_slot_reset(PowerPCCPU *cpu,
target_ulong rets)
{
sPAPRPHBState *sphb;
- sPAPRPHBClass *spc;
uint32_t option;
uint64_t buid;
int ret;
@@ -582,19 +585,18 @@ static void rtas_ibm_set_slot_reset(PowerPCCPU *cpu,
goto param_error_exit;
}
- buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ buid = rtas_ldq(args, 1);
option = rtas_ld(args, 3);
sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
}
- spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb);
- if (!spc->eeh_reset) {
+ if (!spapr_phb_eeh_available(sphb)) {
goto param_error_exit;
}
- ret = spc->eeh_reset(sphb, option);
+ ret = spapr_phb_vfio_eeh_reset(sphb, option);
rtas_st(rets, 0, ret);
return;
@@ -609,7 +611,6 @@ static void rtas_ibm_configure_pe(PowerPCCPU *cpu,
target_ulong rets)
{
sPAPRPHBState *sphb;
- sPAPRPHBClass *spc;
uint64_t buid;
int ret;
@@ -617,18 +618,17 @@ static void rtas_ibm_configure_pe(PowerPCCPU *cpu,
goto param_error_exit;
}
- buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ buid = rtas_ldq(args, 1);
sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
}
- spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb);
- if (!spc->eeh_configure) {
+ if (!spapr_phb_eeh_available(sphb)) {
goto param_error_exit;
}
- ret = spc->eeh_configure(sphb);
+ ret = spapr_phb_vfio_eeh_configure(sphb);
rtas_st(rets, 0, ret);
return;
@@ -644,7 +644,6 @@ static void rtas_ibm_slot_error_detail(PowerPCCPU *cpu,
target_ulong rets)
{
sPAPRPHBState *sphb;
- sPAPRPHBClass *spc;
int option;
uint64_t buid;
@@ -652,14 +651,13 @@ static void rtas_ibm_slot_error_detail(PowerPCCPU *cpu,
goto param_error_exit;
}
- buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ buid = rtas_ldq(args, 1);
sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
}
- spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb);
- if (!spc->eeh_set_option) {
+ if (!spapr_phb_eeh_available(sphb)) {
goto param_error_exit;
}
@@ -955,6 +953,7 @@ static int spapr_populate_pci_child_dt(PCIDevice *dev, void *fdt, int offset,
int pci_status, err;
char *buf = NULL;
uint32_t drc_index = spapr_phb_get_pci_drc_index(sphb, dev);
+ uint32_t max_msi, max_msix;
if (pci_default_read_config(dev, PCI_HEADER_TYPE, 1) ==
PCI_HEADER_TYPE_BRIDGE) {
@@ -1035,8 +1034,15 @@ static int spapr_populate_pci_child_dt(PCIDevice *dev, void *fdt, int offset,
RESOURCE_CELLS_ADDRESS));
_FDT(fdt_setprop_cell(fdt, offset, "#size-cells",
RESOURCE_CELLS_SIZE));
- _FDT(fdt_setprop_cell(fdt, offset, "ibm,req#msi-x",
- RESOURCE_CELLS_SIZE));
+
+ max_msi = msi_nr_vectors_allocated(dev);
+ if (max_msi) {
+ _FDT(fdt_setprop_cell(fdt, offset, "ibm,req#msi", max_msi));
+ }
+ max_msix = dev->msix_entries_nr;
+ if (max_msix) {
+ _FDT(fdt_setprop_cell(fdt, offset, "ibm,req#msi-x", max_msix));
+ }
populate_resource_props(dev, &rp);
_FDT(fdt_setprop(fdt, offset, "reg", (uint8_t *)rp.reg, rp.reg_len));
@@ -1080,6 +1086,12 @@ static void spapr_phb_add_pci_device(sPAPRDRConnector *drc,
void *fdt = NULL;
int fdt_start_offset = 0, fdt_size;
+ if (object_dynamic_cast(OBJECT(pdev), "vfio-pci")) {
+ sPAPRTCETable *tcet = spapr_tce_find_by_liobn(phb->dma_liobn);
+
+ spapr_tce_set_need_vfio(tcet, true);
+ }
+
if (dev->hotplugged) {
fdt = create_device_tree(&fdt_size);
fdt_start_offset = spapr_create_pci_child_dt(phb, pdev, fdt, 0);
@@ -1123,14 +1135,21 @@ static void spapr_phb_remove_pci_device(sPAPRDRConnector *drc,
drck->detach(drc, DEVICE(pdev), spapr_phb_remove_pci_device_cb, phb, errp);
}
-static sPAPRDRConnector *spapr_phb_get_pci_drc(sPAPRPHBState *phb,
- PCIDevice *pdev)
+static sPAPRDRConnector *spapr_phb_get_pci_func_drc(sPAPRPHBState *phb,
+ uint32_t busnr,
+ int32_t devfn)
{
- uint32_t busnr = pci_bus_num(PCI_BUS(qdev_get_parent_bus(DEVICE(pdev))));
return spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_PCI,
(phb->index << 16) |
(busnr << 8) |
- pdev->devfn);
+ devfn);
+}
+
+static sPAPRDRConnector *spapr_phb_get_pci_drc(sPAPRPHBState *phb,
+ PCIDevice *pdev)
+{
+ uint32_t busnr = pci_bus_num(PCI_BUS(qdev_get_parent_bus(DEVICE(pdev))));
+ return spapr_phb_get_pci_func_drc(phb, busnr, pdev->devfn);
}
static uint32_t spapr_phb_get_pci_drc_index(sPAPRPHBState *phb,
@@ -1154,6 +1173,8 @@ static void spapr_phb_hot_plug_child(HotplugHandler *plug_handler,
PCIDevice *pdev = PCI_DEVICE(plugged_dev);
sPAPRDRConnector *drc = spapr_phb_get_pci_drc(phb, pdev);
Error *local_err = NULL;
+ PCIBus *bus = PCI_BUS(qdev_get_parent_bus(DEVICE(pdev)));
+ uint32_t slotnr = PCI_SLOT(pdev->devfn);
/* if DR is disabled we don't need to do anything in the case of
* hotplug or coldplug callbacks
@@ -1171,13 +1192,44 @@ static void spapr_phb_hot_plug_child(HotplugHandler *plug_handler,
g_assert(drc);
+ /* Following the QEMU convention used for PCIe multifunction
+ * hotplug, we do not allow functions to be hotplugged to a
+ * slot that already has function 0 present
+ */
+ if (plugged_dev->hotplugged && bus->devices[PCI_DEVFN(slotnr, 0)] &&
+ PCI_FUNC(pdev->devfn) != 0) {
+ error_setg(errp, "PCI: slot %d function 0 already ocuppied by %s,"
+ " additional functions can no longer be exposed to guest.",
+ slotnr, bus->devices[PCI_DEVFN(slotnr, 0)]->name);
+ return;
+ }
+
spapr_phb_add_pci_device(drc, phb, pdev, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
- if (plugged_dev->hotplugged) {
- spapr_hotplug_req_add_event(drc);
+
+ /* If this is function 0, signal hotplug for all the device functions.
+ * Otherwise defer sending the hotplug event.
+ */
+ if (plugged_dev->hotplugged && PCI_FUNC(pdev->devfn) == 0) {
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ sPAPRDRConnector *func_drc;
+ sPAPRDRConnectorClass *func_drck;
+ sPAPRDREntitySense state;
+
+ func_drc = spapr_phb_get_pci_func_drc(phb, pci_bus_num(bus),
+ PCI_DEVFN(slotnr, i));
+ func_drck = SPAPR_DR_CONNECTOR_GET_CLASS(func_drc);
+ func_drck->entity_sense(func_drc, &state);
+
+ if (state == SPAPR_DR_ENTITY_SENSE_PRESENT) {
+ spapr_hotplug_req_add_by_index(func_drc);
+ }
+ }
}
}
@@ -1200,12 +1252,51 @@ static void spapr_phb_hot_unplug_child(HotplugHandler *plug_handler,
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
if (!drck->release_pending(drc)) {
+ PCIBus *bus = PCI_BUS(qdev_get_parent_bus(DEVICE(pdev)));
+ uint32_t slotnr = PCI_SLOT(pdev->devfn);
+ sPAPRDRConnector *func_drc;
+ sPAPRDRConnectorClass *func_drck;
+ sPAPRDREntitySense state;
+ int i;
+
+ /* ensure any other present functions are pending unplug */
+ if (PCI_FUNC(pdev->devfn) == 0) {
+ for (i = 1; i < 8; i++) {
+ func_drc = spapr_phb_get_pci_func_drc(phb, pci_bus_num(bus),
+ PCI_DEVFN(slotnr, i));
+ func_drck = SPAPR_DR_CONNECTOR_GET_CLASS(func_drc);
+ func_drck->entity_sense(func_drc, &state);
+ if (state == SPAPR_DR_ENTITY_SENSE_PRESENT
+ && !func_drck->release_pending(func_drc)) {
+ error_setg(errp,
+ "PCI: slot %d, function %d still present. "
+ "Must unplug all non-0 functions first.",
+ slotnr, i);
+ return;
+ }
+ }
+ }
+
spapr_phb_remove_pci_device(drc, phb, pdev, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
- spapr_hotplug_req_remove_event(drc);
+
+ /* if this isn't func 0, defer unplug event. otherwise signal removal
+ * for all present functions
+ */
+ if (PCI_FUNC(pdev->devfn) == 0) {
+ for (i = 7; i >= 0; i--) {
+ func_drc = spapr_phb_get_pci_func_drc(phb, pci_bus_num(bus),
+ PCI_DEVFN(slotnr, i));
+ func_drck = SPAPR_DR_CONNECTOR_GET_CLASS(func_drc);
+ func_drck->entity_sense(func_drc, &state);
+ if (state == SPAPR_DR_ENTITY_SENSE_PRESENT) {
+ spapr_hotplug_req_remove_by_index(func_drc);
+ }
+ }
+ }
}
}
@@ -1215,11 +1306,12 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
SysBusDevice *s = SYS_BUS_DEVICE(dev);
sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(s);
PCIHostState *phb = PCI_HOST_BRIDGE(s);
- sPAPRPHBClass *info = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(s);
char *namebuf;
int i;
PCIBus *bus;
uint64_t msi_window_size = 4096;
+ sPAPRTCETable *tcet;
+ uint32_t nb_table;
if (sphb->index != (uint32_t)-1) {
hwaddr windows_base;
@@ -1350,10 +1442,12 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
/* Initialize the LSI table */
for (i = 0; i < PCI_NUM_PINS; i++) {
uint32_t irq;
+ Error *local_err = NULL;
- irq = xics_alloc_block(spapr->icp, 0, 1, true, false);
- if (!irq) {
- error_setg(errp, "spapr_allocate_lsi failed");
+ irq = xics_alloc_block(spapr->icp, 0, 1, true, false, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ error_prepend(errp, "can't allocate LSIs: ");
return;
}
@@ -1369,33 +1463,20 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
}
}
- if (!info->finish_realize) {
- error_setg(errp, "finish_realize not defined");
- return;
- }
-
- info->finish_realize(sphb, errp);
-
- sphb->msi = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free);
-}
-
-static void spapr_phb_finish_realize(sPAPRPHBState *sphb, Error **errp)
-{
- sPAPRTCETable *tcet;
- uint32_t nb_table;
-
- nb_table = SPAPR_PCI_DMA32_SIZE >> SPAPR_TCE_PAGE_SHIFT;
+ nb_table = sphb->dma_win_size >> SPAPR_TCE_PAGE_SHIFT;
tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn,
0, SPAPR_TCE_PAGE_SHIFT, nb_table, false);
if (!tcet) {
error_setg(errp, "Unable to create TCE table for %s",
sphb->dtbusname);
- return ;
+ return;
}
/* Register default 32bit DMA window */
- memory_region_add_subregion(&sphb->iommu_root, 0,
+ memory_region_add_subregion(&sphb->iommu_root, sphb->dma_win_addr,
spapr_tce_get_iommu(tcet));
+
+ sphb->msi = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free);
}
static int spapr_phb_children_reset(Object *child, void *opaque)
@@ -1413,6 +1494,10 @@ static void spapr_phb_reset(DeviceState *qdev)
{
/* Reset the IOMMU state */
object_child_foreach(OBJECT(qdev), spapr_phb_children_reset, NULL);
+
+ if (spapr_phb_eeh_available(SPAPR_PCI_HOST_BRIDGE(qdev))) {
+ spapr_phb_vfio_reset(qdev);
+ }
}
static Property spapr_phb_properties[] = {
@@ -1427,6 +1512,9 @@ static Property spapr_phb_properties[] = {
SPAPR_PCI_IO_WIN_SIZE),
DEFINE_PROP_BOOL("dynamic-reconfiguration", sPAPRPHBState, dr_enabled,
true),
+ /* Default DMA window is 0..1GB */
+ DEFINE_PROP_UINT64("dma_win_addr", sPAPRPHBState, dma_win_addr, 0),
+ DEFINE_PROP_UINT64("dma_win_size", sPAPRPHBState, dma_win_size, 0x40000000),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1460,10 +1548,8 @@ static void spapr_pci_pre_save(void *opaque)
gpointer key, value;
int i;
- if (sphb->msi_devs) {
- g_free(sphb->msi_devs);
- sphb->msi_devs = NULL;
- }
+ g_free(sphb->msi_devs);
+ sphb->msi_devs = NULL;
sphb->msi_devs_num = g_hash_table_size(sphb->msi);
if (!sphb->msi_devs_num) {
return;
@@ -1490,10 +1576,8 @@ static int spapr_pci_post_load(void *opaque, int version_id)
sizeof(sphb->msi_devs[i].value));
g_hash_table_insert(sphb->msi, key, value);
}
- if (sphb->msi_devs) {
- g_free(sphb->msi_devs);
- sphb->msi_devs = NULL;
- }
+ g_free(sphb->msi_devs);
+ sphb->msi_devs = NULL;
sphb->msi_devs_num = 0;
return 0;
@@ -1533,7 +1617,6 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data)
{
PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
- sPAPRPHBClass *spc = SPAPR_PCI_HOST_BRIDGE_CLASS(klass);
HotplugHandlerClass *hp = HOTPLUG_HANDLER_CLASS(klass);
hc->root_bus_path = spapr_phb_root_bus_path;
@@ -1543,7 +1626,6 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data)
dc->vmsd = &vmstate_spapr_pci;
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
dc->cannot_instantiate_with_device_add_yet = false;
- spc->finish_realize = spapr_phb_finish_realize;
hp->plug = spapr_phb_hot_plug_child;
hp->unplug = spapr_phb_hot_unplug_child;
}
@@ -1553,7 +1635,6 @@ static const TypeInfo spapr_phb_info = {
.parent = TYPE_PCI_HOST_BRIDGE,
.instance_size = sizeof(sPAPRPHBState),
.class_init = spapr_phb_class_init,
- .class_size = sizeof(sPAPRPHBClass),
.interfaces = (InterfaceInfo[]) {
{ TYPE_HOTPLUG_HANDLER },
{ }
@@ -1735,6 +1816,9 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb,
sizeof(interrupt_map)));
tcet = spapr_tce_find_by_liobn(SPAPR_PCI_LIOBN(phb->index, 0));
+ if (!tcet) {
+ return -1;
+ }
spapr_dma_dt(fdt, bus_off, "ibm,dma-window",
tcet->liobn, tcet->bus_offset,
tcet->nb_table << tcet->page_shift);
@@ -1770,7 +1854,7 @@ void spapr_pci_rtas_init(void)
rtas_ibm_read_pci_config);
spapr_rtas_register(RTAS_IBM_WRITE_PCI_CONFIG, "ibm,write-pci-config",
rtas_ibm_write_pci_config);
- if (msi_supported) {
+ if (msi_nonbroken) {
spapr_rtas_register(RTAS_IBM_QUERY_INTERRUPT_SOURCE_NUMBER,
"ibm,query-interrupt-source-number",
rtas_ibm_query_interrupt_source_number);
diff --git a/qemu/hw/ppc/spapr_pci_vfio.c b/qemu/hw/ppc/spapr_pci_vfio.c
index cca45ed31..cbd3d23c9 100644
--- a/qemu/hw/ppc/spapr_pci_vfio.c
+++ b/qemu/hw/ppc/spapr_pci_vfio.c
@@ -17,73 +17,51 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/ppc/spapr.h"
#include "hw/pci-host/spapr.h"
#include "hw/pci/msix.h"
#include "linux/vfio.h"
#include "hw/vfio/vfio.h"
+#include "qemu/error-report.h"
-static Property spapr_phb_vfio_properties[] = {
- DEFINE_PROP_INT32("iommu", sPAPRPHBVFIOState, iommugroupid, -1),
- DEFINE_PROP_END_OF_LIST(),
-};
+#define TYPE_SPAPR_PCI_VFIO_HOST_BRIDGE "spapr-pci-vfio-host-bridge"
-static void spapr_phb_vfio_finish_realize(sPAPRPHBState *sphb, Error **errp)
-{
- sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb);
- struct vfio_iommu_spapr_tce_info info = { .argsz = sizeof(info) };
- int ret;
- sPAPRTCETable *tcet;
- uint32_t liobn = svphb->phb.dma_liobn;
+#define SPAPR_PCI_VFIO_HOST_BRIDGE(obj) \
+ OBJECT_CHECK(sPAPRPHBVFIOState, (obj), TYPE_SPAPR_PCI_VFIO_HOST_BRIDGE)
- if (svphb->iommugroupid == -1) {
- error_setg(errp, "Wrong IOMMU group ID %d", svphb->iommugroupid);
- return;
- }
+typedef struct sPAPRPHBVFIOState sPAPRPHBVFIOState;
- ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid,
- VFIO_CHECK_EXTENSION,
- (void *) VFIO_SPAPR_TCE_IOMMU);
- if (ret != 1) {
- error_setg_errno(errp, -ret,
- "spapr-vfio: SPAPR extension is not supported");
- return;
- }
+struct sPAPRPHBVFIOState {
+ sPAPRPHBState phb;
- ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid,
- VFIO_IOMMU_SPAPR_TCE_GET_INFO, &info);
- if (ret) {
- error_setg_errno(errp, -ret,
- "spapr-vfio: get info from container failed");
- return;
- }
+ int32_t iommugroupid;
+};
- tcet = spapr_tce_new_table(DEVICE(sphb), liobn, info.dma32_window_start,
- SPAPR_TCE_PAGE_SHIFT,
- info.dma32_window_size >> SPAPR_TCE_PAGE_SHIFT,
- true);
- if (!tcet) {
- error_setg(errp, "spapr-vfio: failed to create VFIO TCE table");
- return;
- }
+static Property spapr_phb_vfio_properties[] = {
+ DEFINE_PROP_INT32("iommu", sPAPRPHBVFIOState, iommugroupid, -1),
+ DEFINE_PROP_END_OF_LIST(),
+};
- /* Register default 32bit DMA window */
- memory_region_add_subregion(&sphb->iommu_root, tcet->bus_offset,
- spapr_tce_get_iommu(tcet));
+static void spapr_phb_vfio_instance_init(Object *obj)
+{
+ error_report("spapr-pci-vfio-host-bridge is deprecated");
}
-static void spapr_phb_vfio_eeh_reenable(sPAPRPHBVFIOState *svphb)
+bool spapr_phb_eeh_available(sPAPRPHBState *sphb)
{
- struct vfio_eeh_pe_op op = {
- .argsz = sizeof(op),
- .op = VFIO_EEH_PE_ENABLE
- };
+ return vfio_eeh_as_ok(&sphb->iommu_as);
+}
- vfio_container_ioctl(&svphb->phb.iommu_as,
- svphb->iommugroupid, VFIO_EEH_PE_OP, &op);
+static void spapr_phb_vfio_eeh_reenable(sPAPRPHBState *sphb)
+{
+ vfio_eeh_as_op(&sphb->iommu_as, VFIO_EEH_PE_ENABLE);
}
-static void spapr_phb_vfio_reset(DeviceState *qdev)
+void spapr_phb_vfio_reset(DeviceState *qdev)
{
/*
* The PE might be in frozen state. To reenable the EEH
@@ -91,19 +69,18 @@ static void spapr_phb_vfio_reset(DeviceState *qdev)
* ensures that the contained PCI devices will work properly
* after reboot.
*/
- spapr_phb_vfio_eeh_reenable(SPAPR_PCI_VFIO_HOST_BRIDGE(qdev));
+ spapr_phb_vfio_eeh_reenable(SPAPR_PCI_HOST_BRIDGE(qdev));
}
-static int spapr_phb_vfio_eeh_set_option(sPAPRPHBState *sphb,
- unsigned int addr, int option)
+int spapr_phb_vfio_eeh_set_option(sPAPRPHBState *sphb,
+ unsigned int addr, int option)
{
- sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb);
- struct vfio_eeh_pe_op op = { .argsz = sizeof(op) };
+ uint32_t op;
int ret;
switch (option) {
case RTAS_EEH_DISABLE:
- op.op = VFIO_EEH_PE_DISABLE;
+ op = VFIO_EEH_PE_DISABLE;
break;
case RTAS_EEH_ENABLE: {
PCIHostState *phb;
@@ -117,25 +94,24 @@ static int spapr_phb_vfio_eeh_set_option(sPAPRPHBState *sphb,
phb = PCI_HOST_BRIDGE(sphb);
pdev = pci_find_device(phb->bus,
(addr >> 16) & 0xFF, (addr >> 8) & 0xFF);
- if (!pdev) {
+ if (!pdev || !object_dynamic_cast(OBJECT(pdev), "vfio-pci")) {
return RTAS_OUT_PARAM_ERROR;
}
- op.op = VFIO_EEH_PE_ENABLE;
+ op = VFIO_EEH_PE_ENABLE;
break;
}
case RTAS_EEH_THAW_IO:
- op.op = VFIO_EEH_PE_UNFREEZE_IO;
+ op = VFIO_EEH_PE_UNFREEZE_IO;
break;
case RTAS_EEH_THAW_DMA:
- op.op = VFIO_EEH_PE_UNFREEZE_DMA;
+ op = VFIO_EEH_PE_UNFREEZE_DMA;
break;
default:
return RTAS_OUT_PARAM_ERROR;
}
- ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid,
- VFIO_EEH_PE_OP, &op);
+ ret = vfio_eeh_as_op(&sphb->iommu_as, op);
if (ret < 0) {
return RTAS_OUT_HW_ERROR;
}
@@ -143,15 +119,11 @@ static int spapr_phb_vfio_eeh_set_option(sPAPRPHBState *sphb,
return RTAS_OUT_SUCCESS;
}
-static int spapr_phb_vfio_eeh_get_state(sPAPRPHBState *sphb, int *state)
+int spapr_phb_vfio_eeh_get_state(sPAPRPHBState *sphb, int *state)
{
- sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb);
- struct vfio_eeh_pe_op op = { .argsz = sizeof(op) };
int ret;
- op.op = VFIO_EEH_PE_GET_STATE;
- ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid,
- VFIO_EEH_PE_OP, &op);
+ ret = vfio_eeh_as_op(&sphb->iommu_as, VFIO_EEH_PE_GET_STATE);
if (ret < 0) {
return RTAS_OUT_PARAM_ERROR;
}
@@ -203,30 +175,28 @@ static void spapr_phb_vfio_eeh_pre_reset(sPAPRPHBState *sphb)
pci_for_each_bus(phb->bus, spapr_phb_vfio_eeh_clear_bus_msix, NULL);
}
-static int spapr_phb_vfio_eeh_reset(sPAPRPHBState *sphb, int option)
+int spapr_phb_vfio_eeh_reset(sPAPRPHBState *sphb, int option)
{
- sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb);
- struct vfio_eeh_pe_op op = { .argsz = sizeof(op) };
+ uint32_t op;
int ret;
switch (option) {
case RTAS_SLOT_RESET_DEACTIVATE:
- op.op = VFIO_EEH_PE_RESET_DEACTIVATE;
+ op = VFIO_EEH_PE_RESET_DEACTIVATE;
break;
case RTAS_SLOT_RESET_HOT:
spapr_phb_vfio_eeh_pre_reset(sphb);
- op.op = VFIO_EEH_PE_RESET_HOT;
+ op = VFIO_EEH_PE_RESET_HOT;
break;
case RTAS_SLOT_RESET_FUNDAMENTAL:
spapr_phb_vfio_eeh_pre_reset(sphb);
- op.op = VFIO_EEH_PE_RESET_FUNDAMENTAL;
+ op = VFIO_EEH_PE_RESET_FUNDAMENTAL;
break;
default:
return RTAS_OUT_PARAM_ERROR;
}
- ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid,
- VFIO_EEH_PE_OP, &op);
+ ret = vfio_eeh_as_op(&sphb->iommu_as, op);
if (ret < 0) {
return RTAS_OUT_HW_ERROR;
}
@@ -234,15 +204,11 @@ static int spapr_phb_vfio_eeh_reset(sPAPRPHBState *sphb, int option)
return RTAS_OUT_SUCCESS;
}
-static int spapr_phb_vfio_eeh_configure(sPAPRPHBState *sphb)
+int spapr_phb_vfio_eeh_configure(sPAPRPHBState *sphb)
{
- sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb);
- struct vfio_eeh_pe_op op = { .argsz = sizeof(op) };
int ret;
- op.op = VFIO_EEH_PE_CONFIGURE;
- ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid,
- VFIO_EEH_PE_OP, &op);
+ ret = vfio_eeh_as_op(&sphb->iommu_as, VFIO_EEH_PE_CONFIGURE);
if (ret < 0) {
return RTAS_OUT_PARAM_ERROR;
}
@@ -253,23 +219,16 @@ static int spapr_phb_vfio_eeh_configure(sPAPRPHBState *sphb)
static void spapr_phb_vfio_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- sPAPRPHBClass *spc = SPAPR_PCI_HOST_BRIDGE_CLASS(klass);
dc->props = spapr_phb_vfio_properties;
- dc->reset = spapr_phb_vfio_reset;
- spc->finish_realize = spapr_phb_vfio_finish_realize;
- spc->eeh_set_option = spapr_phb_vfio_eeh_set_option;
- spc->eeh_get_state = spapr_phb_vfio_eeh_get_state;
- spc->eeh_reset = spapr_phb_vfio_eeh_reset;
- spc->eeh_configure = spapr_phb_vfio_eeh_configure;
}
static const TypeInfo spapr_phb_vfio_info = {
.name = TYPE_SPAPR_PCI_VFIO_HOST_BRIDGE,
.parent = TYPE_SPAPR_PCI_HOST_BRIDGE,
.instance_size = sizeof(sPAPRPHBVFIOState),
+ .instance_init = spapr_phb_vfio_instance_init,
.class_init = spapr_phb_vfio_class_init,
- .class_size = sizeof(sPAPRPHBClass),
};
static void spapr_pci_vfio_register_types(void)
diff --git a/qemu/hw/ppc/spapr_rng.c b/qemu/hw/ppc/spapr_rng.c
new file mode 100644
index 000000000..80515eb54
--- /dev/null
+++ b/qemu/hw/ppc/spapr_rng.c
@@ -0,0 +1,191 @@
+/*
+ * QEMU sPAPR random number generator "device" for H_RANDOM hypercall
+ *
+ * Copyright 2015 Thomas Huth, 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "qemu/error-report.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/device_tree.h"
+#include "sysemu/rng.h"
+#include "hw/ppc/spapr.h"
+#include "kvm_ppc.h"
+
+#define SPAPR_RNG(obj) \
+ OBJECT_CHECK(sPAPRRngState, (obj), TYPE_SPAPR_RNG)
+
+struct sPAPRRngState {
+ /*< private >*/
+ DeviceState ds;
+ RngBackend *backend;
+ bool use_kvm;
+};
+typedef struct sPAPRRngState sPAPRRngState;
+
+struct HRandomData {
+ QemuSemaphore sem;
+ union {
+ uint64_t v64;
+ uint8_t v8[8];
+ } val;
+ int received;
+};
+typedef struct HRandomData HRandomData;
+
+/* Callback function for the RngBackend */
+static void random_recv(void *dest, const void *src, size_t size)
+{
+ HRandomData *hrdp = dest;
+
+ if (src && size > 0) {
+ assert(size + hrdp->received <= sizeof(hrdp->val.v8));
+ memcpy(&hrdp->val.v8[hrdp->received], src, size);
+ hrdp->received += size;
+ }
+
+ qemu_sem_post(&hrdp->sem);
+}
+
+/* Handler for the H_RANDOM hypercall */
+static target_ulong h_random(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ sPAPRRngState *rngstate;
+ HRandomData hrdata;
+
+ rngstate = SPAPR_RNG(object_resolve_path_type("", TYPE_SPAPR_RNG, NULL));
+
+ if (!rngstate || !rngstate->backend) {
+ return H_HARDWARE;
+ }
+
+ qemu_sem_init(&hrdata.sem, 0);
+ hrdata.val.v64 = 0;
+ hrdata.received = 0;
+
+ while (hrdata.received < 8) {
+ rng_backend_request_entropy(rngstate->backend, 8 - hrdata.received,
+ random_recv, &hrdata);
+ qemu_mutex_unlock_iothread();
+ qemu_sem_wait(&hrdata.sem);
+ qemu_mutex_lock_iothread();
+ }
+
+ qemu_sem_destroy(&hrdata.sem);
+ args[0] = hrdata.val.v64;
+
+ return H_SUCCESS;
+}
+
+static void spapr_rng_instance_init(Object *obj)
+{
+ sPAPRRngState *rngstate = SPAPR_RNG(obj);
+
+ if (object_resolve_path_type("", TYPE_SPAPR_RNG, NULL) != NULL) {
+ error_report("spapr-rng can not be instantiated twice!");
+ return;
+ }
+
+ object_property_add_link(obj, "rng", TYPE_RNG_BACKEND,
+ (Object **)&rngstate->backend,
+ object_property_allow_set_link,
+ OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL);
+ object_property_set_description(obj, "rng",
+ "ID of the random number generator backend",
+ NULL);
+}
+
+static void spapr_rng_realize(DeviceState *dev, Error **errp)
+{
+
+ sPAPRRngState *rngstate = SPAPR_RNG(dev);
+
+ if (rngstate->use_kvm) {
+ if (kvmppc_enable_hwrng() == 0) {
+ return;
+ }
+ /*
+ * If user specified both, use-kvm and a backend, we fall back to
+ * the backend now. If not, provide an appropriate error message.
+ */
+ if (!rngstate->backend) {
+ error_setg(errp, "Could not initialize in-kernel H_RANDOM call!");
+ return;
+ }
+ }
+
+ if (rngstate->backend) {
+ spapr_register_hypercall(H_RANDOM, h_random);
+ } else {
+ error_setg(errp, "spapr-rng needs an RNG backend!");
+ }
+}
+
+int spapr_rng_populate_dt(void *fdt)
+{
+ int node;
+ int ret;
+
+ node = qemu_fdt_add_subnode(fdt, "/ibm,platform-facilities");
+ if (node <= 0) {
+ return -1;
+ }
+ ret = fdt_setprop_string(fdt, node, "device_type",
+ "ibm,platform-facilities");
+ ret |= fdt_setprop_cell(fdt, node, "#address-cells", 0x1);
+ ret |= fdt_setprop_cell(fdt, node, "#size-cells", 0x0);
+
+ node = fdt_add_subnode(fdt, node, "ibm,random-v1");
+ if (node <= 0) {
+ return -1;
+ }
+ ret |= fdt_setprop_string(fdt, node, "compatible", "ibm,random");
+
+ return ret ? -1 : 0;
+}
+
+static Property spapr_rng_properties[] = {
+ DEFINE_PROP_BOOL("use-kvm", sPAPRRngState, use_kvm, false),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void spapr_rng_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = spapr_rng_realize;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ dc->props = spapr_rng_properties;
+ dc->hotpluggable = false;
+}
+
+static const TypeInfo spapr_rng_info = {
+ .name = TYPE_SPAPR_RNG,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(sPAPRRngState),
+ .instance_init = spapr_rng_instance_init,
+ .class_init = spapr_rng_class_init,
+};
+
+static void spapr_rng_register_type(void)
+{
+ type_register_static(&spapr_rng_info);
+}
+type_init(spapr_rng_register_type)
diff --git a/qemu/hw/ppc/spapr_rtas.c b/qemu/hw/ppc/spapr_rtas.c
index 2986f94f0..f07325831 100644
--- a/qemu/hw/ppc/spapr_rtas.c
+++ b/qemu/hw/ppc/spapr_rtas.c
@@ -24,6 +24,7 @@
* THE SOFTWARE.
*
*/
+#include "qemu/osdep.h"
#include "cpu.h"
#include "sysemu/sysemu.h"
#include "sysemu/char.h"
@@ -34,9 +35,11 @@
#include "hw/ppc/spapr.h"
#include "hw/ppc/spapr_vio.h"
#include "qapi-event.h"
+#include "hw/boards.h"
#include <libfdt.h>
#include "hw/ppc/spapr_drc.h"
+#include "qemu/cutils.h"
/* #define DEBUG_SPAPR */
@@ -111,6 +114,7 @@ static void rtas_power_off(PowerPCCPU *cpu, sPAPRMachineState *spapr,
return;
}
qemu_system_shutdown_request();
+ cpu_stop_current();
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
}
@@ -214,7 +218,7 @@ static void rtas_stop_self(PowerPCCPU *cpu, sPAPRMachineState *spapr,
CPUPPCState *env = &cpu->env;
cs->halted = 1;
- cpu_exit(cs);
+ qemu_cpu_kick(cs);
/*
* While stopping a CPU, the guest calls H_CPPR which
* effectively disables interrupts on XICS level.
@@ -227,6 +231,19 @@ static void rtas_stop_self(PowerPCCPU *cpu, sPAPRMachineState *spapr,
env->msr = 0;
}
+static inline int sysparm_st(target_ulong addr, target_ulong len,
+ const void *val, uint16_t vallen)
+{
+ hwaddr phys = ppc64_phys_to_real(addr);
+
+ if (len < 2) {
+ return RTAS_OUT_SYSPARM_PARAM_ERROR;
+ }
+ stw_be_phys(&address_space_memory, phys, vallen);
+ cpu_physical_memory_write(phys + 2, val, MIN(len - 2, vallen));
+ return RTAS_OUT_SUCCESS;
+}
+
static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu,
sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
@@ -236,24 +253,30 @@ static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu,
target_ulong parameter = rtas_ld(args, 0);
target_ulong buffer = rtas_ld(args, 1);
target_ulong length = rtas_ld(args, 2);
- target_ulong ret = RTAS_OUT_SUCCESS;
+ target_ulong ret;
switch (parameter) {
case RTAS_SYSPARM_SPLPAR_CHARACTERISTICS: {
- char *param_val = g_strdup_printf("MaxEntCap=%d,MaxPlatProcs=%d",
- max_cpus, smp_cpus);
- rtas_st_buffer(buffer, length, (uint8_t *)param_val, strlen(param_val));
+ char *param_val = g_strdup_printf("MaxEntCap=%d,"
+ "DesMem=%llu,"
+ "DesProcs=%d,"
+ "MaxPlatProcs=%d",
+ max_cpus,
+ current_machine->ram_size / M_BYTE,
+ smp_cpus,
+ max_cpus);
+ ret = sysparm_st(buffer, length, param_val, strlen(param_val) + 1);
g_free(param_val);
break;
}
case RTAS_SYSPARM_DIAGNOSTICS_RUN_MODE: {
uint8_t param_val = DIAGNOSTICS_RUN_MODE_DISABLED;
- rtas_st_buffer(buffer, length, &param_val, sizeof(param_val));
+ ret = sysparm_st(buffer, length, &param_val, sizeof(param_val));
break;
}
case RTAS_SYSPARM_UUID:
- rtas_st_buffer(buffer, length, qemu_uuid, (qemu_uuid_set ? 16 : 0));
+ ret = sysparm_st(buffer, length, qemu_uuid, (qemu_uuid_set ? 16 : 0));
break;
default:
ret = RTAS_OUT_NOT_SUPPORTED;
@@ -365,12 +388,13 @@ static void rtas_set_indicator(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t sensor_type;
uint32_t sensor_index;
uint32_t sensor_state;
+ uint32_t ret = RTAS_OUT_SUCCESS;
sPAPRDRConnector *drc;
sPAPRDRConnectorClass *drck;
if (nargs != 3 || nret != 1) {
- rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
- return;
+ ret = RTAS_OUT_PARAM_ERROR;
+ goto out;
}
sensor_type = rtas_ld(args, 0);
@@ -386,8 +410,8 @@ static void rtas_set_indicator(PowerPCCPU *cpu, sPAPRMachineState *spapr,
if (!drc) {
DPRINTF("rtas_set_indicator: invalid sensor/DRC index: %xh\n",
sensor_index);
- rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
- return;
+ ret = RTAS_OUT_PARAM_ERROR;
+ goto out;
}
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
@@ -406,19 +430,20 @@ static void rtas_set_indicator(PowerPCCPU *cpu, sPAPRMachineState *spapr,
spapr_ccs_remove(spapr, ccs);
}
}
- drck->set_isolation_state(drc, sensor_state);
+ ret = drck->set_isolation_state(drc, sensor_state);
break;
case RTAS_SENSOR_TYPE_DR:
- drck->set_indicator_state(drc, sensor_state);
+ ret = drck->set_indicator_state(drc, sensor_state);
break;
case RTAS_SENSOR_TYPE_ALLOCATION_STATE:
- drck->set_allocation_state(drc, sensor_state);
+ ret = drck->set_allocation_state(drc, sensor_state);
break;
default:
goto out_unimplemented;
}
- rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+out:
+ rtas_st(rets, 0, ret);
return;
out_unimplemented:
@@ -435,13 +460,14 @@ static void rtas_get_sensor_state(PowerPCCPU *cpu, sPAPRMachineState *spapr,
{
uint32_t sensor_type;
uint32_t sensor_index;
+ uint32_t sensor_state = 0;
sPAPRDRConnector *drc;
sPAPRDRConnectorClass *drck;
- uint32_t entity_sense;
+ uint32_t ret = RTAS_OUT_SUCCESS;
if (nargs != 2 || nret != 2) {
- rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
- return;
+ ret = RTAS_OUT_PARAM_ERROR;
+ goto out;
}
sensor_type = rtas_ld(args, 0);
@@ -451,22 +477,23 @@ static void rtas_get_sensor_state(PowerPCCPU *cpu, sPAPRMachineState *spapr,
/* currently only DR-related sensors are implemented */
DPRINTF("rtas_get_sensor_state: sensor/indicator not implemented: %d\n",
sensor_type);
- rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED);
- return;
+ ret = RTAS_OUT_NOT_SUPPORTED;
+ goto out;
}
drc = spapr_dr_connector_by_index(sensor_index);
if (!drc) {
DPRINTF("rtas_get_sensor_state: invalid sensor/DRC index: %xh\n",
sensor_index);
- rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
- return;
+ ret = RTAS_OUT_PARAM_ERROR;
+ goto out;
}
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
- entity_sense = drck->entity_sense(drc);
+ ret = drck->entity_sense(drc, &sensor_state);
- rtas_st(rets, 0, RTAS_OUT_SUCCESS);
- rtas_st(rets, 1, entity_sense);
+out:
+ rtas_st(rets, 0, ret);
+ rtas_st(rets, 1, sensor_state);
}
/* configure-connector work area offsets, int32_t units for field
@@ -481,6 +508,13 @@ static void rtas_get_sensor_state(PowerPCCPU *cpu, sPAPRMachineState *spapr,
#define CC_VAL_DATA_OFFSET ((CC_IDX_PROP_DATA_OFFSET + 1) * 4)
#define CC_WA_LEN 4096
+static void configure_connector_st(target_ulong addr, target_ulong offset,
+ const void *buf, size_t len)
+{
+ cpu_physical_memory_write(ppc64_phys_to_real(addr + offset),
+ buf, MIN(len, CC_WA_LEN - offset));
+}
+
static void rtas_ibm_configure_connector(PowerPCCPU *cpu,
sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
@@ -515,6 +549,12 @@ static void rtas_ibm_configure_connector(PowerPCCPU *cpu,
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
fdt = drck->get_fdt(drc, NULL);
+ if (!fdt) {
+ DPRINTF("rtas_ibm_configure_connector: Missing FDT for DRC index: %xh\n",
+ drc_index);
+ rc = SPAPR_DR_CC_RESPONSE_NOT_CONFIGURABLE;
+ goto out;
+ }
ccs = spapr_ccs_find(spapr, drc_index);
if (!ccs) {
@@ -540,8 +580,7 @@ static void rtas_ibm_configure_connector(PowerPCCPU *cpu,
/* provide the name of the next OF node */
wa_offset = CC_VAL_DATA_OFFSET;
rtas_st(wa_addr, CC_IDX_NODE_NAME_OFFSET, wa_offset);
- rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset,
- (uint8_t *)name, strlen(name) + 1);
+ configure_connector_st(wa_addr, wa_offset, name, strlen(name) + 1);
resp = SPAPR_DR_CC_RESPONSE_NEXT_CHILD;
break;
case FDT_END_NODE:
@@ -566,8 +605,7 @@ static void rtas_ibm_configure_connector(PowerPCCPU *cpu,
/* provide the name of the next OF property */
wa_offset = CC_VAL_DATA_OFFSET;
rtas_st(wa_addr, CC_IDX_PROP_NAME_OFFSET, wa_offset);
- rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset,
- (uint8_t *)name, strlen(name) + 1);
+ configure_connector_st(wa_addr, wa_offset, name, strlen(name) + 1);
/* provide the length and value of the OF property. data gets
* placed immediately after NULL terminator of the OF property's
@@ -576,9 +614,7 @@ static void rtas_ibm_configure_connector(PowerPCCPU *cpu,
wa_offset += strlen(name) + 1,
rtas_st(wa_addr, CC_IDX_PROP_LEN, prop_len);
rtas_st(wa_addr, CC_IDX_PROP_DATA_OFFSET, wa_offset);
- rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset,
- (uint8_t *)((struct fdt_property *)prop)->data,
- prop_len);
+ configure_connector_st(wa_addr, wa_offset, prop->data, prop_len);
resp = SPAPR_DR_CC_RESPONSE_NEXT_PROPERTY;
break;
case FDT_END:
@@ -631,17 +667,11 @@ target_ulong spapr_rtas_call(PowerPCCPU *cpu, sPAPRMachineState *spapr,
void spapr_rtas_register(int token, const char *name, spapr_rtas_fn fn)
{
- if (!((token >= RTAS_TOKEN_BASE) && (token < RTAS_TOKEN_MAX))) {
- fprintf(stderr, "RTAS invalid token 0x%x\n", token);
- exit(1);
- }
+ assert((token >= RTAS_TOKEN_BASE) && (token < RTAS_TOKEN_MAX));
token -= RTAS_TOKEN_BASE;
- if (rtas_table[token].name) {
- fprintf(stderr, "RTAS call \"%s\" is registered already as 0x%x\n",
- rtas_table[token].name, token);
- exit(1);
- }
+
+ assert(!rtas_table[token].name);
rtas_table[token].name = name;
rtas_table[token].fn = fn;
@@ -654,6 +684,9 @@ int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr,
int i;
uint32_t lrdr_capacity[5];
MachineState *machine = MACHINE(qdev_get_machine());
+ sPAPRMachineState *spapr = SPAPR_MACHINE(machine);
+ uint64_t max_hotplug_addr = spapr->hotplug_memory.base +
+ memory_region_size(&spapr->hotplug_memory.mr);
ret = fdt_add_mem_rsv(fdt, rtas_addr, rtas_size);
if (ret < 0) {
@@ -703,8 +736,8 @@ int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr,
}
- lrdr_capacity[0] = cpu_to_be32(((uint64_t)machine->maxram_size) >> 32);
- lrdr_capacity[1] = cpu_to_be32(machine->maxram_size & 0xffffffff);
+ lrdr_capacity[0] = cpu_to_be32(max_hotplug_addr >> 32);
+ lrdr_capacity[1] = cpu_to_be32(max_hotplug_addr & 0xffffffff);
lrdr_capacity[2] = 0;
lrdr_capacity[3] = cpu_to_be32(SPAPR_MEMORY_BLOCK_SIZE);
lrdr_capacity[4] = cpu_to_be32(max_cpus/smp_threads);
diff --git a/qemu/hw/ppc/spapr_rtc.c b/qemu/hw/ppc/spapr_rtc.c
index 34b27db70..3a17ac42e 100644
--- a/qemu/hw/ppc/spapr_rtc.c
+++ b/qemu/hw/ppc/spapr_rtc.c
@@ -25,11 +25,13 @@
* THE SOFTWARE.
*
*/
+#include "qemu/osdep.h"
#include "cpu.h"
#include "qemu/timer.h"
#include "sysemu/sysemu.h"
#include "hw/ppc/spapr.h"
#include "qapi-event.h"
+#include "qemu/cutils.h"
#define SPAPR_RTC(obj) \
OBJECT_CHECK(sPAPRRTCState, (obj), TYPE_SPAPR_RTC)
@@ -200,7 +202,6 @@ static const TypeInfo spapr_rtc_info = {
.name = TYPE_SPAPR_RTC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(sPAPRRTCState),
- .class_size = sizeof(XICSStateClass),
.class_init = spapr_rtc_class_init,
};
diff --git a/qemu/hw/ppc/spapr_vio.c b/qemu/hw/ppc/spapr_vio.c
index c51eb8e24..8aa021fde 100644
--- a/qemu/hw/ppc/spapr_vio.c
+++ b/qemu/hw/ppc/spapr_vio.c
@@ -19,6 +19,8 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "sysemu/sysemu.h"
#include "hw/boards.h"
@@ -388,7 +390,7 @@ static void rtas_quiesce(PowerPCCPU *cpu, sPAPRMachineState *spapr,
static VIOsPAPRDevice *reg_conflict(VIOsPAPRDevice *dev)
{
- VIOsPAPRBus *bus = DO_UPCAST(VIOsPAPRBus, bus, dev->qdev.parent_bus);
+ VIOsPAPRBus *bus = SPAPR_VIO_BUS(dev->qdev.parent_bus);
BusChild *kid;
VIOsPAPRDevice *other;
@@ -430,6 +432,7 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp)
VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev;
VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
char *id;
+ Error *local_err = NULL;
if (dev->reg != -1) {
/*
@@ -449,7 +452,7 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp)
}
} else {
/* Need to assign an address */
- VIOsPAPRBus *bus = DO_UPCAST(VIOsPAPRBus, bus, dev->qdev.parent_bus);
+ VIOsPAPRBus *bus = SPAPR_VIO_BUS(dev->qdev.parent_bus);
do {
dev->reg = bus->next_reg++;
@@ -462,9 +465,9 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp)
dev->qdev.id = id;
}
- dev->irq = xics_alloc(spapr->icp, 0, dev->irq, false);
- if (!dev->irq) {
- error_setg(errp, "can't allocate IRQ");
+ dev->irq = xics_alloc(spapr->icp, 0, dev->irq, false, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
return;
}
@@ -523,13 +526,12 @@ VIOsPAPRBus *spapr_vio_bus_init(void)
DeviceState *dev;
/* Create bridge device */
- dev = qdev_create(NULL, "spapr-vio-bridge");
+ dev = qdev_create(NULL, TYPE_SPAPR_VIO_BRIDGE);
qdev_init_nofail(dev);
/* Create bus on bridge device */
-
qbus = qbus_create(TYPE_SPAPR_VIO_BUS, dev, "spapr-vio");
- bus = DO_UPCAST(VIOsPAPRBus, bus, qbus);
+ bus = SPAPR_VIO_BUS(qbus);
bus->next_reg = 0x71000000;
/* hcall-vio */
@@ -567,9 +569,8 @@ static void spapr_vio_bridge_class_init(ObjectClass *klass, void *data)
}
static const TypeInfo spapr_vio_bridge_info = {
- .name = "spapr-vio-bridge",
+ .name = TYPE_SPAPR_VIO_BRIDGE,
.parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(SysBusDevice),
.class_init = spapr_vio_bridge_class_init,
};
diff --git a/qemu/hw/ppc/virtex_ml507.c b/qemu/hw/ppc/virtex_ml507.c
index de86f7c64..b807a08c2 100644
--- a/qemu/hw/ppc/virtex_ml507.c
+++ b/qemu/hw/ppc/virtex_ml507.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/hw.h"
#include "hw/char/serial.h"
@@ -257,7 +258,8 @@ static void virtex_init(MachineState *machine)
/* Boots a kernel elf binary. */
kernel_size = load_elf(kernel_filename, NULL, NULL,
- &entry, &low, &high, 1, ELF_MACHINE, 0);
+ &entry, &low, &high, 1, PPC_ELF_MACHINE,
+ 0, 0);
boot_info.bootstrap_pc = entry & 0x00ffffff;
if (kernel_size < 0) {
@@ -297,15 +299,10 @@ static void virtex_init(MachineState *machine)
env->load_info = &boot_info;
}
-static QEMUMachine virtex_machine = {
- .name = "virtex-ml507",
- .desc = "Xilinx Virtex ML507 reference design",
- .init = virtex_init,
-};
-
-static void virtex_machine_init(void)
+static void virtex_machine_init(MachineClass *mc)
{
- qemu_register_machine(&virtex_machine);
+ mc->desc = "Xilinx Virtex ML507 reference design";
+ mc->init = virtex_init;
}
-machine_init(virtex_machine_init);
+DEFINE_MACHINE("virtex-ml507", virtex_machine_init)
diff --git a/qemu/hw/s390x/Makefile.objs b/qemu/hw/s390x/Makefile.objs
index 27cd75a93..220361782 100644
--- a/qemu/hw/s390x/Makefile.objs
+++ b/qemu/hw/s390x/Makefile.objs
@@ -1,4 +1,4 @@
-obj-y = s390-virtio-bus.o s390-virtio.o
+obj-y += s390-virtio.o
obj-y += s390-virtio-hcall.o
obj-y += sclp.o
obj-y += event-facility.o
@@ -9,3 +9,5 @@ obj-y += css.o
obj-y += s390-virtio-ccw.o
obj-y += virtio-ccw.o
obj-y += s390-pci-bus.o s390-pci-inst.o
+obj-y += s390-skeys.o
+obj-$(CONFIG_KVM) += s390-skeys-kvm.o
diff --git a/qemu/hw/s390x/css.c b/qemu/hw/s390x/css.c
index 5df450e00..3a1d91958 100644
--- a/qemu/hw/s390x/css.c
+++ b/qemu/hw/s390x/css.c
@@ -9,6 +9,7 @@
* directory.
*/
+#include "qemu/osdep.h"
#include <hw/qdev.h>
#include "qemu/bitops.h"
#include "exec/address-spaces.h"
@@ -49,6 +50,7 @@ typedef struct IoAdapter {
typedef struct ChannelSubSys {
QTAILQ_HEAD(, CrwContainer) pending_crws;
+ bool sei_pending;
bool do_crw_mchk;
bool crws_lost;
uint8_t max_cssid;
@@ -58,9 +60,81 @@ typedef struct ChannelSubSys {
CssImage *css[MAX_CSSID + 1];
uint8_t default_cssid;
QTAILQ_HEAD(, IoAdapter) io_adapters;
+ QTAILQ_HEAD(, IndAddr) indicator_addresses;
} ChannelSubSys;
-static ChannelSubSys *channel_subsys;
+static ChannelSubSys channel_subsys = {
+ .pending_crws = QTAILQ_HEAD_INITIALIZER(channel_subsys.pending_crws),
+ .do_crw_mchk = true,
+ .sei_pending = false,
+ .do_crw_mchk = true,
+ .crws_lost = false,
+ .chnmon_active = false,
+ .io_adapters = QTAILQ_HEAD_INITIALIZER(channel_subsys.io_adapters),
+ .indicator_addresses =
+ QTAILQ_HEAD_INITIALIZER(channel_subsys.indicator_addresses),
+};
+
+IndAddr *get_indicator(hwaddr ind_addr, int len)
+{
+ IndAddr *indicator;
+
+ QTAILQ_FOREACH(indicator, &channel_subsys.indicator_addresses, sibling) {
+ if (indicator->addr == ind_addr) {
+ indicator->refcnt++;
+ return indicator;
+ }
+ }
+ indicator = g_new0(IndAddr, 1);
+ indicator->addr = ind_addr;
+ indicator->len = len;
+ indicator->refcnt = 1;
+ QTAILQ_INSERT_TAIL(&channel_subsys.indicator_addresses,
+ indicator, sibling);
+ return indicator;
+}
+
+static int s390_io_adapter_map(AdapterInfo *adapter, uint64_t map_addr,
+ bool do_map)
+{
+ S390FLICState *fs = s390_get_flic();
+ S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
+
+ return fsc->io_adapter_map(fs, adapter->adapter_id, map_addr, do_map);
+}
+
+void release_indicator(AdapterInfo *adapter, IndAddr *indicator)
+{
+ assert(indicator->refcnt > 0);
+ indicator->refcnt--;
+ if (indicator->refcnt > 0) {
+ return;
+ }
+ QTAILQ_REMOVE(&channel_subsys.indicator_addresses, indicator, sibling);
+ if (indicator->map) {
+ s390_io_adapter_map(adapter, indicator->map, false);
+ }
+ g_free(indicator);
+}
+
+int map_indicator(AdapterInfo *adapter, IndAddr *indicator)
+{
+ int ret;
+
+ if (indicator->map) {
+ return 0; /* already mapped is not an error */
+ }
+ indicator->map = indicator->addr;
+ ret = s390_io_adapter_map(adapter, indicator->map, true);
+ if ((ret != 0) && (ret != -ENOSYS)) {
+ goto out_err;
+ }
+ return 0;
+
+out_err:
+ indicator->map = 0;
+ return ret;
+}
int css_create_css_image(uint8_t cssid, bool default_image)
{
@@ -68,12 +142,12 @@ int css_create_css_image(uint8_t cssid, bool default_image)
if (cssid > MAX_CSSID) {
return -EINVAL;
}
- if (channel_subsys->css[cssid]) {
+ if (channel_subsys.css[cssid]) {
return -EBUSY;
}
- channel_subsys->css[cssid] = g_malloc0(sizeof(CssImage));
+ channel_subsys.css[cssid] = g_malloc0(sizeof(CssImage));
if (default_image) {
- channel_subsys->default_cssid = cssid;
+ channel_subsys.default_cssid = cssid;
}
return 0;
}
@@ -88,7 +162,7 @@ int css_register_io_adapter(uint8_t type, uint8_t isc, bool swap,
S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
*id = 0;
- QTAILQ_FOREACH(adapter, &channel_subsys->io_adapters, sibling) {
+ QTAILQ_FOREACH(adapter, &channel_subsys.io_adapters, sibling) {
if ((adapter->type == type) && (adapter->isc == isc)) {
*id = adapter->id;
found = true;
@@ -108,7 +182,7 @@ int css_register_io_adapter(uint8_t type, uint8_t isc, bool swap,
adapter->id = *id;
adapter->isc = isc;
adapter->type = type;
- QTAILQ_INSERT_TAIL(&channel_subsys->io_adapters, adapter, sibling);
+ QTAILQ_INSERT_TAIL(&channel_subsys.io_adapters, adapter, sibling);
} else {
g_free(adapter);
fprintf(stderr, "Unexpected error %d when registering adapter %d\n",
@@ -120,7 +194,7 @@ out:
uint16_t css_build_subchannel_id(SubchDev *sch)
{
- if (channel_subsys->max_cssid > 0) {
+ if (channel_subsys.max_cssid > 0) {
return (sch->cssid << 8) | (1 << 3) | (sch->ssid << 1) | 1;
}
return (sch->ssid << 1) | 1;
@@ -261,11 +335,15 @@ static CCW1 copy_ccw_from_guest(hwaddr addr, bool fmt1)
ret.flags = tmp0.flags;
ret.count = be16_to_cpu(tmp0.count);
ret.cda = be16_to_cpu(tmp0.cda1) | (tmp0.cda0 << 16);
+ if ((ret.cmd_code & 0x0f) == CCW_CMD_TIC) {
+ ret.cmd_code &= 0x0f;
+ }
}
return ret;
}
-static int css_interpret_ccw(SubchDev *sch, hwaddr ccw_addr)
+static int css_interpret_ccw(SubchDev *sch, hwaddr ccw_addr,
+ bool suspend_allowed)
{
int ret;
bool check_len;
@@ -287,9 +365,13 @@ static int css_interpret_ccw(SubchDev *sch, hwaddr ccw_addr)
((ccw.cmd_code & 0xf0) != 0)) {
return -EINVAL;
}
+ if (!sch->ccw_fmt_1 && (ccw.count == 0) &&
+ (ccw.cmd_code != CCW_CMD_TIC)) {
+ return -EINVAL;
+ }
if (ccw.flags & CCW_FLAG_SUSPEND) {
- return -EINPROGRESS;
+ return suspend_allowed ? -EINPROGRESS : -EINVAL;
}
check_len = !((ccw.flags & CCW_FLAG_SLI) && !(ccw.flags & CCW_FLAG_DC));
@@ -387,11 +469,14 @@ static void sch_handle_start_func(SubchDev *sch, ORB *orb)
SCSW *s = &sch->curr_status.scsw;
int path;
int ret;
+ bool suspend_allowed;
/* Path management: In our simple css, we always choose the only path. */
path = 0x80;
if (!(s->ctrl & SCSW_ACTL_SUSP)) {
+ s->cstat = 0;
+ s->dstat = 0;
/* Look at the orb and try to execute the channel program. */
assert(orb != NULL); /* resume does not pass an orb */
p->intparm = orb->intparm;
@@ -404,12 +489,15 @@ static void sch_handle_start_func(SubchDev *sch, ORB *orb)
}
sch->ccw_fmt_1 = !!(orb->ctrl0 & ORB_CTRL0_MASK_FMT);
sch->ccw_no_data_cnt = 0;
+ suspend_allowed = !!(orb->ctrl0 & ORB_CTRL0_MASK_SPND);
} else {
s->ctrl &= ~(SCSW_ACTL_SUSP | SCSW_ACTL_RESUME_PEND);
+ /* The channel program had been suspended before. */
+ suspend_allowed = true;
}
sch->last_cmd_valid = false;
do {
- ret = css_interpret_ccw(sch, sch->channel_prog);
+ ret = css_interpret_ccw(sch, sch->channel_prog, suspend_allowed);
switch (ret) {
case -EAGAIN:
/* ccw chain, continue processing */
@@ -692,7 +780,7 @@ int css_do_csch(SubchDev *sch)
/* Trigger the clear function. */
s->ctrl &= ~(SCSW_CTRL_MASK_FCTL | SCSW_CTRL_MASK_ACTL);
- s->ctrl |= SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_CLEAR_FUNC;
+ s->ctrl |= SCSW_FCTL_CLEAR_FUNC | SCSW_ACTL_CLEAR_PEND;
do_subchannel_work(sch, NULL);
ret = 0;
@@ -767,12 +855,12 @@ static void css_update_chnmon(SubchDev *sch)
offset = sch->curr_status.pmcw.mbi << 5;
count = address_space_lduw(&address_space_memory,
- channel_subsys->chnmon_area + offset,
+ channel_subsys.chnmon_area + offset,
MEMTXATTRS_UNSPECIFIED,
NULL);
count++;
address_space_stw(&address_space_memory,
- channel_subsys->chnmon_area + offset, count,
+ channel_subsys.chnmon_area + offset, count,
MEMTXATTRS_UNSPECIFIED, NULL);
}
}
@@ -801,7 +889,7 @@ int css_do_ssch(SubchDev *sch, ORB *orb)
}
/* If monitoring is active, update counter. */
- if (channel_subsys->chnmon_active) {
+ if (channel_subsys.chnmon_active) {
css_update_chnmon(sch);
}
sch->channel_prog = orb->cpa;
@@ -883,8 +971,14 @@ int css_do_tsch_get_irb(SubchDev *sch, IRB *target_irb, int *irb_len)
/* If a unit check is pending, copy sense data. */
if ((s->dstat & SCSW_DSTAT_UNIT_CHECK) &&
(p->chars & PMCW_CHARS_MASK_CSENSE)) {
+ int i;
+
irb.scsw.flags |= SCSW_FLAGS_MASK_ESWF | SCSW_FLAGS_MASK_ECTL;
+ /* Attention: sense_data is already BE! */
memcpy(irb.ecw, sch->sense_data, sizeof(sch->sense_data));
+ for (i = 0; i < ARRAY_SIZE(irb.ecw); i++) {
+ irb.ecw[i] = be32_to_cpu(irb.ecw[i]);
+ }
irb.esw[1] = 0x01000000 | (sizeof(sch->sense_data) << 8);
}
}
@@ -954,16 +1048,16 @@ int css_do_stcrw(CRW *crw)
CrwContainer *crw_cont;
int ret;
- crw_cont = QTAILQ_FIRST(&channel_subsys->pending_crws);
+ crw_cont = QTAILQ_FIRST(&channel_subsys.pending_crws);
if (crw_cont) {
- QTAILQ_REMOVE(&channel_subsys->pending_crws, crw_cont, sibling);
+ QTAILQ_REMOVE(&channel_subsys.pending_crws, crw_cont, sibling);
copy_crw_to_guest(crw, &crw_cont->crw);
g_free(crw_cont);
ret = 0;
} else {
/* List was empty, turn crw machine checks on again. */
memset(crw, 0, sizeof(*crw));
- channel_subsys->do_crw_mchk = true;
+ channel_subsys.do_crw_mchk = true;
ret = 1;
}
@@ -982,12 +1076,12 @@ void css_undo_stcrw(CRW *crw)
crw_cont = g_try_malloc0(sizeof(CrwContainer));
if (!crw_cont) {
- channel_subsys->crws_lost = true;
+ channel_subsys.crws_lost = true;
return;
}
copy_crw_from_guest(&crw_cont->crw, crw);
- QTAILQ_INSERT_HEAD(&channel_subsys->pending_crws, crw_cont, sibling);
+ QTAILQ_INSERT_HEAD(&channel_subsys.pending_crws, crw_cont, sibling);
}
int css_do_tpi(IOIntCode *int_code, int lowcore)
@@ -1005,9 +1099,9 @@ int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid, uint8_t l_chpid,
CssImage *css;
if (!m && !cssid) {
- css = channel_subsys->css[channel_subsys->default_cssid];
+ css = channel_subsys.css[channel_subsys.default_cssid];
} else {
- css = channel_subsys->css[cssid];
+ css = channel_subsys.css[cssid];
}
if (!css) {
return 0;
@@ -1042,15 +1136,15 @@ void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo)
{
/* dct is currently ignored (not really meaningful for our devices) */
/* TODO: Don't ignore mbk. */
- if (update && !channel_subsys->chnmon_active) {
+ if (update && !channel_subsys.chnmon_active) {
/* Enable measuring. */
- channel_subsys->chnmon_area = mbo;
- channel_subsys->chnmon_active = true;
+ channel_subsys.chnmon_area = mbo;
+ channel_subsys.chnmon_active = true;
}
- if (!update && channel_subsys->chnmon_active) {
+ if (!update && channel_subsys.chnmon_active) {
/* Disable measuring. */
- channel_subsys->chnmon_area = 0;
- channel_subsys->chnmon_active = false;
+ channel_subsys.chnmon_area = 0;
+ channel_subsys.chnmon_active = false;
}
}
@@ -1078,7 +1172,7 @@ int css_do_rsch(SubchDev *sch)
}
/* If monitoring is active, update counter. */
- if (channel_subsys->chnmon_active) {
+ if (channel_subsys.chnmon_active) {
css_update_chnmon(sch);
}
@@ -1094,23 +1188,23 @@ int css_do_rchp(uint8_t cssid, uint8_t chpid)
{
uint8_t real_cssid;
- if (cssid > channel_subsys->max_cssid) {
+ if (cssid > channel_subsys.max_cssid) {
return -EINVAL;
}
- if (channel_subsys->max_cssid == 0) {
- real_cssid = channel_subsys->default_cssid;
+ if (channel_subsys.max_cssid == 0) {
+ real_cssid = channel_subsys.default_cssid;
} else {
real_cssid = cssid;
}
- if (!channel_subsys->css[real_cssid]) {
+ if (!channel_subsys.css[real_cssid]) {
return -EINVAL;
}
- if (!channel_subsys->css[real_cssid]->chpids[chpid].in_use) {
+ if (!channel_subsys.css[real_cssid]->chpids[chpid].in_use) {
return -ENODEV;
}
- if (!channel_subsys->css[real_cssid]->chpids[chpid].is_virtual) {
+ if (!channel_subsys.css[real_cssid]->chpids[chpid].is_virtual) {
fprintf(stderr,
"rchp unsupported for non-virtual chpid %x.%02x!\n",
real_cssid, chpid);
@@ -1119,8 +1213,8 @@ int css_do_rchp(uint8_t cssid, uint8_t chpid)
/* We don't really use a channel path, so we're done here. */
css_queue_crw(CRW_RSC_CHP, CRW_ERC_INIT,
- channel_subsys->max_cssid > 0 ? 1 : 0, chpid);
- if (channel_subsys->max_cssid > 0) {
+ channel_subsys.max_cssid > 0 ? 1 : 0, chpid);
+ if (channel_subsys.max_cssid > 0) {
css_queue_crw(CRW_RSC_CHP, CRW_ERC_INIT, 0, real_cssid << 8);
}
return 0;
@@ -1131,13 +1225,13 @@ bool css_schid_final(int m, uint8_t cssid, uint8_t ssid, uint16_t schid)
SubchSet *set;
uint8_t real_cssid;
- real_cssid = (!m && (cssid == 0)) ? channel_subsys->default_cssid : cssid;
+ real_cssid = (!m && (cssid == 0)) ? channel_subsys.default_cssid : cssid;
if (real_cssid > MAX_CSSID || ssid > MAX_SSID ||
- !channel_subsys->css[real_cssid] ||
- !channel_subsys->css[real_cssid]->sch_set[ssid]) {
+ !channel_subsys.css[real_cssid] ||
+ !channel_subsys.css[real_cssid]->sch_set[ssid]) {
return true;
}
- set = channel_subsys->css[real_cssid]->sch_set[ssid];
+ set = channel_subsys.css[real_cssid]->sch_set[ssid];
return schid > find_last_bit(set->schids_used,
(MAX_SCHID + 1) / sizeof(unsigned long));
}
@@ -1150,7 +1244,7 @@ static int css_add_virtual_chpid(uint8_t cssid, uint8_t chpid, uint8_t type)
if (cssid > MAX_CSSID) {
return -EINVAL;
}
- css = channel_subsys->css[cssid];
+ css = channel_subsys.css[cssid];
if (!css) {
return -EINVAL;
}
@@ -1171,7 +1265,7 @@ void css_sch_build_virtual_schib(SubchDev *sch, uint8_t chpid, uint8_t type)
PMCW *p = &sch->curr_status.pmcw;
SCSW *s = &sch->curr_status.scsw;
int i;
- CssImage *css = channel_subsys->css[sch->cssid];
+ CssImage *css = channel_subsys.css[sch->cssid];
assert(css != NULL);
memset(p, 0, sizeof(PMCW));
@@ -1197,27 +1291,27 @@ SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, uint16_t schid)
{
uint8_t real_cssid;
- real_cssid = (!m && (cssid == 0)) ? channel_subsys->default_cssid : cssid;
+ real_cssid = (!m && (cssid == 0)) ? channel_subsys.default_cssid : cssid;
- if (!channel_subsys->css[real_cssid]) {
+ if (!channel_subsys.css[real_cssid]) {
return NULL;
}
- if (!channel_subsys->css[real_cssid]->sch_set[ssid]) {
+ if (!channel_subsys.css[real_cssid]->sch_set[ssid]) {
return NULL;
}
- return channel_subsys->css[real_cssid]->sch_set[ssid]->sch[schid];
+ return channel_subsys.css[real_cssid]->sch_set[ssid]->sch[schid];
}
bool css_subch_visible(SubchDev *sch)
{
- if (sch->ssid > channel_subsys->max_ssid) {
+ if (sch->ssid > channel_subsys.max_ssid) {
return false;
}
- if (sch->cssid != channel_subsys->default_cssid) {
- return (channel_subsys->max_cssid > 0);
+ if (sch->cssid != channel_subsys.default_cssid) {
+ return (channel_subsys.max_cssid > 0);
}
return true;
@@ -1225,20 +1319,20 @@ bool css_subch_visible(SubchDev *sch)
bool css_present(uint8_t cssid)
{
- return (channel_subsys->css[cssid] != NULL);
+ return (channel_subsys.css[cssid] != NULL);
}
bool css_devno_used(uint8_t cssid, uint8_t ssid, uint16_t devno)
{
- if (!channel_subsys->css[cssid]) {
+ if (!channel_subsys.css[cssid]) {
return false;
}
- if (!channel_subsys->css[cssid]->sch_set[ssid]) {
+ if (!channel_subsys.css[cssid]->sch_set[ssid]) {
return false;
}
return !!test_bit(devno,
- channel_subsys->css[cssid]->sch_set[ssid]->devnos_used);
+ channel_subsys.css[cssid]->sch_set[ssid]->devnos_used);
}
void css_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid,
@@ -1249,13 +1343,13 @@ void css_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid,
trace_css_assign_subch(sch ? "assign" : "deassign", cssid, ssid, schid,
devno);
- if (!channel_subsys->css[cssid]) {
+ if (!channel_subsys.css[cssid]) {
fprintf(stderr,
"Suspicious call to %s (%x.%x.%04x) for non-existing css!\n",
__func__, cssid, ssid, schid);
return;
}
- css = channel_subsys->css[cssid];
+ css = channel_subsys.css[cssid];
if (!css->sch_set[ssid]) {
css->sch_set[ssid] = g_malloc0(sizeof(SubchSet));
@@ -1280,7 +1374,7 @@ void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid)
/* TODO: Maybe use a static crw pool? */
crw_cont = g_try_malloc0(sizeof(CrwContainer));
if (!crw_cont) {
- channel_subsys->crws_lost = true;
+ channel_subsys.crws_lost = true;
return;
}
crw_cont->crw.flags = (rsc << 8) | erc;
@@ -1288,15 +1382,15 @@ void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid)
crw_cont->crw.flags |= CRW_FLAGS_MASK_C;
}
crw_cont->crw.rsid = rsid;
- if (channel_subsys->crws_lost) {
+ if (channel_subsys.crws_lost) {
crw_cont->crw.flags |= CRW_FLAGS_MASK_R;
- channel_subsys->crws_lost = false;
+ channel_subsys.crws_lost = false;
}
- QTAILQ_INSERT_TAIL(&channel_subsys->pending_crws, crw_cont, sibling);
+ QTAILQ_INSERT_TAIL(&channel_subsys.pending_crws, crw_cont, sibling);
- if (channel_subsys->do_crw_mchk) {
- channel_subsys->do_crw_mchk = false;
+ if (channel_subsys.do_crw_mchk) {
+ channel_subsys.do_crw_mchk = false;
/* Inject crw pending machine check. */
s390_crw_mchk();
}
@@ -1311,9 +1405,9 @@ void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid,
if (add && !hotplugged) {
return;
}
- if (channel_subsys->max_cssid == 0) {
+ if (channel_subsys.max_cssid == 0) {
/* Default cssid shows up as 0. */
- guest_cssid = (cssid == channel_subsys->default_cssid) ? 0 : cssid;
+ guest_cssid = (cssid == channel_subsys.default_cssid) ? 0 : cssid;
} else {
/* Show real cssid to the guest. */
guest_cssid = cssid;
@@ -1322,14 +1416,14 @@ void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid,
* Only notify for higher subchannel sets/channel subsystems if the
* guest has enabled it.
*/
- if ((ssid > channel_subsys->max_ssid) ||
- (guest_cssid > channel_subsys->max_cssid) ||
- ((channel_subsys->max_cssid == 0) &&
- (cssid != channel_subsys->default_cssid))) {
+ if ((ssid > channel_subsys.max_ssid) ||
+ (guest_cssid > channel_subsys.max_cssid) ||
+ ((channel_subsys.max_cssid == 0) &&
+ (cssid != channel_subsys.default_cssid))) {
return;
}
- chain_crw = (channel_subsys->max_ssid > 0) ||
- (channel_subsys->max_cssid > 0);
+ chain_crw = (channel_subsys.max_ssid > 0) ||
+ (channel_subsys.max_cssid > 0);
css_queue_crw(CRW_RSC_SUBCH, CRW_ERC_IPI, chain_crw ? 1 : 0, schid);
if (chain_crw) {
css_queue_crw(CRW_RSC_SUBCH, CRW_ERC_IPI, 0,
@@ -1344,20 +1438,28 @@ void css_generate_chp_crws(uint8_t cssid, uint8_t chpid)
void css_generate_css_crws(uint8_t cssid)
{
- css_queue_crw(CRW_RSC_CSS, 0, 0, cssid);
+ if (!channel_subsys.sei_pending) {
+ css_queue_crw(CRW_RSC_CSS, 0, 0, cssid);
+ }
+ channel_subsys.sei_pending = true;
+}
+
+void css_clear_sei_pending(void)
+{
+ channel_subsys.sei_pending = false;
}
int css_enable_mcsse(void)
{
trace_css_enable_facility("mcsse");
- channel_subsys->max_cssid = MAX_CSSID;
+ channel_subsys.max_cssid = MAX_CSSID;
return 0;
}
int css_enable_mss(void)
{
trace_css_enable_facility("mss");
- channel_subsys->max_ssid = MAX_SSID;
+ channel_subsys.max_ssid = MAX_SSID;
return 0;
}
@@ -1415,7 +1517,6 @@ void subch_device_save(SubchDev *s, QEMUFile *f)
}
qemu_put_byte(f, s->ccw_fmt_1);
qemu_put_byte(f, s->ccw_no_data_cnt);
- return;
}
int subch_device_load(SubchDev *s, QEMUFile *f)
@@ -1481,27 +1582,15 @@ int subch_device_load(SubchDev *s, QEMUFile *f)
*/
if (s->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA) {
if (s->ssid) {
- channel_subsys->max_ssid = MAX_SSID;
+ channel_subsys.max_ssid = MAX_SSID;
}
- if (s->cssid != channel_subsys->default_cssid) {
- channel_subsys->max_cssid = MAX_CSSID;
+ if (s->cssid != channel_subsys.default_cssid) {
+ channel_subsys.max_cssid = MAX_CSSID;
}
}
return 0;
}
-
-static void css_init(void)
-{
- channel_subsys = g_malloc0(sizeof(*channel_subsys));
- QTAILQ_INIT(&channel_subsys->pending_crws);
- channel_subsys->do_crw_mchk = true;
- channel_subsys->crws_lost = false;
- channel_subsys->chnmon_active = false;
- QTAILQ_INIT(&channel_subsys->io_adapters);
-}
-machine_init(css_init);
-
void css_reset_sch(SubchDev *sch)
{
PMCW *p = &sch->curr_status.pmcw;
@@ -1539,18 +1628,19 @@ void css_reset(void)
CrwContainer *crw_cont;
/* Clean up monitoring. */
- channel_subsys->chnmon_active = false;
- channel_subsys->chnmon_area = 0;
+ channel_subsys.chnmon_active = false;
+ channel_subsys.chnmon_area = 0;
/* Clear pending CRWs. */
- while ((crw_cont = QTAILQ_FIRST(&channel_subsys->pending_crws))) {
- QTAILQ_REMOVE(&channel_subsys->pending_crws, crw_cont, sibling);
+ while ((crw_cont = QTAILQ_FIRST(&channel_subsys.pending_crws))) {
+ QTAILQ_REMOVE(&channel_subsys.pending_crws, crw_cont, sibling);
g_free(crw_cont);
}
- channel_subsys->do_crw_mchk = true;
- channel_subsys->crws_lost = false;
+ channel_subsys.sei_pending = false;
+ channel_subsys.do_crw_mchk = true;
+ channel_subsys.crws_lost = false;
/* Reset maximum ids. */
- channel_subsys->max_cssid = 0;
- channel_subsys->max_ssid = 0;
+ channel_subsys.max_cssid = 0;
+ channel_subsys.max_ssid = 0;
}
diff --git a/qemu/hw/s390x/css.h b/qemu/hw/s390x/css.h
index a09bb1f87..a320eea59 100644
--- a/qemu/hw/s390x/css.h
+++ b/qemu/hw/s390x/css.h
@@ -12,6 +12,8 @@
#ifndef CSS_H
#define CSS_H
+#include "hw/s390x/adapter.h"
+#include "hw/s390x/s390_flic.h"
#include "ioinst.h"
/* Channel subsystem constants. */
@@ -86,6 +88,18 @@ struct SubchDev {
void *driver_data;
};
+typedef struct IndAddr {
+ hwaddr addr;
+ uint64_t map;
+ unsigned long refcnt;
+ int len;
+ QTAILQ_ENTRY(IndAddr) sibling;
+} IndAddr;
+
+IndAddr *get_indicator(hwaddr ind_addr, int len);
+void release_indicator(AdapterInfo *adapter, IndAddr *indicator);
+int map_indicator(AdapterInfo *adapter, IndAddr *indicator);
+
typedef SubchDev *(*css_subch_cb_func)(uint8_t m, uint8_t cssid, uint8_t ssid,
uint16_t schid);
void subch_device_save(SubchDev *s, QEMUFile *f);
@@ -103,6 +117,7 @@ void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid,
int hotplugged, int add);
void css_generate_chp_crws(uint8_t cssid, uint8_t chpid);
void css_generate_css_crws(uint8_t cssid);
+void css_clear_sei_pending(void);
void css_adapter_interrupt(uint8_t isc);
#define CSS_IO_ADAPTER_VIRTIO 1
diff --git a/qemu/hw/s390x/event-facility.c b/qemu/hw/s390x/event-facility.c
index 0c700effb..34b2faf01 100644
--- a/qemu/hw/s390x/event-facility.c
+++ b/qemu/hw/s390x/event-facility.c
@@ -15,6 +15,8 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "sysemu/sysemu.h"
#include "hw/s390x/sclp.h"
@@ -31,8 +33,6 @@ struct SCLPEventFacility {
unsigned int receive_mask;
};
-static SCLPEvent cpu_hotplug;
-
/* return true if any child has event pending set */
static bool event_pending(SCLPEventFacility *ef)
{
@@ -240,12 +240,13 @@ static void read_event_data(SCLPEventFacility *ef, SCCB *sccb)
sclp_active_selection_mask = sclp_cp_receive_mask;
break;
case SCLP_SELECTIVE_READ:
- if (!(sclp_cp_receive_mask & be32_to_cpu(red->mask))) {
+ sclp_active_selection_mask = be32_to_cpu(red->mask);
+ if (!sclp_cp_receive_mask ||
+ (sclp_active_selection_mask & ~sclp_cp_receive_mask)) {
sccb->h.response_code =
cpu_to_be16(SCLP_RC_INVALID_SELECTION_MASK);
goto out;
}
- sclp_active_selection_mask = be32_to_cpu(red->mask);
break;
default:
sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_FUNCTION);
@@ -286,8 +287,26 @@ out:
#define TYPE_SCLP_EVENTS_BUS "s390-sclp-events-bus"
+static void sclp_events_bus_realize(BusState *bus, Error **errp)
+{
+ BusChild *kid;
+
+ /* TODO: recursive realization has to be done in common code */
+ QTAILQ_FOREACH(kid, &bus->children, sibling) {
+ DeviceState *dev = kid->child;
+
+ object_property_set_bool(OBJECT(dev), true, "realized", errp);
+ if (*errp) {
+ return;
+ }
+ }
+}
+
static void sclp_events_bus_class_init(ObjectClass *klass, void *data)
{
+ BusClass *bc = BUS_CLASS(klass);
+
+ bc->realize = sclp_events_bus_realize;
}
static const TypeInfo sclp_events_bus_info = {
@@ -324,26 +343,26 @@ static const VMStateDescription vmstate_event_facility = {
}
};
-static int init_event_facility(SCLPEventFacility *event_facility)
+static void init_event_facility(Object *obj)
{
- DeviceState *sdev = DEVICE(event_facility);
- DeviceState *quiesce;
+ SCLPEventFacility *event_facility = EVENT_FACILITY(obj);
+ DeviceState *sdev = DEVICE(obj);
+ Object *new;
/* Spawn a new bus for SCLP events */
qbus_create_inplace(&event_facility->sbus, sizeof(event_facility->sbus),
TYPE_SCLP_EVENTS_BUS, sdev, NULL);
- quiesce = qdev_create(&event_facility->sbus.qbus, "sclpquiesce");
- if (!quiesce) {
- return -1;
- }
- qdev_init_nofail(quiesce);
-
- object_initialize(&cpu_hotplug, sizeof(cpu_hotplug), TYPE_SCLP_CPU_HOTPLUG);
- qdev_set_parent_bus(DEVICE(&cpu_hotplug), BUS(&event_facility->sbus));
- object_property_set_bool(OBJECT(&cpu_hotplug), true, "realized", NULL);
+ new = object_new(TYPE_SCLP_QUIESCE);
+ object_property_add_child(obj, TYPE_SCLP_QUIESCE, new, NULL);
+ object_unref(new);
+ qdev_set_parent_bus(DEVICE(new), &event_facility->sbus.qbus);
- return 0;
+ new = object_new(TYPE_SCLP_CPU_HOTPLUG);
+ object_property_add_child(obj, TYPE_SCLP_CPU_HOTPLUG, new, NULL);
+ object_unref(new);
+ qdev_set_parent_bus(DEVICE(new), &event_facility->sbus.qbus);
+ /* the facility will automatically realize the devices via the bus */
}
static void reset_event_facility(DeviceState *dev)
@@ -362,7 +381,6 @@ static void init_event_facility_class(ObjectClass *klass, void *data)
dc->reset = reset_event_facility;
dc->vmsd = &vmstate_event_facility;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
- k->init = init_event_facility;
k->command_handler = command_handler;
k->event_pending = event_pending;
}
@@ -370,6 +388,7 @@ static void init_event_facility_class(ObjectClass *klass, void *data)
static const TypeInfo sclp_event_facility_info = {
.name = TYPE_SCLP_EVENT_FACILITY,
.parent = TYPE_SYS_BUS_DEVICE,
+ .instance_init = init_event_facility,
.instance_size = sizeof(SCLPEventFacility),
.class_init = init_event_facility_class,
.class_size = sizeof(SCLPEventFacilityClass),
diff --git a/qemu/hw/s390x/ipl.c b/qemu/hw/s390x/ipl.c
index 2e0a8b6e0..f10420027 100644
--- a/qemu/hw/s390x/ipl.c
+++ b/qemu/hw/s390x/ipl.c
@@ -11,11 +11,12 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "sysemu/sysemu.h"
#include "cpu.h"
#include "elf.h"
#include "hw/loader.h"
-#include "hw/sysbus.h"
#include "hw/s390x/virtio-ccw.h"
#include "hw/s390x/css.h"
#include "ipl.h"
@@ -29,44 +30,6 @@
#define ZIPL_IMAGE_START 0x009000UL
#define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64)
-#define TYPE_S390_IPL "s390-ipl"
-#define S390_IPL(obj) \
- OBJECT_CHECK(S390IPLState, (obj), TYPE_S390_IPL)
-#if 0
-#define S390_IPL_CLASS(klass) \
- OBJECT_CLASS_CHECK(S390IPLState, (klass), TYPE_S390_IPL)
-#define S390_IPL_GET_CLASS(obj) \
- OBJECT_GET_CLASS(S390IPLState, (obj), TYPE_S390_IPL)
-#endif
-
-typedef struct S390IPLClass {
- /*< private >*/
- SysBusDeviceClass parent_class;
- /*< public >*/
-
- void (*parent_reset) (SysBusDevice *dev);
-} S390IPLClass;
-
-typedef struct S390IPLState {
- /*< private >*/
- SysBusDevice parent_obj;
- uint64_t start_addr;
- uint64_t bios_start_addr;
- bool enforce_bios;
- IplParameterBlock iplb;
- bool iplb_valid;
- bool reipl_requested;
-
- /*< public >*/
- char *kernel;
- char *initrd;
- char *cmdline;
- char *firmware;
- uint8_t cssid;
- uint8_t ssid;
- uint16_t devno;
-} S390IPLState;
-
static const VMStateDescription vmstate_iplb = {
.name = "ipl/iplb",
.version_id = 0,
@@ -95,6 +58,11 @@ static const VMStateDescription vmstate_ipl = {
}
};
+static S390IPLState *get_ipl_device(void)
+{
+ return S390_IPL(object_resolve_path_type("", TYPE_S390_IPL, NULL));
+}
+
static uint64_t bios_translate_addr(void *opaque, uint64_t srcaddr)
{
uint64_t dstaddr = *(uint64_t *) opaque;
@@ -105,11 +73,12 @@ static uint64_t bios_translate_addr(void *opaque, uint64_t srcaddr)
return srcaddr + dstaddr;
}
-static int s390_ipl_init(SysBusDevice *dev)
+static void s390_ipl_realize(DeviceState *dev, Error **errp)
{
S390IPLState *ipl = S390_IPL(dev);
uint64_t pentry = KERN_IMAGE_START;
int kernel_size;
+ Error *err = NULL;
int bios_size;
char *bios_filename;
@@ -127,17 +96,18 @@ static int s390_ipl_init(SysBusDevice *dev)
bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
if (bios_filename == NULL) {
- hw_error("could not find stage1 bootloader\n");
+ error_setg(&err, "could not find stage1 bootloader");
+ goto error;
}
bios_size = load_elf(bios_filename, bios_translate_addr, &fwbase,
&ipl->bios_start_addr, NULL, NULL, 1,
- ELF_MACHINE, 0);
+ EM_S390, 0, 0);
if (bios_size > 0) {
/* Adjust ELF start address to final location */
ipl->bios_start_addr += fwbase;
} else {
- /* Try to load non-ELF file (e.g. s390-zipl.rom) */
+ /* Try to load non-ELF file (e.g. s390-ccw.img) */
bios_size = load_image_targphys(bios_filename, ZIPL_IMAGE_START,
4096);
ipl->bios_start_addr = ZIPL_IMAGE_START;
@@ -145,7 +115,8 @@ static int s390_ipl_init(SysBusDevice *dev)
g_free(bios_filename);
if (bios_size == -1) {
- hw_error("could not load bootloader '%s'\n", bios_name);
+ error_setg(&err, "could not load bootloader '%s'", bios_name);
+ goto error;
}
/* default boot target is the bios */
@@ -154,13 +125,13 @@ static int s390_ipl_init(SysBusDevice *dev)
if (ipl->kernel) {
kernel_size = load_elf(ipl->kernel, NULL, NULL, &pentry, NULL,
- NULL, 1, ELF_MACHINE, 0);
+ NULL, 1, EM_S390, 0, 0);
if (kernel_size < 0) {
kernel_size = load_image_targphys(ipl->kernel, 0, ram_size);
}
if (kernel_size < 0) {
- fprintf(stderr, "could not load kernel '%s'\n", ipl->kernel);
- return -1;
+ error_setg(&err, "could not load kernel '%s'", ipl->kernel);
+ goto error;
}
/*
* Is it a Linux kernel (starting at 0x10000)? If yes, we fill in the
@@ -187,9 +158,8 @@ static int s390_ipl_init(SysBusDevice *dev)
initrd_size = load_image_targphys(ipl->initrd, initrd_offset,
ram_size - initrd_offset);
if (initrd_size == -1) {
- fprintf(stderr, "qemu: could not load initrd '%s'\n",
- ipl->initrd);
- exit(1);
+ error_setg(&err, "could not load initrd '%s'", ipl->initrd);
+ goto error;
}
/*
@@ -200,7 +170,9 @@ static int s390_ipl_init(SysBusDevice *dev)
stq_p(rom_ptr(INITRD_PARM_SIZE), initrd_size);
}
}
- return 0;
+ qemu_register_reset(qdev_reset_all_fn, dev);
+error:
+ error_propagate(errp, err);
}
static Property s390_ipl_properties[] = {
@@ -218,7 +190,7 @@ static Property s390_ipl_properties[] = {
* - -1 if no valid boot device was found
* - ccw id of the boot device otherwise
*/
-static uint64_t s390_update_iplstate(CPUS390XState *env, S390IPLState *ipl)
+static uint64_t s390_update_iplstate(S390IPLState *ipl)
{
DeviceState *dev_st;
@@ -251,25 +223,19 @@ out:
return (uint32_t) (ipl->cssid << 24 | ipl->ssid << 16 | ipl->devno);
}
-int s390_ipl_update_diag308(IplParameterBlock *iplb)
+void s390_ipl_update_diag308(IplParameterBlock *iplb)
{
- S390IPLState *ipl;
+ S390IPLState *ipl = get_ipl_device();
- ipl = S390_IPL(object_resolve_path(TYPE_S390_IPL, NULL));
- if (ipl) {
- ipl->iplb = *iplb;
- ipl->iplb_valid = true;
- return 0;
- }
- return -1;
+ ipl->iplb = *iplb;
+ ipl->iplb_valid = true;
}
IplParameterBlock *s390_ipl_get_iplb(void)
{
- S390IPLState *ipl;
+ S390IPLState *ipl = get_ipl_device();
- ipl = S390_IPL(object_resolve_path(TYPE_S390_IPL, NULL));
- if (!ipl || !ipl->iplb_valid) {
+ if (!ipl->iplb_valid) {
return NULL;
}
return &ipl->iplb;
@@ -277,41 +243,40 @@ IplParameterBlock *s390_ipl_get_iplb(void)
void s390_reipl_request(void)
{
- S390IPLState *ipl;
+ S390IPLState *ipl = get_ipl_device();
- ipl = S390_IPL(object_resolve_path(TYPE_S390_IPL, NULL));
ipl->reipl_requested = true;
qemu_system_reset_request();
}
+void s390_ipl_prepare_cpu(S390CPU *cpu)
+{
+ S390IPLState *ipl = get_ipl_device();
+
+ cpu->env.psw.addr = ipl->start_addr;
+ cpu->env.psw.mask = IPL_PSW_MASK;
+
+ if (!ipl->kernel || ipl->iplb_valid) {
+ cpu->env.psw.addr = ipl->bios_start_addr;
+ cpu->env.regs[7] = s390_update_iplstate(ipl);
+ }
+}
+
static void s390_ipl_reset(DeviceState *dev)
{
S390IPLState *ipl = S390_IPL(dev);
- S390CPU *cpu = S390_CPU(qemu_get_cpu(0));
- CPUS390XState *env = &cpu->env;
-
- env->psw.addr = ipl->start_addr;
- env->psw.mask = IPL_PSW_MASK;
if (!ipl->reipl_requested) {
ipl->iplb_valid = false;
}
ipl->reipl_requested = false;
-
- if (!ipl->kernel || ipl->iplb_valid) {
- env->psw.addr = ipl->bios_start_addr;
- env->regs[7] = s390_update_iplstate(env, ipl);
- }
-
- s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
}
static void s390_ipl_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = s390_ipl_init;
+ dc->realize = s390_ipl_realize;
dc->props = s390_ipl_properties;
dc->reset = s390_ipl_reset;
dc->vmsd = &vmstate_ipl;
@@ -320,8 +285,8 @@ static void s390_ipl_class_init(ObjectClass *klass, void *data)
static const TypeInfo s390_ipl_info = {
.class_init = s390_ipl_class_init,
- .parent = TYPE_SYS_BUS_DEVICE,
- .name = "s390-ipl",
+ .parent = TYPE_DEVICE,
+ .name = TYPE_S390_IPL,
.instance_size = sizeof(S390IPLState),
};
diff --git a/qemu/hw/s390x/ipl.h b/qemu/hw/s390x/ipl.h
index 70497bc65..6b48ed7b9 100644
--- a/qemu/hw/s390x/ipl.h
+++ b/qemu/hw/s390x/ipl.h
@@ -12,14 +12,42 @@
#ifndef HW_S390_IPL_H
#define HW_S390_IPL_H
+#include "hw/qdev.h"
+#include "cpu.h"
+
typedef struct IplParameterBlock {
uint8_t reserved1[110];
uint16_t devno;
uint8_t reserved2[88];
} IplParameterBlock;
-int s390_ipl_update_diag308(IplParameterBlock *iplb);
+void s390_ipl_update_diag308(IplParameterBlock *iplb);
+void s390_ipl_prepare_cpu(S390CPU *cpu);
IplParameterBlock *s390_ipl_get_iplb(void);
void s390_reipl_request(void);
+#define TYPE_S390_IPL "s390-ipl"
+#define S390_IPL(obj) OBJECT_CHECK(S390IPLState, (obj), TYPE_S390_IPL)
+
+struct S390IPLState {
+ /*< private >*/
+ DeviceState parent_obj;
+ uint64_t start_addr;
+ uint64_t bios_start_addr;
+ bool enforce_bios;
+ IplParameterBlock iplb;
+ bool iplb_valid;
+ bool reipl_requested;
+
+ /*< public >*/
+ char *kernel;
+ char *initrd;
+ char *cmdline;
+ char *firmware;
+ uint8_t cssid;
+ uint8_t ssid;
+ uint16_t devno;
+};
+typedef struct S390IPLState S390IPLState;
+
#endif
diff --git a/qemu/hw/s390x/s390-pci-bus.c b/qemu/hw/s390x/s390-pci-bus.c
index 560b66a50..918b58543 100644
--- a/qemu/hw/s390x/s390-pci-bus.c
+++ b/qemu/hw/s390x/s390-pci-bus.c
@@ -11,6 +11,9 @@
* directory.
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "s390-pci-bus.h"
#include <hw/pci/pci_bus.h>
#include <hw/pci/msi.h>
@@ -123,7 +126,6 @@ void s390_pci_sclp_configure(int configure, SCCB *sccb)
}
psccb->header.response_code = cpu_to_be16(rc);
- return;
}
static uint32_t s390_pci_get_pfid(PCIDevice *pdev)
@@ -308,9 +310,8 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr,
{
uint64_t pte;
uint32_t flags;
- S390PCIBusDevice *pbdev = container_of(iommu, S390PCIBusDevice, mr);
- S390pciState *s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pbdev->pdev)
- ->qbus.parent);
+ S390PCIBusDevice *pbdev = container_of(iommu, S390PCIBusDevice, iommu_mr);
+ S390pciState *s;
IOMMUTLBEntry ret = {
.target_as = &address_space_memory,
.iova = 0,
@@ -319,8 +320,13 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr,
.perm = IOMMU_NONE,
};
+ if (!pbdev->configured || !pbdev->pdev || !(pbdev->fh & FH_ENABLED)) {
+ return ret;
+ }
+
DPRINTF("iommu trans addr 0x%" PRIx64 "\n", addr);
+ s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pbdev->pdev)->qbus.parent);
/* s390 does not have an APIC mapped to main storage so we use
* a separate AddressSpace only for msix notifications
*/
@@ -425,6 +431,10 @@ static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data,
return;
}
+ if (!(pbdev->fh & FH_ENABLED)) {
+ return;
+ }
+
ind_bit = pbdev->routes.adapter.ind_offset;
sum_bit = pbdev->routes.adapter.summary_offset;
@@ -435,8 +445,6 @@ static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data,
io_int_word = (pbdev->isc << 27) | IO_INT_WORD_AI;
s390_io_interrupt(0, 0, 0, io_int_word);
}
-
- return;
}
static uint64_t s390_msi_ctrl_read(void *opaque, hwaddr addr, unsigned size)
@@ -450,14 +458,32 @@ static const MemoryRegionOps s390_msi_ctrl_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
+void s390_pcihost_iommu_configure(S390PCIBusDevice *pbdev, bool enable)
+{
+ pbdev->configured = false;
+
+ if (enable) {
+ uint64_t size = pbdev->pal - pbdev->pba + 1;
+ memory_region_init_iommu(&pbdev->iommu_mr, OBJECT(&pbdev->mr),
+ &s390_iommu_ops, "iommu-s390", size);
+ memory_region_add_subregion(&pbdev->mr, pbdev->pba, &pbdev->iommu_mr);
+ } else {
+ memory_region_del_subregion(&pbdev->mr, &pbdev->iommu_mr);
+ }
+
+ pbdev->configured = true;
+}
+
static void s390_pcihost_init_as(S390pciState *s)
{
int i;
+ S390PCIBusDevice *pbdev;
for (i = 0; i < PCI_SLOT_MAX; i++) {
- memory_region_init_iommu(&s->pbdev[i].mr, OBJECT(s),
- &s390_iommu_ops, "iommu-s390", UINT64_MAX);
- address_space_init(&s->pbdev[i].as, &s->pbdev[i].mr, "iommu-pci");
+ pbdev = &s->pbdev[i];
+ memory_region_init(&pbdev->mr, OBJECT(s),
+ "iommu-root-s390", UINT64_MAX);
+ address_space_init(&pbdev->as, &pbdev->mr, "iommu-pci");
}
memory_region_init_io(&s->msix_notify_mr, OBJECT(s),
@@ -500,7 +526,7 @@ static int s390_pcihost_setup_msix(S390PCIBusDevice *pbdev)
return 0;
}
- ctrl = pci_host_config_read_common(pbdev->pdev, pos + PCI_CAP_FLAGS,
+ ctrl = pci_host_config_read_common(pbdev->pdev, pos + PCI_MSIX_FLAGS,
pci_config_size(pbdev->pdev), sizeof(ctrl));
table = pci_host_config_read_common(pbdev->pdev, pos + PCI_MSIX_TABLE,
pci_config_size(pbdev->pdev), sizeof(table));
@@ -539,7 +565,6 @@ static void s390_pcihost_hot_plug(HotplugHandler *hotplug_dev,
s390_pci_generate_plug_event(HP_EVENT_TO_CONFIGURED,
pbdev->fh, pbdev->fid);
}
- return;
}
static void s390_pcihost_hot_unplug(HotplugHandler *hotplug_dev,
@@ -574,7 +599,7 @@ static void s390_pcihost_class_init(ObjectClass *klass, void *data)
k->init = s390_pcihost_init;
hc->plug = s390_pcihost_hot_plug;
hc->unplug = s390_pcihost_hot_unplug;
- msi_supported = true;
+ msi_nonbroken = true;
}
static const TypeInfo s390_pcihost_info = {
diff --git a/qemu/hw/s390x/s390-pci-bus.h b/qemu/hw/s390x/s390-pci-bus.h
index 464a92eed..59fd5c958 100644
--- a/qemu/hw/s390x/s390-pci-bus.h
+++ b/qemu/hw/s390x/s390-pci-bus.h
@@ -23,6 +23,7 @@
#define TYPE_S390_PCI_HOST_BRIDGE "s390-pcihost"
#define FH_VIRT 0x00ff0000
#define ENABLE_BIT_OFFSET 31
+#define FH_ENABLED (1 << ENABLE_BIT_OFFSET)
#define S390_PCIPT_ADAPTER 2
#define S390_PCI_HOST_BRIDGE(obj) \
@@ -231,6 +232,9 @@ typedef struct S390PCIBusDevice {
AdapterRoutes routes;
AddressSpace as;
MemoryRegion mr;
+ MemoryRegion iommu_mr;
+ IndAddr *summary_ind;
+ IndAddr *indicator;
} S390PCIBusDevice;
typedef struct S390pciState {
@@ -244,6 +248,7 @@ typedef struct S390pciState {
int chsc_sei_nt2_get_event(void *res);
int chsc_sei_nt2_have_event(void);
void s390_pci_sclp_configure(int configure, SCCB *sccb);
+void s390_pcihost_iommu_configure(S390PCIBusDevice *pbdev, bool enable);
S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx);
S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh);
S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid);
diff --git a/qemu/hw/s390x/s390-pci-inst.c b/qemu/hw/s390x/s390-pci-inst.c
index f9151a9af..b28e7d14f 100644
--- a/qemu/hw/s390x/s390-pci-inst.c
+++ b/qemu/hw/s390x/s390-pci-inst.c
@@ -11,6 +11,9 @@
* directory.
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "s390-pci-inst.h"
#include "s390-pci-bus.h"
#include <exec/memory-internal.h>
@@ -105,7 +108,8 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc)
pci_get_word(pbdev->pdev->config + PCI_DEVICE_ID));
stw_p(&rrb->response.fh_list[idx - resume_token].vendor_id,
pci_get_word(pbdev->pdev->config + PCI_VENDOR_ID));
- stl_p(&rrb->response.fh_list[idx - resume_token].config, 0x80000000);
+ stl_p(&rrb->response.fh_list[idx - resume_token].config,
+ pbdev->configured << 31);
stl_p(&rrb->response.fh_list[idx - resume_token].fid, pbdev->fid);
stl_p(&rrb->response.fh_list[idx - resume_token].fh, pbdev->fh);
@@ -208,12 +212,12 @@ int clp_service_call(S390CPU *cpu, uint8_t r2)
switch (reqsetpci->oc) {
case CLP_SET_ENABLE_PCI_FN:
- pbdev->fh = pbdev->fh | 1 << ENABLE_BIT_OFFSET;
+ pbdev->fh = pbdev->fh | FH_ENABLED;
stl_p(&ressetpci->fh, pbdev->fh);
stw_p(&ressetpci->hdr.rsp, CLP_RC_OK);
break;
case CLP_SET_DISABLE_PCI_FN:
- pbdev->fh = pbdev->fh & ~(1 << ENABLE_BIT_OFFSET);
+ pbdev->fh = pbdev->fh & ~FH_ENABLED;
pbdev->error_state = false;
pbdev->lgstg_blocked = false;
stl_p(&ressetpci->fh, pbdev->fh);
@@ -313,7 +317,7 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2)
offset = env->regs[r2 + 1];
pbdev = s390_pci_find_dev_by_fh(fh);
- if (!pbdev) {
+ if (!pbdev || !(pbdev->fh & FH_ENABLED)) {
DPRINTF("pcilg no pci dev\n");
setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
return 0;
@@ -430,7 +434,7 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2)
offset = env->regs[r2 + 1];
pbdev = s390_pci_find_dev_by_fh(fh);
- if (!pbdev) {
+ if (!pbdev || !(pbdev->fh & FH_ENABLED)) {
DPRINTF("pcistg no pci dev\n");
setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
return 0;
@@ -521,14 +525,13 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2)
end = start + env->regs[r2 + 1];
pbdev = s390_pci_find_dev_by_fh(fh);
-
- if (!pbdev) {
+ if (!pbdev || !(pbdev->fh & FH_ENABLED)) {
DPRINTF("rpcit no pci dev\n");
setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
goto out;
}
- mr = pci_device_iommu_address_space(pbdev->pdev)->root;
+ mr = &pbdev->iommu_mr;
while (start < end) {
entry = mr->iommu_ops->translate(mr, start, 0);
@@ -586,7 +589,7 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr,
}
pbdev = s390_pci_find_dev_by_fh(fh);
- if (!pbdev) {
+ if (!pbdev || !(pbdev->fh & FH_ENABLED)) {
DPRINTF("pcistb no pci dev fh 0x%x\n", fh);
setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
return 0;
@@ -620,19 +623,19 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr,
static int reg_irqs(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib)
{
- int ret;
- S390FLICState *fs = s390_get_flic();
- S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
+ int ret, len;
ret = css_register_io_adapter(S390_PCIPT_ADAPTER,
FIB_DATA_ISC(ldl_p(&fib.data)), true, false,
&pbdev->routes.adapter.adapter_id);
assert(ret == 0);
- fsc->io_adapter_map(fs, pbdev->routes.adapter.adapter_id,
- ldq_p(&fib.aisb), true);
- fsc->io_adapter_map(fs, pbdev->routes.adapter.adapter_id,
- ldq_p(&fib.aibv), true);
+ pbdev->summary_ind = get_indicator(ldq_p(&fib.aisb), sizeof(uint64_t));
+ len = BITS_TO_LONGS(FIB_DATA_NOI(ldl_p(&fib.data))) * sizeof(unsigned long);
+ pbdev->indicator = get_indicator(ldq_p(&fib.aibv), len);
+
+ map_indicator(&pbdev->routes.adapter, pbdev->summary_ind);
+ map_indicator(&pbdev->routes.adapter, pbdev->indicator);
pbdev->routes.adapter.summary_addr = ldq_p(&fib.aisb);
pbdev->routes.adapter.summary_offset = FIB_DATA_AISBO(ldl_p(&fib.data));
@@ -648,12 +651,11 @@ static int reg_irqs(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib)
static int dereg_irqs(S390PCIBusDevice *pbdev)
{
- S390FLICState *fs = s390_get_flic();
- S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
-
- fsc->io_adapter_map(fs, pbdev->routes.adapter.adapter_id,
- pbdev->routes.adapter.ind_addr, false);
+ release_indicator(&pbdev->routes.adapter, pbdev->summary_ind);
+ release_indicator(&pbdev->routes.adapter, pbdev->indicator);
+ pbdev->summary_ind = NULL;
+ pbdev->indicator = NULL;
pbdev->routes.adapter.summary_addr = 0;
pbdev->routes.adapter.summary_offset = 0;
pbdev->routes.adapter.ind_addr = 0;
@@ -689,6 +691,9 @@ static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib)
pbdev->pba = pba;
pbdev->pal = pal;
pbdev->g_iota = g_iota;
+
+ s390_pcihost_iommu_configure(pbdev, true);
+
return 0;
}
@@ -697,6 +702,8 @@ static void dereg_ioat(S390PCIBusDevice *pbdev)
pbdev->pba = 0;
pbdev->pal = 0;
pbdev->g_iota = 0;
+
+ s390_pcihost_iommu_configure(pbdev, false);
}
int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar)
@@ -722,7 +729,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar)
}
pbdev = s390_pci_find_dev_by_fh(fh);
- if (!pbdev) {
+ if (!pbdev || !(pbdev->fh & FH_ENABLED)) {
DPRINTF("mpcifc no pci dev fh 0x%x\n", fh);
setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
return 0;
@@ -814,7 +821,7 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar)
((uint32_t)pbdev->sum << 7) | pbdev->routes.adapter.summary_offset;
stl_p(&fib.data, data);
- if (pbdev->fh >> ENABLE_BIT_OFFSET) {
+ if (pbdev->fh & FH_ENABLED) {
fib.fc |= 0x80;
}
diff --git a/qemu/hw/s390x/s390-skeys-kvm.c b/qemu/hw/s390x/s390-skeys-kvm.c
new file mode 100644
index 000000000..131da56bb
--- /dev/null
+++ b/qemu/hw/s390x/s390-skeys-kvm.c
@@ -0,0 +1,76 @@
+/*
+ * s390 storage key device
+ *
+ * Copyright 2015 IBM Corp.
+ * Author(s): Jason J. Herne <jjherne@linux.vnet.ibm.com>
+ *
+ * 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 "hw/s390x/storage-keys.h"
+#include "sysemu/kvm.h"
+#include "qemu/error-report.h"
+
+static int kvm_s390_skeys_enabled(S390SKeysState *ss)
+{
+ S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
+ uint8_t single_key;
+ int r;
+
+ r = skeyclass->get_skeys(ss, 0, 1, &single_key);
+ if (r != 0 && r != KVM_S390_GET_SKEYS_NONE) {
+ error_report("S390_GET_KEYS error %d", r);
+ }
+ return (r == 0);
+}
+
+static int kvm_s390_skeys_get(S390SKeysState *ss, uint64_t start_gfn,
+ uint64_t count, uint8_t *keys)
+{
+ struct kvm_s390_skeys args = {
+ .start_gfn = start_gfn,
+ .count = count,
+ .skeydata_addr = (__u64)keys
+ };
+
+ return kvm_vm_ioctl(kvm_state, KVM_S390_GET_SKEYS, &args);
+}
+
+static int kvm_s390_skeys_set(S390SKeysState *ss, uint64_t start_gfn,
+ uint64_t count, uint8_t *keys)
+{
+ struct kvm_s390_skeys args = {
+ .start_gfn = start_gfn,
+ .count = count,
+ .skeydata_addr = (__u64)keys
+ };
+
+ return kvm_vm_ioctl(kvm_state, KVM_S390_SET_SKEYS, &args);
+}
+
+static void kvm_s390_skeys_class_init(ObjectClass *oc, void *data)
+{
+ S390SKeysClass *skeyclass = S390_SKEYS_CLASS(oc);
+
+ skeyclass->skeys_enabled = kvm_s390_skeys_enabled;
+ skeyclass->get_skeys = kvm_s390_skeys_get;
+ skeyclass->set_skeys = kvm_s390_skeys_set;
+}
+
+static const TypeInfo kvm_s390_skeys_info = {
+ .name = TYPE_KVM_S390_SKEYS,
+ .parent = TYPE_S390_SKEYS,
+ .instance_size = sizeof(S390SKeysState),
+ .class_init = kvm_s390_skeys_class_init,
+ .class_size = sizeof(S390SKeysClass),
+};
+
+static void kvm_s390_skeys_register_types(void)
+{
+ type_register_static(&kvm_s390_skeys_info);
+}
+
+type_init(kvm_s390_skeys_register_types)
diff --git a/qemu/hw/s390x/s390-skeys.c b/qemu/hw/s390x/s390-skeys.c
new file mode 100644
index 000000000..6528ffed1
--- /dev/null
+++ b/qemu/hw/s390x/s390-skeys.c
@@ -0,0 +1,415 @@
+/*
+ * s390 storage key device
+ *
+ * Copyright 2015 IBM Corp.
+ * Author(s): Jason J. Herne <jjherne@linux.vnet.ibm.com>
+ *
+ * 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 "hw/boards.h"
+#include "qmp-commands.h"
+#include "migration/qemu-file.h"
+#include "hw/s390x/storage-keys.h"
+#include "qemu/error-report.h"
+
+#define S390_SKEYS_BUFFER_SIZE 131072 /* Room for 128k storage keys */
+#define S390_SKEYS_SAVE_FLAG_EOS 0x01
+#define S390_SKEYS_SAVE_FLAG_SKEYS 0x02
+#define S390_SKEYS_SAVE_FLAG_ERROR 0x04
+
+S390SKeysState *s390_get_skeys_device(void)
+{
+ S390SKeysState *ss;
+
+ ss = S390_SKEYS(object_resolve_path_type("", TYPE_S390_SKEYS, NULL));
+ assert(ss);
+ return ss;
+}
+
+void s390_skeys_init(void)
+{
+ Object *obj;
+
+ if (kvm_enabled()) {
+ obj = object_new(TYPE_KVM_S390_SKEYS);
+ } else {
+ obj = object_new(TYPE_QEMU_S390_SKEYS);
+ }
+ object_property_add_child(qdev_get_machine(), TYPE_S390_SKEYS,
+ obj, NULL);
+ object_unref(obj);
+
+ qdev_init_nofail(DEVICE(obj));
+}
+
+static void write_keys(QEMUFile *f, uint8_t *keys, uint64_t startgfn,
+ uint64_t count, Error **errp)
+{
+ uint64_t curpage = startgfn;
+ uint64_t maxpage = curpage + count - 1;
+ const char *fmt = "page=%03" PRIx64 ": key(%d) => ACC=%X, FP=%d, REF=%d,"
+ " ch=%d, reserved=%d\n";
+ char buf[128];
+ int len;
+
+ for (; curpage <= maxpage; curpage++) {
+ uint8_t acc = (*keys & 0xF0) >> 4;
+ int fp = (*keys & 0x08);
+ int ref = (*keys & 0x04);
+ int ch = (*keys & 0x02);
+ int res = (*keys & 0x01);
+
+ len = snprintf(buf, sizeof(buf), fmt, curpage,
+ *keys, acc, fp, ref, ch, res);
+ assert(len < sizeof(buf));
+ qemu_put_buffer(f, (uint8_t *)buf, len);
+ keys++;
+ }
+}
+
+void hmp_info_skeys(Monitor *mon, const QDict *qdict)
+{
+ S390SKeysState *ss = s390_get_skeys_device();
+ S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
+ uint64_t addr = qdict_get_int(qdict, "addr");
+ uint8_t key;
+ int r;
+
+ /* Quick check to see if guest is using storage keys*/
+ if (!skeyclass->skeys_enabled(ss)) {
+ monitor_printf(mon, "Error: This guest is not using storage keys\n");
+ return;
+ }
+
+ r = skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key);
+ if (r < 0) {
+ monitor_printf(mon, "Error: %s\n", strerror(-r));
+ return;
+ }
+
+ monitor_printf(mon, " key: 0x%X\n", key);
+}
+
+void hmp_dump_skeys(Monitor *mon, const QDict *qdict)
+{
+ const char *filename = qdict_get_str(qdict, "filename");
+ Error *err = NULL;
+
+ qmp_dump_skeys(filename, &err);
+ if (err) {
+ error_report_err(err);
+ }
+}
+
+void qmp_dump_skeys(const char *filename, Error **errp)
+{
+ S390SKeysState *ss = s390_get_skeys_device();
+ S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
+ const uint64_t total_count = ram_size / TARGET_PAGE_SIZE;
+ uint64_t handled_count = 0, cur_count;
+ Error *lerr = NULL;
+ vaddr cur_gfn = 0;
+ uint8_t *buf;
+ int ret;
+ QEMUFile *f;
+
+ /* Quick check to see if guest is using storage keys*/
+ if (!skeyclass->skeys_enabled(ss)) {
+ error_setg(errp, "This guest is not using storage keys - "
+ "nothing to dump");
+ return;
+ }
+
+ f = qemu_fopen(filename, "wb");
+ if (!f) {
+ error_setg_file_open(errp, errno, filename);
+ return;
+ }
+
+ buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
+ if (!buf) {
+ error_setg(errp, "Could not allocate memory");
+ goto out;
+ }
+
+ /* we'll only dump initial memory for now */
+ while (handled_count < total_count) {
+ /* Calculate how many keys to ask for & handle overflow case */
+ cur_count = MIN(total_count - handled_count, S390_SKEYS_BUFFER_SIZE);
+
+ ret = skeyclass->get_skeys(ss, cur_gfn, cur_count, buf);
+ if (ret < 0) {
+ error_setg(errp, "get_keys error %d", ret);
+ goto out_free;
+ }
+
+ /* write keys to stream */
+ write_keys(f, buf, cur_gfn, cur_count, &lerr);
+ if (lerr) {
+ goto out_free;
+ }
+
+ cur_gfn += cur_count;
+ handled_count += cur_count;
+ }
+
+out_free:
+ error_propagate(errp, lerr);
+ g_free(buf);
+out:
+ qemu_fclose(f);
+}
+
+static void qemu_s390_skeys_init(Object *obj)
+{
+ QEMUS390SKeysState *skeys = QEMU_S390_SKEYS(obj);
+ MachineState *machine = MACHINE(qdev_get_machine());
+
+ skeys->key_count = machine->maxram_size / TARGET_PAGE_SIZE;
+ skeys->keydata = g_malloc0(skeys->key_count);
+}
+
+static int qemu_s390_skeys_enabled(S390SKeysState *ss)
+{
+ return 1;
+}
+
+/*
+ * TODO: for memory hotplug support qemu_s390_skeys_set and qemu_s390_skeys_get
+ * will have to make sure that the given gfn belongs to a memory region and not
+ * a memory hole.
+ */
+static int qemu_s390_skeys_set(S390SKeysState *ss, uint64_t start_gfn,
+ uint64_t count, uint8_t *keys)
+{
+ QEMUS390SKeysState *skeydev = QEMU_S390_SKEYS(ss);
+ int i;
+
+ /* Check for uint64 overflow and access beyond end of key data */
+ if (start_gfn + count > skeydev->key_count || start_gfn + count < count) {
+ error_report("Error: Setting storage keys for page beyond the end "
+ "of memory: gfn=%" PRIx64 " count=%" PRId64,
+ start_gfn, count);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < count; i++) {
+ skeydev->keydata[start_gfn + i] = keys[i];
+ }
+ return 0;
+}
+
+static int qemu_s390_skeys_get(S390SKeysState *ss, uint64_t start_gfn,
+ uint64_t count, uint8_t *keys)
+{
+ QEMUS390SKeysState *skeydev = QEMU_S390_SKEYS(ss);
+ int i;
+
+ /* Check for uint64 overflow and access beyond end of key data */
+ if (start_gfn + count > skeydev->key_count || start_gfn + count < count) {
+ error_report("Error: Getting storage keys for page beyond the end "
+ "of memory: gfn=%" PRIx64 " count=%" PRId64,
+ start_gfn, count);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < count; i++) {
+ keys[i] = skeydev->keydata[start_gfn + i];
+ }
+ return 0;
+}
+
+static void qemu_s390_skeys_class_init(ObjectClass *oc, void *data)
+{
+ S390SKeysClass *skeyclass = S390_SKEYS_CLASS(oc);
+
+ skeyclass->skeys_enabled = qemu_s390_skeys_enabled;
+ skeyclass->get_skeys = qemu_s390_skeys_get;
+ skeyclass->set_skeys = qemu_s390_skeys_set;
+}
+
+static const TypeInfo qemu_s390_skeys_info = {
+ .name = TYPE_QEMU_S390_SKEYS,
+ .parent = TYPE_S390_SKEYS,
+ .instance_init = qemu_s390_skeys_init,
+ .instance_size = sizeof(QEMUS390SKeysState),
+ .class_init = qemu_s390_skeys_class_init,
+ .class_size = sizeof(S390SKeysClass),
+};
+
+static void s390_storage_keys_save(QEMUFile *f, void *opaque)
+{
+ S390SKeysState *ss = S390_SKEYS(opaque);
+ S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
+ uint64_t pages_left = ram_size / TARGET_PAGE_SIZE;
+ uint64_t read_count, eos = S390_SKEYS_SAVE_FLAG_EOS;
+ vaddr cur_gfn = 0;
+ int error = 0;
+ uint8_t *buf;
+
+ if (!skeyclass->skeys_enabled(ss)) {
+ goto end_stream;
+ }
+
+ buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
+ if (!buf) {
+ error_report("storage key save could not allocate memory");
+ goto end_stream;
+ }
+
+ /* We only support initial memory. Standby memory is not handled yet. */
+ qemu_put_be64(f, (cur_gfn * TARGET_PAGE_SIZE) | S390_SKEYS_SAVE_FLAG_SKEYS);
+ qemu_put_be64(f, pages_left);
+
+ while (pages_left) {
+ read_count = MIN(pages_left, S390_SKEYS_BUFFER_SIZE);
+
+ if (!error) {
+ error = skeyclass->get_skeys(ss, cur_gfn, read_count, buf);
+ if (error) {
+ /*
+ * If error: we want to fill the stream with valid data instead
+ * of stopping early so we pad the stream with 0x00 values and
+ * use S390_SKEYS_SAVE_FLAG_ERROR to indicate failure to the
+ * reading side.
+ */
+ error_report("S390_GET_KEYS error %d", error);
+ memset(buf, 0, S390_SKEYS_BUFFER_SIZE);
+ eos = S390_SKEYS_SAVE_FLAG_ERROR;
+ }
+ }
+
+ qemu_put_buffer(f, buf, read_count);
+ cur_gfn += read_count;
+ pages_left -= read_count;
+ }
+
+ g_free(buf);
+end_stream:
+ qemu_put_be64(f, eos);
+}
+
+static int s390_storage_keys_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S390SKeysState *ss = S390_SKEYS(opaque);
+ S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
+ int ret = 0;
+
+ while (!ret) {
+ ram_addr_t addr;
+ int flags;
+
+ addr = qemu_get_be64(f);
+ flags = addr & ~TARGET_PAGE_MASK;
+ addr &= TARGET_PAGE_MASK;
+
+ switch (flags) {
+ case S390_SKEYS_SAVE_FLAG_SKEYS: {
+ const uint64_t total_count = qemu_get_be64(f);
+ uint64_t handled_count = 0, cur_count;
+ uint64_t cur_gfn = addr / TARGET_PAGE_SIZE;
+ uint8_t *buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
+
+ if (!buf) {
+ error_report("storage key load could not allocate memory");
+ ret = -ENOMEM;
+ break;
+ }
+
+ while (handled_count < total_count) {
+ cur_count = MIN(total_count - handled_count,
+ S390_SKEYS_BUFFER_SIZE);
+ qemu_get_buffer(f, buf, cur_count);
+
+ ret = skeyclass->set_skeys(ss, cur_gfn, cur_count, buf);
+ if (ret < 0) {
+ error_report("S390_SET_KEYS error %d", ret);
+ break;
+ }
+ handled_count += cur_count;
+ cur_gfn += cur_count;
+ }
+ g_free(buf);
+ break;
+ }
+ case S390_SKEYS_SAVE_FLAG_ERROR: {
+ error_report("Storage key data is incomplete");
+ ret = -EINVAL;
+ break;
+ }
+ case S390_SKEYS_SAVE_FLAG_EOS:
+ /* normal exit */
+ return 0;
+ default:
+ error_report("Unexpected storage key flag data: %#x", flags);
+ ret = -EINVAL;
+ }
+ }
+
+ return ret;
+}
+
+static inline bool s390_skeys_get_migration_enabled(Object *obj, Error **errp)
+{
+ S390SKeysState *ss = S390_SKEYS(obj);
+
+ return ss->migration_enabled;
+}
+
+static inline void s390_skeys_set_migration_enabled(Object *obj, bool value,
+ Error **errp)
+{
+ S390SKeysState *ss = S390_SKEYS(obj);
+
+ /* Prevent double registration of savevm handler */
+ if (ss->migration_enabled == value) {
+ return;
+ }
+
+ ss->migration_enabled = value;
+
+ if (ss->migration_enabled) {
+ register_savevm(NULL, TYPE_S390_SKEYS, 0, 1, s390_storage_keys_save,
+ s390_storage_keys_load, ss);
+ } else {
+ unregister_savevm(DEVICE(ss), TYPE_S390_SKEYS, ss);
+ }
+}
+
+static void s390_skeys_instance_init(Object *obj)
+{
+ object_property_add_bool(obj, "migration-enabled",
+ s390_skeys_get_migration_enabled,
+ s390_skeys_set_migration_enabled, NULL);
+ object_property_set_bool(obj, true, "migration-enabled", NULL);
+}
+
+static void s390_skeys_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->hotpluggable = false;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+}
+
+static const TypeInfo s390_skeys_info = {
+ .name = TYPE_S390_SKEYS,
+ .parent = TYPE_DEVICE,
+ .instance_init = s390_skeys_instance_init,
+ .instance_size = sizeof(S390SKeysState),
+ .class_init = s390_skeys_class_init,
+ .class_size = sizeof(S390SKeysClass),
+ .abstract = true,
+};
+
+static void qemu_s390_skeys_register_types(void)
+{
+ type_register_static(&s390_skeys_info);
+ type_register_static(&qemu_s390_skeys_info);
+}
+
+type_init(qemu_s390_skeys_register_types)
diff --git a/qemu/hw/s390x/s390-virtio-bus.c b/qemu/hw/s390x/s390-virtio-bus.c
deleted file mode 100644
index 77aec8a5b..000000000
--- a/qemu/hw/s390x/s390-virtio-bus.c
+++ /dev/null
@@ -1,763 +0,0 @@
-/*
- * QEMU S390 virtio target
- *
- * Copyright (c) 2009 Alexander Graf <agraf@suse.de>
- *
- * 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 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 <http://www.gnu.org/licenses/>.
- */
-
-#include "hw/hw.h"
-#include "sysemu/block-backend.h"
-#include "sysemu/sysemu.h"
-#include "hw/boards.h"
-#include "hw/loader.h"
-#include "elf.h"
-#include "hw/virtio/virtio.h"
-#include "hw/virtio/virtio-rng.h"
-#include "hw/virtio/virtio-serial.h"
-#include "hw/virtio/virtio-net.h"
-#include "hw/virtio/vhost-scsi.h"
-#include "hw/sysbus.h"
-#include "sysemu/kvm.h"
-
-#include "hw/s390x/s390-virtio-bus.h"
-#include "hw/virtio/virtio-bus.h"
-
-/* #define DEBUG_S390 */
-
-#ifdef DEBUG_S390
-#define DPRINTF(fmt, ...) \
- do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) \
- do { } while (0)
-#endif
-
-#define VIRTIO_S390_QUEUE_MAX 64
-
-static void virtio_s390_bus_new(VirtioBusState *bus, size_t bus_size,
- VirtIOS390Device *dev);
-
-static const TypeInfo s390_virtio_bus_info = {
- .name = TYPE_S390_VIRTIO_BUS,
- .parent = TYPE_BUS,
- .instance_size = sizeof(VirtIOS390Bus),
-};
-
-static ram_addr_t s390_virtio_device_num_vq(VirtIOS390Device *dev);
-
-/* length of VirtIO device pages */
-const hwaddr virtio_size = S390_DEVICE_PAGES * TARGET_PAGE_SIZE;
-
-static void s390_virtio_bus_reset(void *opaque)
-{
- VirtIOS390Bus *bus = opaque;
- bus->next_ring = bus->dev_page + TARGET_PAGE_SIZE;
-}
-
-void s390_virtio_reset_idx(VirtIOS390Device *dev)
-{
- int i;
- hwaddr idx_addr;
- uint8_t num_vq;
-
- num_vq = s390_virtio_device_num_vq(dev);
- for (i = 0; i < num_vq; i++) {
- idx_addr = virtio_queue_get_avail_addr(dev->vdev, i) +
- VIRTIO_VRING_AVAIL_IDX_OFFS;
- address_space_stw(&address_space_memory, idx_addr, 0,
- MEMTXATTRS_UNSPECIFIED, NULL);
- idx_addr = virtio_queue_get_avail_addr(dev->vdev, i) +
- virtio_queue_get_avail_size(dev->vdev, i);
- address_space_stw(&address_space_memory, idx_addr, 0,
- MEMTXATTRS_UNSPECIFIED, NULL);
- idx_addr = virtio_queue_get_used_addr(dev->vdev, i) +
- VIRTIO_VRING_USED_IDX_OFFS;
- address_space_stw(&address_space_memory, idx_addr, 0,
- MEMTXATTRS_UNSPECIFIED, NULL);
- idx_addr = virtio_queue_get_used_addr(dev->vdev, i) +
- virtio_queue_get_used_size(dev->vdev, i);
- address_space_stw(&address_space_memory, idx_addr, 0,
- MEMTXATTRS_UNSPECIFIED, NULL);
- }
-}
-
-VirtIOS390Bus *s390_virtio_bus_init(ram_addr_t *ram_size)
-{
- VirtIOS390Bus *bus;
- BusState *_bus;
- DeviceState *dev;
-
- /* Create bridge device */
- dev = qdev_create(NULL, "s390-virtio-bridge");
- qdev_init_nofail(dev);
-
- /* Create bus on bridge device */
-
- _bus = qbus_create(TYPE_S390_VIRTIO_BUS, dev, "s390-virtio");
- bus = DO_UPCAST(VirtIOS390Bus, bus, _bus);
-
- bus->dev_page = *ram_size;
- bus->dev_offs = bus->dev_page;
- bus->next_ring = bus->dev_page + TARGET_PAGE_SIZE;
-
- /* Enable hotplugging */
- qbus_set_hotplug_handler(_bus, dev, &error_abort);
-
- /* Allocate RAM for VirtIO device pages (descriptors, queues, rings) */
- *ram_size += S390_DEVICE_PAGES * TARGET_PAGE_SIZE;
-
- qemu_register_reset(s390_virtio_bus_reset, bus);
- return bus;
-}
-
-static void s390_virtio_device_init(VirtIOS390Device *dev,
- VirtIODevice *vdev)
-{
- VirtIOS390Bus *bus;
- int dev_len;
-
- bus = DO_UPCAST(VirtIOS390Bus, bus, dev->qdev.parent_bus);
- dev->vdev = vdev;
- dev->dev_offs = bus->dev_offs;
- dev->feat_len = sizeof(uint32_t); /* always keep 32 bits features */
-
- dev_len = VIRTIO_DEV_OFFS_CONFIG;
- dev_len += s390_virtio_device_num_vq(dev) * VIRTIO_VQCONFIG_LEN;
- dev_len += dev->feat_len * 2;
- dev_len += virtio_bus_get_vdev_config_len(&dev->bus);
-
- bus->dev_offs += dev_len;
-
- s390_virtio_device_sync(dev);
- s390_virtio_reset_idx(dev);
- if (dev->qdev.hotplugged) {
- s390_virtio_irq(VIRTIO_PARAM_DEV_ADD, dev->dev_offs);
- }
-}
-
-static void s390_virtio_net_realize(VirtIOS390Device *s390_dev, Error **errp)
-{
- DeviceState *qdev = DEVICE(s390_dev);
- VirtIONetS390 *dev = VIRTIO_NET_S390(s390_dev);
- DeviceState *vdev = DEVICE(&dev->vdev);
- Error *err = NULL;
-
- virtio_net_set_netclient_name(&dev->vdev, qdev->id,
- object_get_typename(OBJECT(qdev)));
- qdev_set_parent_bus(vdev, BUS(&s390_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", &err);
- if (err) {
- error_propagate(errp, err);
- return;
- }
-
- s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev));
-}
-
-static void s390_virtio_net_instance_init(Object *obj)
-{
- VirtIONetS390 *dev = VIRTIO_NET_S390(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VIRTIO_NET);
- object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev),
- "bootindex", &error_abort);
-}
-
-static void s390_virtio_blk_realize(VirtIOS390Device *s390_dev, Error **errp)
-{
- VirtIOBlkS390 *dev = VIRTIO_BLK_S390(s390_dev);
- DeviceState *vdev = DEVICE(&dev->vdev);
- Error *err = NULL;
-
- qdev_set_parent_bus(vdev, BUS(&s390_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", &err);
- if (err) {
- error_propagate(errp, err);
- return;
- }
- s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev));
-}
-
-static void s390_virtio_blk_instance_init(Object *obj)
-{
- VirtIOBlkS390 *dev = VIRTIO_BLK_S390(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VIRTIO_BLK);
- object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev),"iothread",
- &error_abort);
- object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev),
- "bootindex", &error_abort);
-}
-
-static void s390_virtio_serial_realize(VirtIOS390Device *s390_dev, Error **errp)
-{
- VirtIOSerialS390 *dev = VIRTIO_SERIAL_S390(s390_dev);
- DeviceState *vdev = DEVICE(&dev->vdev);
- DeviceState *qdev = DEVICE(s390_dev);
- Error *err = NULL;
- VirtIOS390Bus *bus;
- char *bus_name;
-
- bus = DO_UPCAST(VirtIOS390Bus, bus, qdev->parent_bus);
-
- /*
- * For command line compatibility, this sets the virtio-serial-device bus
- * name as before.
- */
- if (qdev->id) {
- bus_name = g_strdup_printf("%s.0", qdev->id);
- virtio_device_set_child_bus_name(VIRTIO_DEVICE(vdev), bus_name);
- g_free(bus_name);
- }
-
- qdev_set_parent_bus(vdev, BUS(&s390_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", &err);
- if (err) {
- error_propagate(errp, err);
- return;
- }
-
- s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev));
- bus->console = s390_dev;
-}
-
-static void s390_virtio_serial_instance_init(Object *obj)
-{
- VirtIOSerialS390 *dev = VIRTIO_SERIAL_S390(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VIRTIO_SERIAL);
-}
-
-static void s390_virtio_scsi_realize(VirtIOS390Device *s390_dev, Error **errp)
-{
- VirtIOSCSIS390 *dev = VIRTIO_SCSI_S390(s390_dev);
- DeviceState *vdev = DEVICE(&dev->vdev);
- DeviceState *qdev = DEVICE(s390_dev);
- Error *err = NULL;
- char *bus_name;
-
- /*
- * For command line compatibility, this sets the virtio-scsi-device bus
- * name as before.
- */
- if (qdev->id) {
- bus_name = g_strdup_printf("%s.0", qdev->id);
- virtio_device_set_child_bus_name(VIRTIO_DEVICE(vdev), bus_name);
- g_free(bus_name);
- }
-
- qdev_set_parent_bus(vdev, BUS(&s390_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", &err);
- if (err) {
- error_propagate(errp, err);
- return;
- }
-
- s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev));
-}
-
-static void s390_virtio_scsi_instance_init(Object *obj)
-{
- VirtIOSCSIS390 *dev = VIRTIO_SCSI_S390(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VIRTIO_SCSI);
-}
-
-#ifdef CONFIG_VHOST_SCSI
-static void s390_vhost_scsi_realize(VirtIOS390Device *s390_dev, Error **errp)
-{
- VHostSCSIS390 *dev = VHOST_SCSI_S390(s390_dev);
- DeviceState *vdev = DEVICE(&dev->vdev);
- Error *err = NULL;
-
- qdev_set_parent_bus(vdev, BUS(&s390_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", &err);
- if (err) {
- error_propagate(errp, err);
- return;
- }
-
- s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev));
-}
-
-static void s390_vhost_scsi_instance_init(Object *obj)
-{
- VHostSCSIS390 *dev = VHOST_SCSI_S390(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VHOST_SCSI);
-}
-#endif
-
-
-static void s390_virtio_rng_realize(VirtIOS390Device *s390_dev, Error **errp)
-{
- VirtIORNGS390 *dev = VIRTIO_RNG_S390(s390_dev);
- DeviceState *vdev = DEVICE(&dev->vdev);
- Error *err = NULL;
-
- qdev_set_parent_bus(vdev, BUS(&s390_dev->bus));
- object_property_set_bool(OBJECT(vdev), true, "realized", &err);
- if (err) {
- error_propagate(errp, err);
- return;
- }
-
- object_property_set_link(OBJECT(dev),
- OBJECT(dev->vdev.conf.rng), "rng",
- NULL);
-
- s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev));
-}
-
-static void s390_virtio_rng_instance_init(Object *obj)
-{
- VirtIORNGS390 *dev = VIRTIO_RNG_S390(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VIRTIO_RNG);
- object_property_add_alias(obj, "rng", OBJECT(&dev->vdev),
- "rng", &error_abort);
-}
-
-static uint64_t s390_virtio_device_vq_token(VirtIOS390Device *dev, int vq)
-{
- ram_addr_t token_off;
-
- token_off = (dev->dev_offs + VIRTIO_DEV_OFFS_CONFIG) +
- (vq * VIRTIO_VQCONFIG_LEN) +
- VIRTIO_VQCONFIG_OFFS_TOKEN;
-
- return address_space_ldq_be(&address_space_memory, token_off,
- MEMTXATTRS_UNSPECIFIED, NULL);
-}
-
-static ram_addr_t s390_virtio_device_num_vq(VirtIOS390Device *dev)
-{
- VirtIODevice *vdev = dev->vdev;
- int num_vq;
-
- for (num_vq = 0; num_vq < VIRTIO_S390_QUEUE_MAX; num_vq++) {
- if (!virtio_queue_get_num(vdev, num_vq)) {
- break;
- }
- }
-
- return num_vq;
-}
-
-static ram_addr_t s390_virtio_next_ring(VirtIOS390Bus *bus)
-{
- ram_addr_t r = bus->next_ring;
-
- bus->next_ring += VIRTIO_RING_LEN;
- return r;
-}
-
-void s390_virtio_device_sync(VirtIOS390Device *dev)
-{
- VirtIOS390Bus *bus = DO_UPCAST(VirtIOS390Bus, bus, dev->qdev.parent_bus);
- ram_addr_t cur_offs;
- uint8_t num_vq;
- int i;
-
- virtio_reset(dev->vdev);
-
- /* Sync dev space */
- address_space_stb(&address_space_memory,
- dev->dev_offs + VIRTIO_DEV_OFFS_TYPE,
- dev->vdev->device_id,
- MEMTXATTRS_UNSPECIFIED,
- NULL);
-
- address_space_stb(&address_space_memory,
- dev->dev_offs + VIRTIO_DEV_OFFS_NUM_VQ,
- s390_virtio_device_num_vq(dev),
- MEMTXATTRS_UNSPECIFIED,
- NULL);
- address_space_stb(&address_space_memory,
- dev->dev_offs + VIRTIO_DEV_OFFS_FEATURE_LEN,
- dev->feat_len,
- MEMTXATTRS_UNSPECIFIED,
- NULL);
-
- address_space_stb(&address_space_memory,
- dev->dev_offs + VIRTIO_DEV_OFFS_CONFIG_LEN,
- dev->vdev->config_len,
- MEMTXATTRS_UNSPECIFIED,
- NULL);
-
- num_vq = s390_virtio_device_num_vq(dev);
- address_space_stb(&address_space_memory,
- dev->dev_offs + VIRTIO_DEV_OFFS_NUM_VQ, num_vq,
- MEMTXATTRS_UNSPECIFIED, NULL);
-
- /* Sync virtqueues */
- for (i = 0; i < num_vq; i++) {
- ram_addr_t vq = (dev->dev_offs + VIRTIO_DEV_OFFS_CONFIG) +
- (i * VIRTIO_VQCONFIG_LEN);
- ram_addr_t vring;
-
- vring = s390_virtio_next_ring(bus);
- virtio_queue_set_addr(dev->vdev, i, vring);
- virtio_queue_set_vector(dev->vdev, i, i);
- address_space_stq_be(&address_space_memory,
- vq + VIRTIO_VQCONFIG_OFFS_ADDRESS, vring,
- MEMTXATTRS_UNSPECIFIED, NULL);
- address_space_stw_be(&address_space_memory,
- vq + VIRTIO_VQCONFIG_OFFS_NUM,
- virtio_queue_get_num(dev->vdev, i),
- MEMTXATTRS_UNSPECIFIED,
- NULL);
- }
-
- cur_offs = dev->dev_offs;
- cur_offs += VIRTIO_DEV_OFFS_CONFIG;
- cur_offs += num_vq * VIRTIO_VQCONFIG_LEN;
-
- /* Sync feature bitmap */
- address_space_stl_le(&address_space_memory, cur_offs,
- dev->vdev->host_features,
- MEMTXATTRS_UNSPECIFIED, NULL);
-
- dev->feat_offs = cur_offs + dev->feat_len;
- cur_offs += dev->feat_len * 2;
-
- /* Sync config space */
- virtio_bus_get_vdev_config(&dev->bus, dev->vdev->config);
-
- cpu_physical_memory_write(cur_offs,
- dev->vdev->config, dev->vdev->config_len);
- cur_offs += dev->vdev->config_len;
-}
-
-void s390_virtio_device_update_status(VirtIOS390Device *dev)
-{
- VirtIODevice *vdev = dev->vdev;
- uint32_t features;
-
- virtio_set_status(vdev,
- address_space_ldub(&address_space_memory,
- dev->dev_offs + VIRTIO_DEV_OFFS_STATUS,
- MEMTXATTRS_UNSPECIFIED, NULL));
-
- /* Update guest supported feature bitmap */
-
- features = bswap32(address_space_ldl_be(&address_space_memory,
- dev->feat_offs,
- MEMTXATTRS_UNSPECIFIED, NULL));
- virtio_set_features(vdev, features);
-}
-
-/* Find a device by vring address */
-VirtIOS390Device *s390_virtio_bus_find_vring(VirtIOS390Bus *bus,
- ram_addr_t mem,
- int *vq_num)
-{
- BusChild *kid;
- int i;
-
- QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
- VirtIOS390Device *dev = (VirtIOS390Device *)kid->child;
-
- for (i = 0; i < VIRTIO_S390_QUEUE_MAX; i++) {
- if (!virtio_queue_get_addr(dev->vdev, i))
- break;
- if (virtio_queue_get_addr(dev->vdev, i) == mem) {
- if (vq_num) {
- *vq_num = i;
- }
- return dev;
- }
- }
- }
-
- return NULL;
-}
-
-/* Find a device by device descriptor location */
-VirtIOS390Device *s390_virtio_bus_find_mem(VirtIOS390Bus *bus, ram_addr_t mem)
-{
- BusChild *kid;
-
- QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
- VirtIOS390Device *dev = (VirtIOS390Device *)kid->child;
- if (dev->dev_offs == mem) {
- return dev;
- }
- }
-
- return NULL;
-}
-
-/* DeviceState to VirtIOS390Device. Note: used on datapath,
- * be careful and test performance if you change this.
- */
-static inline VirtIOS390Device *to_virtio_s390_device_fast(DeviceState *d)
-{
- return container_of(d, VirtIOS390Device, qdev);
-}
-
-/* DeviceState to VirtIOS390Device. TODO: use QOM. */
-static inline VirtIOS390Device *to_virtio_s390_device(DeviceState *d)
-{
- return container_of(d, VirtIOS390Device, qdev);
-}
-
-static void virtio_s390_notify(DeviceState *d, uint16_t vector)
-{
- VirtIOS390Device *dev = to_virtio_s390_device_fast(d);
- uint64_t token = s390_virtio_device_vq_token(dev, vector);
-
- s390_virtio_irq(0, token);
-}
-
-static void virtio_s390_device_plugged(DeviceState *d, Error **errp)
-{
- VirtIOS390Device *dev = to_virtio_s390_device(d);
- VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
- int n = virtio_get_num_queues(vdev);
-
- if (n > VIRTIO_S390_QUEUE_MAX) {
- error_setg(errp, "The nubmer of virtqueues %d "
- "exceeds s390 limit %d", n,
- VIRTIO_S390_QUEUE_MAX);
- }
-}
-
-/**************** S390 Virtio Bus Device Descriptions *******************/
-
-static void s390_virtio_net_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass);
-
- k->realize = s390_virtio_net_realize;
- set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
-}
-
-static const TypeInfo s390_virtio_net = {
- .name = TYPE_VIRTIO_NET_S390,
- .parent = TYPE_VIRTIO_S390_DEVICE,
- .instance_size = sizeof(VirtIONetS390),
- .instance_init = s390_virtio_net_instance_init,
- .class_init = s390_virtio_net_class_init,
-};
-
-static void s390_virtio_blk_class_init(ObjectClass *klass, void *data)
-{
- VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass);
- DeviceClass *dc = DEVICE_CLASS(klass);
-
- k->realize = s390_virtio_blk_realize;
- set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
-}
-
-static const TypeInfo s390_virtio_blk = {
- .name = "virtio-blk-s390",
- .parent = TYPE_VIRTIO_S390_DEVICE,
- .instance_size = sizeof(VirtIOBlkS390),
- .instance_init = s390_virtio_blk_instance_init,
- .class_init = s390_virtio_blk_class_init,
-};
-
-static Property s390_virtio_serial_properties[] = {
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void s390_virtio_serial_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass);
-
- k->realize = s390_virtio_serial_realize;
- dc->props = s390_virtio_serial_properties;
- set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
-}
-
-static const TypeInfo s390_virtio_serial = {
- .name = TYPE_VIRTIO_SERIAL_S390,
- .parent = TYPE_VIRTIO_S390_DEVICE,
- .instance_size = sizeof(VirtIOSerialS390),
- .instance_init = s390_virtio_serial_instance_init,
- .class_init = s390_virtio_serial_class_init,
-};
-
-static void s390_virtio_rng_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass);
-
- k->realize = s390_virtio_rng_realize;
- set_bit(DEVICE_CATEGORY_MISC, dc->categories);
-}
-
-static const TypeInfo s390_virtio_rng = {
- .name = TYPE_VIRTIO_RNG_S390,
- .parent = TYPE_VIRTIO_S390_DEVICE,
- .instance_size = sizeof(VirtIORNGS390),
- .instance_init = s390_virtio_rng_instance_init,
- .class_init = s390_virtio_rng_class_init,
-};
-
-static void s390_virtio_busdev_realize(DeviceState *dev, Error **errp)
-{
- VirtIOS390Device *_dev = (VirtIOS390Device *)dev;
- VirtIOS390DeviceClass *_info = VIRTIO_S390_DEVICE_GET_CLASS(dev);
-
- virtio_s390_bus_new(&_dev->bus, sizeof(_dev->bus), _dev);
-
- _info->realize(_dev, errp);
-}
-
-static void s390_virtio_busdev_reset(DeviceState *dev)
-{
- VirtIOS390Device *_dev = (VirtIOS390Device *)dev;
-
- virtio_reset(_dev->vdev);
-}
-
-static void virtio_s390_device_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
-
- dc->realize = s390_virtio_busdev_realize;
- dc->bus_type = TYPE_S390_VIRTIO_BUS;
- dc->reset = s390_virtio_busdev_reset;
-}
-
-static const TypeInfo virtio_s390_device_info = {
- .name = TYPE_VIRTIO_S390_DEVICE,
- .parent = TYPE_DEVICE,
- .instance_size = sizeof(VirtIOS390Device),
- .class_init = virtio_s390_device_class_init,
- .class_size = sizeof(VirtIOS390DeviceClass),
- .abstract = true,
-};
-
-static void s390_virtio_scsi_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass);
-
- k->realize = s390_virtio_scsi_realize;
- set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
-}
-
-static const TypeInfo s390_virtio_scsi = {
- .name = TYPE_VIRTIO_SCSI_S390,
- .parent = TYPE_VIRTIO_S390_DEVICE,
- .instance_size = sizeof(VirtIOSCSIS390),
- .instance_init = s390_virtio_scsi_instance_init,
- .class_init = s390_virtio_scsi_class_init,
-};
-
-#ifdef CONFIG_VHOST_SCSI
-static void s390_vhost_scsi_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass);
-
- k->realize = s390_vhost_scsi_realize;
- set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
-}
-
-static const TypeInfo s390_vhost_scsi = {
- .name = TYPE_VHOST_SCSI_S390,
- .parent = TYPE_VIRTIO_S390_DEVICE,
- .instance_size = sizeof(VHostSCSIS390),
- .instance_init = s390_vhost_scsi_instance_init,
- .class_init = s390_vhost_scsi_class_init,
-};
-#endif
-
-/***************** S390 Virtio Bus Bridge Device *******************/
-/* Only required to have the virtio bus as child in the system bus */
-
-static int s390_virtio_bridge_init(SysBusDevice *dev)
-{
- /* nothing */
- return 0;
-}
-
-static void s390_virtio_bridge_class_init(ObjectClass *klass, void *data)
-{
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- DeviceClass *dc = DEVICE_CLASS(klass);
-
- k->init = s390_virtio_bridge_init;
- set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
-}
-
-static const TypeInfo s390_virtio_bridge_info = {
- .name = "s390-virtio-bridge",
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(SysBusDevice),
- .class_init = s390_virtio_bridge_class_init,
- .interfaces = (InterfaceInfo[]) {
- { TYPE_HOTPLUG_HANDLER },
- { }
- }
-};
-
-/* virtio-s390-bus */
-
-static void virtio_s390_bus_new(VirtioBusState *bus, size_t bus_size,
- VirtIOS390Device *dev)
-{
- DeviceState *qdev = DEVICE(dev);
- char virtio_bus_name[] = "virtio-bus";
-
- qbus_create_inplace(bus, bus_size, TYPE_VIRTIO_S390_BUS,
- qdev, virtio_bus_name);
-}
-
-static void virtio_s390_bus_class_init(ObjectClass *klass, void *data)
-{
- VirtioBusClass *k = VIRTIO_BUS_CLASS(klass);
- BusClass *bus_class = BUS_CLASS(klass);
- bus_class->max_dev = 1;
- k->notify = virtio_s390_notify;
- k->device_plugged = virtio_s390_device_plugged;
-}
-
-static const TypeInfo virtio_s390_bus_info = {
- .name = TYPE_VIRTIO_S390_BUS,
- .parent = TYPE_VIRTIO_BUS,
- .instance_size = sizeof(VirtioS390BusState),
- .class_init = virtio_s390_bus_class_init,
-};
-
-static void s390_virtio_register_types(void)
-{
- type_register_static(&virtio_s390_bus_info);
- type_register_static(&s390_virtio_bus_info);
- type_register_static(&virtio_s390_device_info);
- type_register_static(&s390_virtio_serial);
- type_register_static(&s390_virtio_blk);
- type_register_static(&s390_virtio_net);
- type_register_static(&s390_virtio_scsi);
-#ifdef CONFIG_VHOST_SCSI
- type_register_static(&s390_vhost_scsi);
-#endif
- type_register_static(&s390_virtio_rng);
- type_register_static(&s390_virtio_bridge_info);
-}
-
-type_init(s390_virtio_register_types)
diff --git a/qemu/hw/s390x/s390-virtio-bus.h b/qemu/hw/s390x/s390-virtio-bus.h
deleted file mode 100644
index 7ad295e68..000000000
--- a/qemu/hw/s390x/s390-virtio-bus.h
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * QEMU S390x VirtIO BUS definitions
- *
- * Copyright (c) 2009 Alexander Graf <agraf@suse.de>
- *
- * 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 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 <http://www.gnu.org/licenses/>.
- */
-#ifndef HW_S390_VIRTIO_BUS_H
-#define HW_S390_VIRTIO_BUS_H 1
-
-#include <stddef.h>
-
-#include "standard-headers/asm-s390/kvm_virtio.h"
-#include "standard-headers/linux/virtio_ring.h"
-#include "hw/virtio/virtio-blk.h"
-#include "hw/virtio/virtio-net.h"
-#include "hw/virtio/virtio-rng.h"
-#include "hw/virtio/virtio-serial.h"
-#include "hw/virtio/virtio-scsi.h"
-#include "hw/virtio/virtio-bus.h"
-#ifdef CONFIG_VHOST_SCSI
-#include "hw/virtio/vhost-scsi.h"
-#endif
-
-typedef struct kvm_device_desc KvmDeviceDesc;
-
-#define VIRTIO_DEV_OFFS_TYPE offsetof(KvmDeviceDesc, type)
-#define VIRTIO_DEV_OFFS_NUM_VQ offsetof(KvmDeviceDesc, num_vq)
-#define VIRTIO_DEV_OFFS_FEATURE_LEN offsetof(KvmDeviceDesc, feature_len)
-#define VIRTIO_DEV_OFFS_CONFIG_LEN offsetof(KvmDeviceDesc, config_len)
-#define VIRTIO_DEV_OFFS_STATUS offsetof(KvmDeviceDesc, status)
-#define VIRTIO_DEV_OFFS_CONFIG offsetof(KvmDeviceDesc, config)
-
-typedef struct kvm_vqconfig KvmVqConfig;
-#define VIRTIO_VQCONFIG_OFFS_TOKEN offsetof(KvmVqConfig,token) /* 64 bit */
-#define VIRTIO_VQCONFIG_OFFS_ADDRESS offsetof(KvmVqConfig, address) /* 64 bit */
-#define VIRTIO_VQCONFIG_OFFS_NUM offsetof(KvmVqConfig, num) /* 16 bit */
-#define VIRTIO_VQCONFIG_LEN sizeof(KvmVqConfig)
-
-#define VIRTIO_RING_LEN (TARGET_PAGE_SIZE * 3)
-#define VIRTIO_VRING_AVAIL_IDX_OFFS offsetof(struct vring_avail, idx)
-#define VIRTIO_VRING_USED_IDX_OFFS offsetof(struct vring_used, idx)
-#define S390_DEVICE_PAGES 512
-
-#define TYPE_VIRTIO_S390_DEVICE "virtio-s390-device"
-#define VIRTIO_S390_DEVICE(obj) \
- OBJECT_CHECK(VirtIOS390Device, (obj), TYPE_VIRTIO_S390_DEVICE)
-#define VIRTIO_S390_DEVICE_CLASS(klass) \
- OBJECT_CLASS_CHECK(VirtIOS390DeviceClass, (klass), TYPE_VIRTIO_S390_DEVICE)
-#define VIRTIO_S390_DEVICE_GET_CLASS(obj) \
- OBJECT_GET_CLASS(VirtIOS390DeviceClass, (obj), TYPE_VIRTIO_S390_DEVICE)
-
-#define TYPE_S390_VIRTIO_BUS "s390-virtio-bus"
-#define S390_VIRTIO_BUS(obj) \
- OBJECT_CHECK(VirtIOS390Bus, (obj), TYPE_S390_VIRTIO_BUS)
-
-/* virtio-s390-bus */
-
-typedef struct VirtioBusState VirtioS390BusState;
-typedef struct VirtioBusClass VirtioS390BusClass;
-
-#define TYPE_VIRTIO_S390_BUS "virtio-s390-bus"
-#define VIRTIO_S390_BUS(obj) \
- OBJECT_CHECK(VirtioS390BusState, (obj), TYPE_VIRTIO_S390_BUS)
-#define VIRTIO_S390_BUS_GET_CLASS(obj) \
- OBJECT_GET_CLASS(VirtioS390BusClass, obj, TYPE_VIRTIO_S390_BUS)
-#define VIRTIO_S390_BUS_CLASS(klass) \
- OBJECT_CLASS_CHECK(VirtioS390BusClass, klass, TYPE_VIRTIO_S390_BUS)
-
-
-typedef struct VirtIOS390Device VirtIOS390Device;
-
-typedef struct VirtIOS390DeviceClass {
- DeviceClass qdev;
- void (*realize)(VirtIOS390Device *dev, Error **errp);
-} VirtIOS390DeviceClass;
-
-struct VirtIOS390Device {
- DeviceState qdev;
- ram_addr_t dev_offs;
- ram_addr_t feat_offs;
- uint8_t feat_len;
- VirtIODevice *vdev;
- VirtioBusState bus;
-};
-
-typedef struct VirtIOS390Bus {
- BusState bus;
-
- VirtIOS390Device *console;
- ram_addr_t dev_page;
- ram_addr_t dev_offs;
- ram_addr_t next_ring;
-} VirtIOS390Bus;
-
-
-void s390_virtio_device_update_status(VirtIOS390Device *dev);
-
-VirtIOS390Bus *s390_virtio_bus_init(ram_addr_t *ram_size);
-
-VirtIOS390Device *s390_virtio_bus_find_vring(VirtIOS390Bus *bus,
- ram_addr_t mem, int *vq_num);
-VirtIOS390Device *s390_virtio_bus_find_mem(VirtIOS390Bus *bus, ram_addr_t mem);
-void s390_virtio_device_sync(VirtIOS390Device *dev);
-void s390_virtio_reset_idx(VirtIOS390Device *dev);
-
-/* virtio-blk-s390 */
-
-#define TYPE_VIRTIO_BLK_S390 "virtio-blk-s390"
-#define VIRTIO_BLK_S390(obj) \
- OBJECT_CHECK(VirtIOBlkS390, (obj), TYPE_VIRTIO_BLK_S390)
-
-typedef struct VirtIOBlkS390 {
- VirtIOS390Device parent_obj;
- VirtIOBlock vdev;
-} VirtIOBlkS390;
-
-/* virtio-scsi-s390 */
-
-#define TYPE_VIRTIO_SCSI_S390 "virtio-scsi-s390"
-#define VIRTIO_SCSI_S390(obj) \
- OBJECT_CHECK(VirtIOSCSIS390, (obj), TYPE_VIRTIO_SCSI_S390)
-
-typedef struct VirtIOSCSIS390 {
- VirtIOS390Device parent_obj;
- VirtIOSCSI vdev;
-} VirtIOSCSIS390;
-
-/* virtio-serial-s390 */
-
-#define TYPE_VIRTIO_SERIAL_S390 "virtio-serial-s390"
-#define VIRTIO_SERIAL_S390(obj) \
- OBJECT_CHECK(VirtIOSerialS390, (obj), TYPE_VIRTIO_SERIAL_S390)
-
-typedef struct VirtIOSerialS390 {
- VirtIOS390Device parent_obj;
- VirtIOSerial vdev;
-} VirtIOSerialS390;
-
-/* virtio-net-s390 */
-
-#define TYPE_VIRTIO_NET_S390 "virtio-net-s390"
-#define VIRTIO_NET_S390(obj) \
- OBJECT_CHECK(VirtIONetS390, (obj), TYPE_VIRTIO_NET_S390)
-
-typedef struct VirtIONetS390 {
- VirtIOS390Device parent_obj;
- VirtIONet vdev;
-} VirtIONetS390;
-
-/* vhost-scsi-s390 */
-
-#ifdef CONFIG_VHOST_SCSI
-#define TYPE_VHOST_SCSI_S390 "vhost-scsi-s390"
-#define VHOST_SCSI_S390(obj) \
- OBJECT_CHECK(VHostSCSIS390, (obj), TYPE_VHOST_SCSI_S390)
-
-typedef struct VHostSCSIS390 {
- VirtIOS390Device parent_obj;
- VHostSCSI vdev;
-} VHostSCSIS390;
-#endif
-
-/* virtio-rng-s390 */
-
-#define TYPE_VIRTIO_RNG_S390 "virtio-rng-s390"
-#define VIRTIO_RNG_S390(obj) \
- OBJECT_CHECK(VirtIORNGS390, (obj), TYPE_VIRTIO_RNG_S390)
-
-typedef struct VirtIORNGS390 {
- VirtIOS390Device parent_obj;
- VirtIORNG vdev;
-} VirtIORNGS390;
-
-#endif
diff --git a/qemu/hw/s390x/s390-virtio-ccw.c b/qemu/hw/s390x/s390-virtio-ccw.c
index 4c51d1a5b..e3df9c78b 100644
--- a/qemu/hw/s390x/s390-virtio-ccw.c
+++ b/qemu/hw/s390x/s390-virtio-ccw.c
@@ -9,6 +9,10 @@
* directory.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/boards.h"
#include "exec/address-spaces.h"
#include "s390-virtio.h"
@@ -19,41 +23,27 @@
#include "virtio-ccw.h"
#include "qemu/config-file.h"
#include "s390-pci-bus.h"
+#include "hw/s390x/storage-keys.h"
+#include "hw/compat.h"
+#include "hw/s390x/s390-virtio-ccw.h"
+
+static const char *const reset_dev_types[] = {
+ "virtual-css-bridge",
+ "s390-sclp-event-facility",
+ "s390-flic",
+ "diag288",
+};
-#define TYPE_S390_CCW_MACHINE "s390-ccw-machine"
-
-#define S390_CCW_MACHINE(obj) \
- OBJECT_CHECK(S390CcwMachineState, (obj), TYPE_S390_CCW_MACHINE)
-
-typedef struct S390CcwMachineState {
- /*< private >*/
- MachineState parent_obj;
-
- /*< public >*/
- bool aes_key_wrap;
- bool dea_key_wrap;
-} S390CcwMachineState;
-
-void io_subsystem_reset(void)
+void subsystem_reset(void)
{
- DeviceState *css, *sclp, *flic, *diag288;
+ DeviceState *dev;
+ int i;
- css = DEVICE(object_resolve_path_type("", "virtual-css-bridge", NULL));
- if (css) {
- qdev_reset_all(css);
- }
- sclp = DEVICE(object_resolve_path_type("",
- "s390-sclp-event-facility", NULL));
- if (sclp) {
- qdev_reset_all(sclp);
- }
- flic = DEVICE(object_resolve_path_type("", "s390-flic", NULL));
- if (flic) {
- qdev_reset_all(flic);
- }
- diag288 = DEVICE(object_resolve_path_type("", "diag288", NULL));
- if (diag288) {
- qdev_reset_all(diag288);
+ for (i = 0; i < ARRAY_SIZE(reset_dev_types); i++) {
+ dev = DEVICE(object_resolve_path_type("", reset_dev_types[i], NULL));
+ if (dev) {
+ qdev_reset_all(dev);
+ }
}
}
@@ -99,58 +89,30 @@ static void virtio_ccw_register_hcalls(void)
virtio_ccw_hcall_early_printk);
}
-static void ccw_init(MachineState *machine)
+void s390_memory_init(ram_addr_t mem_size)
{
- ram_addr_t my_ram_size = machine->ram_size;
MemoryRegion *sysmem = get_system_memory();
MemoryRegion *ram = g_new(MemoryRegion, 1);
- sclpMemoryHotplugDev *mhd = init_sclp_memory_hotplug_dev();
- uint8_t *storage_keys;
+
+ /* allocate RAM for core */
+ memory_region_allocate_system_memory(ram, NULL, "s390.ram", mem_size);
+ memory_region_add_subregion(sysmem, 0, ram);
+
+ /* Initialize storage key device */
+ s390_skeys_init();
+}
+
+static void ccw_init(MachineState *machine)
+{
int ret;
VirtualCssBus *css_bus;
DeviceState *dev;
- QemuOpts *opts = qemu_opts_find(qemu_find_opts("memory"), NULL);
- ram_addr_t pad_size = 0;
- ram_addr_t maxmem = qemu_opt_get_size(opts, "maxmem", my_ram_size);
- ram_addr_t standby_mem_size = maxmem - my_ram_size;
- uint64_t kvm_limit;
-
- /* The storage increment size is a multiple of 1M and is a power of 2.
- * The number of storage increments must be MAX_STORAGE_INCREMENTS or fewer.
- * The variable 'mhd->increment_size' is an exponent of 2 that can be
- * used to calculate the size (in bytes) of an increment. */
- mhd->increment_size = 20;
- while ((my_ram_size >> mhd->increment_size) > MAX_STORAGE_INCREMENTS) {
- mhd->increment_size++;
- }
- while ((standby_mem_size >> mhd->increment_size) > MAX_STORAGE_INCREMENTS) {
- mhd->increment_size++;
- }
- /* The core and standby memory areas need to be aligned with
- * the increment size. In effect, this can cause the
- * user-specified memory size to be rounded down to align
- * with the nearest increment boundary. */
- standby_mem_size = standby_mem_size >> mhd->increment_size
- << mhd->increment_size;
- my_ram_size = my_ram_size >> mhd->increment_size
- << mhd->increment_size;
-
- /* let's propagate the changed ram size into the global variable. */
- ram_size = my_ram_size;
- machine->maxram_size = my_ram_size + standby_mem_size;
-
- ret = s390_set_memory_limit(machine->maxram_size, &kvm_limit);
- if (ret == -E2BIG) {
- hw_error("qemu: host supports a maximum of %" PRIu64 " GB",
- kvm_limit >> 30);
- } else if (ret) {
- hw_error("qemu: setting the guest size failed");
- }
+ s390_sclp_init();
+ s390_memory_init(machine->ram_size);
/* get a BUS */
css_bus = virtual_css_bus_init();
- s390_sclp_init();
s390_init_ipl_dev(machine->kernel_filename, machine->kernel_cmdline,
machine->initrd_filename, "s390-ccw.img", true);
s390_flic_init();
@@ -163,27 +125,8 @@ static void ccw_init(MachineState *machine)
/* register hypercalls */
virtio_ccw_register_hcalls();
- /* allocate RAM for core */
- memory_region_init_ram(ram, NULL, "s390.ram", my_ram_size, &error_abort);
- vmstate_register_ram_global(ram);
- memory_region_add_subregion(sysmem, 0, ram);
-
- /* If the size of ram is not on a MEM_SECTION_SIZE boundary,
- calculate the pad size necessary to force this boundary. */
- if (standby_mem_size) {
- if (my_ram_size % MEM_SECTION_SIZE) {
- pad_size = MEM_SECTION_SIZE - my_ram_size % MEM_SECTION_SIZE;
- }
- my_ram_size += standby_mem_size + pad_size;
- mhd->pad_size = pad_size;
- mhd->standby_mem_size = standby_mem_size;
- }
-
- /* allocate storage keys */
- storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE);
-
/* init CPUs */
- s390_init_cpus(machine->cpu_model, storage_keys);
+ s390_init_cpus(machine);
if (kvm_enabled()) {
kvm_s390_enable_css_support(s390_cpu_addr2state(0));
@@ -203,12 +146,54 @@ static void ccw_init(MachineState *machine)
gtod_save, gtod_load, kvm_state);
}
+static void s390_cpu_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ gchar *name;
+ S390CPU *cpu = S390_CPU(dev);
+ CPUState *cs = CPU(dev);
+
+ name = g_strdup_printf("cpu[%i]", cpu->env.cpu_num);
+ object_property_set_link(OBJECT(hotplug_dev), OBJECT(cs), name,
+ errp);
+ g_free(name);
+}
+
+static void s390_machine_device_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
+ s390_cpu_plug(hotplug_dev, dev, errp);
+ }
+}
+
+static HotplugHandler *s390_get_hotplug_handler(MachineState *machine,
+ DeviceState *dev)
+{
+ if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
+ return HOTPLUG_HANDLER(machine);
+ }
+ return NULL;
+}
+
+static void s390_hot_add_cpu(const int64_t id, Error **errp)
+{
+ MachineState *machine = MACHINE(qdev_get_machine());
+ Error *err = NULL;
+
+ s390x_new_cpu(machine->cpu_model, id, &err);
+ error_propagate(errp, err);
+}
+
static void ccw_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
NMIClass *nc = NMI_CLASS(oc);
+ HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
mc->init = ccw_init;
+ mc->reset = s390_machine_reset;
+ mc->hot_add_cpu = s390_hot_add_cpu;
mc->block_default_type = IF_VIRTIO;
mc->no_cdrom = 1;
mc->no_floppy = 1;
@@ -217,6 +202,8 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data)
mc->no_sdcard = 1;
mc->use_sclp = 1;
mc->max_cpus = 255;
+ mc->get_hotplug_handler = s390_get_hotplug_handler;
+ hc->plug = s390_machine_device_plug;
nc->nmi_monitor_handler = s390_nmi;
}
@@ -278,30 +265,117 @@ static const TypeInfo ccw_machine_info = {
.class_init = ccw_machine_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_NMI },
+ { TYPE_HOTPLUG_HANDLER},
{ }
},
};
-static void ccw_machine_2_4_class_init(ObjectClass *oc, void *data)
+#define DEFINE_CCW_MACHINE(suffix, verstr, latest) \
+ static void ccw_machine_##suffix##_class_init(ObjectClass *oc, \
+ void *data) \
+ { \
+ MachineClass *mc = MACHINE_CLASS(oc); \
+ ccw_machine_##suffix##_class_options(mc); \
+ mc->desc = "VirtIO-ccw based S390 machine v" verstr; \
+ if (latest) { \
+ mc->alias = "s390-ccw-virtio"; \
+ mc->is_default = 1; \
+ } \
+ } \
+ static void ccw_machine_##suffix##_instance_init(Object *obj) \
+ { \
+ MachineState *machine = MACHINE(obj); \
+ ccw_machine_##suffix##_instance_options(machine); \
+ } \
+ static const TypeInfo ccw_machine_##suffix##_info = { \
+ .name = MACHINE_TYPE_NAME("s390-ccw-virtio-" verstr), \
+ .parent = TYPE_S390_CCW_MACHINE, \
+ .class_init = ccw_machine_##suffix##_class_init, \
+ .instance_init = ccw_machine_##suffix##_instance_init, \
+ }; \
+ static void ccw_machine_register_##suffix(void) \
+ { \
+ type_register_static(&ccw_machine_##suffix##_info); \
+ } \
+ type_init(ccw_machine_register_##suffix)
+
+#define CCW_COMPAT_2_5 \
+ HW_COMPAT_2_5
+
+#define CCW_COMPAT_2_4 \
+ CCW_COMPAT_2_5 \
+ HW_COMPAT_2_4 \
+ {\
+ .driver = TYPE_S390_SKEYS,\
+ .property = "migration-enabled",\
+ .value = "off",\
+ },{\
+ .driver = "virtio-blk-ccw",\
+ .property = "max_revision",\
+ .value = "0",\
+ },{\
+ .driver = "virtio-balloon-ccw",\
+ .property = "max_revision",\
+ .value = "0",\
+ },{\
+ .driver = "virtio-serial-ccw",\
+ .property = "max_revision",\
+ .value = "0",\
+ },{\
+ .driver = "virtio-9p-ccw",\
+ .property = "max_revision",\
+ .value = "0",\
+ },{\
+ .driver = "virtio-rng-ccw",\
+ .property = "max_revision",\
+ .value = "0",\
+ },{\
+ .driver = "virtio-net-ccw",\
+ .property = "max_revision",\
+ .value = "0",\
+ },{\
+ .driver = "virtio-scsi-ccw",\
+ .property = "max_revision",\
+ .value = "0",\
+ },{\
+ .driver = "vhost-scsi-ccw",\
+ .property = "max_revision",\
+ .value = "0",\
+ },
+
+static void ccw_machine_2_6_instance_options(MachineState *machine)
{
- MachineClass *mc = MACHINE_CLASS(oc);
+}
- mc->name = "s390-ccw-virtio-2.4";
- mc->alias = "s390-ccw-virtio";
- mc->desc = "VirtIO-ccw based S390 machine v2.4";
- mc->is_default = 1;
+static void ccw_machine_2_6_class_options(MachineClass *mc)
+{
}
+DEFINE_CCW_MACHINE(2_6, "2.6", true);
-static const TypeInfo ccw_machine_2_4_info = {
- .name = TYPE_S390_CCW_MACHINE "2.4",
- .parent = TYPE_S390_CCW_MACHINE,
- .class_init = ccw_machine_2_4_class_init,
-};
+static void ccw_machine_2_5_instance_options(MachineState *machine)
+{
+}
+
+static void ccw_machine_2_5_class_options(MachineClass *mc)
+{
+ SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_5);
+}
+DEFINE_CCW_MACHINE(2_5, "2.5", false);
+
+static void ccw_machine_2_4_instance_options(MachineState *machine)
+{
+ ccw_machine_2_5_instance_options(machine);
+}
+
+static void ccw_machine_2_4_class_options(MachineClass *mc)
+{
+ SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_4);
+}
+DEFINE_CCW_MACHINE(2_4, "2.4", false);
static void ccw_machine_register_types(void)
{
type_register_static(&ccw_machine_info);
- type_register_static(&ccw_machine_2_4_info);
}
type_init(ccw_machine_register_types)
diff --git a/qemu/hw/s390x/s390-virtio-hcall.c b/qemu/hw/s390x/s390-virtio-hcall.c
index c7bdc2005..23d67d617 100644
--- a/qemu/hw/s390x/s390-virtio-hcall.c
+++ b/qemu/hw/s390x/s390-virtio-hcall.c
@@ -9,6 +9,7 @@
* directory.
*/
+#include "qemu/osdep.h"
#include "cpu.h"
#include "hw/s390x/s390-virtio.h"
diff --git a/qemu/hw/s390x/s390-virtio.c b/qemu/hw/s390x/s390-virtio.c
index 1284e77b2..544c61643 100644
--- a/qemu/hw/s390x/s390-virtio.c
+++ b/qemu/hw/s390x/s390-virtio.c
@@ -21,8 +21,11 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "qapi/qmp/qerror.h"
+#include "qemu/error-report.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
#include "sysemu/sysemu.h"
@@ -30,14 +33,15 @@
#include "hw/boards.h"
#include "hw/loader.h"
#include "hw/virtio/virtio.h"
-#include "hw/sysbus.h"
#include "sysemu/kvm.h"
#include "exec/address-spaces.h"
+#include "sysemu/qtest.h"
-#include "hw/s390x/s390-virtio-bus.h"
#include "hw/s390x/sclp.h"
#include "hw/s390x/s390_flic.h"
#include "hw/s390x/s390-virtio.h"
+#include "hw/s390x/storage-keys.h"
+#include "hw/s390x/ipl.h"
#include "cpu.h"
//#define DEBUG_S390
@@ -51,94 +55,20 @@
#endif
#define MAX_BLK_DEVS 10
-#define ZIPL_FILENAME "s390-zipl.rom"
-#define TYPE_S390_MACHINE "s390-machine"
#define S390_TOD_CLOCK_VALUE_MISSING 0x00
#define S390_TOD_CLOCK_VALUE_PRESENT 0x01
-static VirtIOS390Bus *s390_bus;
-static S390CPU **ipi_states;
+static S390CPU **cpu_states;
S390CPU *s390_cpu_addr2state(uint16_t cpu_addr)
{
- if (cpu_addr >= smp_cpus) {
+ if (cpu_addr >= max_cpus) {
return NULL;
}
- return ipi_states[cpu_addr];
-}
-
-static int s390_virtio_hcall_notify(const uint64_t *args)
-{
- uint64_t mem = args[0];
- int r = 0, i;
-
- if (mem > ram_size) {
- VirtIOS390Device *dev = s390_virtio_bus_find_vring(s390_bus, mem, &i);
- if (dev) {
- /*
- * Older kernels will use the virtqueue before setting DRIVER_OK.
- * In this case the feature bits are not yet up to date, meaning
- * that several funny things can happen, e.g. the guest thinks
- * EVENT_IDX is on and QEMU thinks it is off. Let's force a feature
- * and status sync.
- */
- if (!(dev->vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
- s390_virtio_device_update_status(dev);
- }
- virtio_queue_notify(dev->vdev, i);
- } else {
- r = -EINVAL;
- }
- } else {
- /* Early printk */
- }
- return r;
-}
-
-static int s390_virtio_hcall_reset(const uint64_t *args)
-{
- uint64_t mem = args[0];
- VirtIOS390Device *dev;
-
- dev = s390_virtio_bus_find_mem(s390_bus, mem);
- if (dev == NULL) {
- return -EINVAL;
- }
- virtio_reset(dev->vdev);
- address_space_stb(&address_space_memory,
- dev->dev_offs + VIRTIO_DEV_OFFS_STATUS, 0,
- MEMTXATTRS_UNSPECIFIED, NULL);
- s390_virtio_device_sync(dev);
- s390_virtio_reset_idx(dev);
-
- return 0;
-}
-
-static int s390_virtio_hcall_set_status(const uint64_t *args)
-{
- uint64_t mem = args[0];
- int r = 0;
- VirtIOS390Device *dev;
-
- dev = s390_virtio_bus_find_mem(s390_bus, mem);
- if (dev) {
- s390_virtio_device_update_status(dev);
- } else {
- r = -EINVAL;
- }
- return r;
-}
-
-static void s390_virtio_register_hcalls(void)
-{
- s390_register_virtio_hypercall(KVM_S390_VIRTIO_NOTIFY,
- s390_virtio_hcall_notify);
- s390_register_virtio_hypercall(KVM_S390_VIRTIO_RESET,
- s390_virtio_hcall_reset);
- s390_register_virtio_hypercall(KVM_S390_VIRTIO_SET_STATUS,
- s390_virtio_hcall_set_status);
+ /* Fast lookup via CPU ID */
+ return cpu_states[cpu_addr];
}
void s390_init_ipl_dev(const char *kernel_filename,
@@ -147,9 +77,9 @@ void s390_init_ipl_dev(const char *kernel_filename,
const char *firmware,
bool enforce_bios)
{
- DeviceState *dev;
+ Object *new = object_new(TYPE_S390_IPL);
+ DeviceState *dev = DEVICE(new);
- dev = qdev_create(NULL, "s390-ipl");
if (kernel_filename) {
qdev_prop_set_string(dev, "kernel", kernel_filename);
}
@@ -159,32 +89,35 @@ void s390_init_ipl_dev(const char *kernel_filename,
qdev_prop_set_string(dev, "cmdline", kernel_cmdline);
qdev_prop_set_string(dev, "firmware", firmware);
qdev_prop_set_bit(dev, "enforce_bios", enforce_bios);
- object_property_add_child(qdev_get_machine(), "s390-ipl",
- OBJECT(dev), NULL);
+ object_property_add_child(qdev_get_machine(), TYPE_S390_IPL,
+ new, NULL);
+ object_unref(new);
qdev_init_nofail(dev);
}
-void s390_init_cpus(const char *cpu_model, uint8_t *storage_keys)
+void s390_init_cpus(MachineState *machine)
{
int i;
+ gchar *name;
- if (cpu_model == NULL) {
- cpu_model = "host";
+ if (machine->cpu_model == NULL) {
+ machine->cpu_model = "host";
}
- ipi_states = g_malloc(sizeof(S390CPU *) * smp_cpus);
-
- for (i = 0; i < smp_cpus; i++) {
- S390CPU *cpu;
- CPUState *cs;
+ cpu_states = g_new0(S390CPU *, max_cpus);
- cpu = cpu_s390x_init(cpu_model);
- cs = CPU(cpu);
+ for (i = 0; i < max_cpus; i++) {
+ name = g_strdup_printf("cpu[%i]", i);
+ object_property_add_link(OBJECT(machine), name, TYPE_S390_CPU,
+ (Object **) &cpu_states[i],
+ object_property_allow_set_link,
+ OBJ_PROP_LINK_UNREF_ON_RELEASE,
+ &error_abort);
+ g_free(name);
+ }
- ipi_states[i] = cpu;
- cs->halted = 1;
- cs->exception_index = EXCP_HLT;
- cpu->env.storage_keys = storage_keys;
+ for (i = 0; i < smp_cpus; i++) {
+ s390x_new_cpu(machine->cpu_model, i, &error_fatal);
}
}
@@ -201,10 +134,7 @@ void s390_create_virtio_net(BusState *bus, const char *name)
nd->model = g_strdup("virtio");
}
- if (strcmp(nd->model, "virtio")) {
- fprintf(stderr, "S390 only supports VirtIO nics\n");
- exit(1);
- }
+ qemu_check_nic_model(nd, "virtio");
dev = qdev_create(bus, name);
qdev_set_nic_properties(dev, nd);
@@ -257,68 +187,6 @@ int gtod_load(QEMUFile *f, void *opaque, int version_id)
return 0;
}
-/* PC hardware initialisation */
-static void s390_init(MachineState *machine)
-{
- ram_addr_t my_ram_size = machine->ram_size;
- MemoryRegion *sysmem = get_system_memory();
- MemoryRegion *ram = g_new(MemoryRegion, 1);
- int increment_size = 20;
- uint8_t *storage_keys;
- void *virtio_region;
- hwaddr virtio_region_len;
- hwaddr virtio_region_start;
-
- /*
- * The storage increment size is a multiple of 1M and is a power of 2.
- * The number of storage increments must be MAX_STORAGE_INCREMENTS or
- * fewer.
- */
- while ((my_ram_size >> increment_size) > MAX_STORAGE_INCREMENTS) {
- increment_size++;
- }
- my_ram_size = my_ram_size >> increment_size << increment_size;
-
- /* let's propagate the changed ram size into the global variable. */
- ram_size = my_ram_size;
-
- /* get a BUS */
- s390_bus = s390_virtio_bus_init(&my_ram_size);
- s390_sclp_init();
- s390_init_ipl_dev(machine->kernel_filename, machine->kernel_cmdline,
- machine->initrd_filename, ZIPL_FILENAME, false);
- s390_flic_init();
-
- /* register hypercalls */
- s390_virtio_register_hcalls();
-
- /* allocate RAM */
- memory_region_init_ram(ram, NULL, "s390.ram", my_ram_size, &error_abort);
- vmstate_register_ram_global(ram);
- memory_region_add_subregion(sysmem, 0, ram);
-
- /* clear virtio region */
- virtio_region_len = my_ram_size - ram_size;
- virtio_region_start = ram_size;
- virtio_region = cpu_physical_memory_map(virtio_region_start,
- &virtio_region_len, true);
- memset(virtio_region, 0, virtio_region_len);
- cpu_physical_memory_unmap(virtio_region, virtio_region_len, 1,
- virtio_region_len);
-
- /* allocate storage keys */
- storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE);
-
- /* init CPUs */
- s390_init_cpus(machine->cpu_model, storage_keys);
-
- /* Create VirtIO network adapters */
- s390_create_virtio_net((BusState *)s390_bus, "virtio-net-s390");
-
- /* Register savevm handler for guest TOD clock */
- register_savevm(NULL, "todclock", 0, 1, gtod_save, gtod_load, NULL);
-}
-
void s390_nmi(NMIState *n, int cpu_index, Error **errp)
{
CPUState *cs = qemu_get_cpu(cpu_index);
@@ -328,39 +196,15 @@ void s390_nmi(NMIState *n, int cpu_index, Error **errp)
}
}
-static void s390_machine_class_init(ObjectClass *oc, void *data)
+void s390_machine_reset(void)
{
- MachineClass *mc = MACHINE_CLASS(oc);
- NMIClass *nc = NMI_CLASS(oc);
-
- mc->name = "s390-virtio";
- mc->alias = "s390";
- mc->desc = "VirtIO based S390 machine";
- mc->init = s390_init;
- mc->block_default_type = IF_VIRTIO;
- mc->max_cpus = 255;
- mc->no_serial = 1;
- mc->no_parallel = 1;
- mc->use_virtcon = 1;
- mc->no_floppy = 1;
- mc->no_cdrom = 1;
- mc->no_sdcard = 1;
- nc->nmi_monitor_handler = s390_nmi;
-}
+ S390CPU *ipl_cpu = S390_CPU(qemu_get_cpu(0));
-static const TypeInfo s390_machine_info = {
- .name = TYPE_S390_MACHINE,
- .parent = TYPE_MACHINE,
- .class_init = s390_machine_class_init,
- .interfaces = (InterfaceInfo[]) {
- { TYPE_NMI },
- { }
- },
-};
-
-static void s390_machine_register_types(void)
-{
- type_register_static(&s390_machine_info);
-}
+ qemu_devices_reset();
+ s390_cmma_reset();
+ s390_crypto_reset();
-type_init(s390_machine_register_types)
+ /* all cpus are stopped - configure and start the ipl cpu only */
+ s390_ipl_prepare_cpu(ipl_cpu);
+ s390_cpu_set_state(CPU_STATE_OPERATING, ipl_cpu);
+}
diff --git a/qemu/hw/s390x/s390-virtio.h b/qemu/hw/s390x/s390-virtio.h
index c84785395..ffd014cb5 100644
--- a/qemu/hw/s390x/s390-virtio.h
+++ b/qemu/hw/s390x/s390-virtio.h
@@ -19,7 +19,7 @@
typedef int (*s390_virtio_fn)(const uint64_t *args);
void s390_register_virtio_hypercall(uint64_t code, s390_virtio_fn fn);
-void s390_init_cpus(const char *cpu_model, uint8_t *storage_keys);
+void s390_init_cpus(MachineState *machine);
void s390_init_ipl_dev(const char *kernel_filename,
const char *kernel_cmdline,
const char *initrd_filename,
@@ -27,4 +27,6 @@ void s390_init_ipl_dev(const char *kernel_filename,
bool enforce_bios);
void s390_create_virtio_net(BusState *bus, const char *name);
void s390_nmi(NMIState *n, int cpu_index, Error **errp);
+void s390_machine_reset(void);
+void s390_memory_init(ram_addr_t mem_size);
#endif
diff --git a/qemu/hw/s390x/sclp.c b/qemu/hw/s390x/sclp.c
index b3a6c5e5a..85dbe1b60 100644
--- a/qemu/hw/s390x/sclp.c
+++ b/qemu/hw/s390x/sclp.c
@@ -12,42 +12,34 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "cpu.h"
#include "sysemu/kvm.h"
#include "exec/memory.h"
#include "sysemu/sysemu.h"
#include "exec/address-spaces.h"
-#include "qemu/config-file.h"
+#include "hw/boards.h"
#include "hw/s390x/sclp.h"
#include "hw/s390x/event-facility.h"
#include "hw/s390x/s390-pci-bus.h"
-static inline SCLPEventFacility *get_event_facility(void)
+static inline SCLPDevice *get_sclp_device(void)
{
- ObjectProperty *op = object_property_find(qdev_get_machine(),
- TYPE_SCLP_EVENT_FACILITY,
- NULL);
- assert(op);
- return op->opaque;
+ return SCLP(object_resolve_path_type("", TYPE_SCLP, NULL));
}
/* Provide information about the configuration, CPUs and storage */
-static void read_SCP_info(SCCB *sccb)
+static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb)
{
ReadInfo *read_info = (ReadInfo *) sccb;
+ MachineState *machine = MACHINE(qdev_get_machine());
sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
CPUState *cpu;
int cpu_count = 0;
int i = 0;
- int increment_size = 20;
int rnsize, rnmax;
- QemuOpts *opts = qemu_opts_find(qemu_find_opts("memory"), NULL);
- int slots = qemu_opt_get_number(opts, "slots", 0);
- int max_avail_slots = s390_get_memslot_count(kvm_state);
-
- if (slots > max_avail_slots) {
- slots = max_avail_slots;
- }
+ int slots = MIN(machine->ram_slots, s390_get_memslot_count(kvm_state));
CPU_FOREACH(cpu) {
cpu_count++;
@@ -66,23 +58,8 @@ static void read_SCP_info(SCCB *sccb)
read_info->facilities = cpu_to_be64(SCLP_HAS_CPU_INFO |
SCLP_HAS_PCI_RECONFIG);
- /*
- * The storage increment size is a multiple of 1M and is a power of 2.
- * The number of storage increments must be MAX_STORAGE_INCREMENTS or fewer.
- */
- while ((ram_size >> increment_size) > MAX_STORAGE_INCREMENTS) {
- increment_size++;
- }
- rnmax = ram_size >> increment_size;
-
/* Memory Hotplug is only supported for the ccw machine type */
if (mhd) {
- while ((mhd->standby_mem_size >> increment_size) >
- MAX_STORAGE_INCREMENTS) {
- increment_size++;
- }
- assert(increment_size == mhd->increment_size);
-
mhd->standby_subregion_size = MEM_SECTION_SIZE;
/* Deduct the memory slot already used for core */
if (slots > 0) {
@@ -108,13 +85,11 @@ static void read_SCP_info(SCCB *sccb)
}
mhd->padded_ram_size = ram_size + mhd->pad_size;
mhd->rzm = 1 << mhd->increment_size;
- rnmax = ((ram_size + mhd->standby_mem_size + mhd->pad_size)
- >> mhd->increment_size);
read_info->facilities |= cpu_to_be64(SCLP_FC_ASSIGN_ATTACH_READ_STOR);
}
- rnsize = 1 << (increment_size - 20);
+ rnsize = 1 << (sclp->increment_size - 20);
if (rnsize <= 128) {
read_info->rnsize = rnsize;
} else {
@@ -122,6 +97,7 @@ static void read_SCP_info(SCCB *sccb)
read_info->rnsize2 = cpu_to_be32(rnsize);
}
+ rnmax = machine->maxram_size >> sclp->increment_size;
if (rnmax < 0x10000) {
read_info->rnmax = cpu_to_be16(rnmax);
} else {
@@ -132,14 +108,17 @@ static void read_SCP_info(SCCB *sccb)
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION);
}
-static void read_storage_element0_info(SCCB *sccb)
+static void read_storage_element0_info(SCLPDevice *sclp, SCCB *sccb)
{
int i, assigned;
int subincrement_id = SCLP_STARTING_SUBINCREMENT_ID;
ReadStorageElementInfo *storage_info = (ReadStorageElementInfo *) sccb;
sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
- assert(mhd);
+ if (!mhd) {
+ sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
+ return;
+ }
if ((ram_size >> mhd->increment_size) >= 0x10000) {
sccb->h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION);
@@ -158,12 +137,15 @@ static void read_storage_element0_info(SCCB *sccb)
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION);
}
-static void read_storage_element1_info(SCCB *sccb)
+static void read_storage_element1_info(SCLPDevice *sclp, SCCB *sccb)
{
ReadStorageElementInfo *storage_info = (ReadStorageElementInfo *) sccb;
sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
- assert(mhd);
+ if (!mhd) {
+ sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
+ return;
+ }
if ((mhd->standby_mem_size >> mhd->increment_size) >= 0x10000) {
sccb->h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION);
@@ -179,13 +161,17 @@ static void read_storage_element1_info(SCCB *sccb)
sccb->h.response_code = cpu_to_be16(SCLP_RC_STANDBY_READ_COMPLETION);
}
-static void attach_storage_element(SCCB *sccb, uint16_t element)
+static void attach_storage_element(SCLPDevice *sclp, SCCB *sccb,
+ uint16_t element)
{
int i, assigned, subincrement_id;
AttachStorageElement *attach_info = (AttachStorageElement *) sccb;
sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
- assert(mhd);
+ if (!mhd) {
+ sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
+ return;
+ }
if (element != 1) {
sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
@@ -203,20 +189,26 @@ static void attach_storage_element(SCCB *sccb, uint16_t element)
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION);
}
-static void assign_storage(SCCB *sccb)
+static void assign_storage(SCLPDevice *sclp, SCCB *sccb)
{
MemoryRegion *mr = NULL;
uint64_t this_subregion_size;
AssignStorage *assign_info = (AssignStorage *) sccb;
sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
- assert(mhd);
- ram_addr_t assign_addr = (assign_info->rn - 1) * mhd->rzm;
+ ram_addr_t assign_addr;
MemoryRegion *sysmem = get_system_memory();
+ if (!mhd) {
+ sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
+ return;
+ }
+ assign_addr = (assign_info->rn - 1) * mhd->rzm;
+
if ((assign_addr % MEM_SECTION_SIZE == 0) &&
(assign_addr >= mhd->padded_ram_size)) {
/* Re-use existing memory region if found */
mr = memory_region_find(sysmem, assign_addr, 1).mr;
+ memory_region_unref(mr);
if (!mr) {
MemoryRegion *standby_ram = g_new(MemoryRegion, 1);
@@ -241,7 +233,13 @@ static void assign_storage(SCCB *sccb)
this_subregion_size = mhd->standby_subregion_size;
}
- memory_region_init_ram(standby_ram, NULL, id, this_subregion_size, &error_abort);
+ memory_region_init_ram(standby_ram, NULL, id, this_subregion_size,
+ &error_fatal);
+ /* This is a hack to make memory hotunplug work again. Once we have
+ * subdevices, we have to unparent them when unassigning memory,
+ * instead of doing it via the ref count of the MemoryRegion. */
+ object_ref(OBJECT(standby_ram));
+ object_unparent(OBJECT(standby_ram));
vmstate_register_ram_global(standby_ram);
memory_region_add_subregion(sysmem, offset, standby_ram);
}
@@ -252,15 +250,20 @@ static void assign_storage(SCCB *sccb)
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION);
}
-static void unassign_storage(SCCB *sccb)
+static void unassign_storage(SCLPDevice *sclp, SCCB *sccb)
{
MemoryRegion *mr = NULL;
AssignStorage *assign_info = (AssignStorage *) sccb;
sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
- assert(mhd);
- ram_addr_t unassign_addr = (assign_info->rn - 1) * mhd->rzm;
+ ram_addr_t unassign_addr;
MemoryRegion *sysmem = get_system_memory();
+ if (!mhd) {
+ sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
+ return;
+ }
+ unassign_addr = (assign_info->rn - 1) * mhd->rzm;
+
/* if the addr is a multiple of 256 MB */
if ((unassign_addr % MEM_SECTION_SIZE == 0) &&
(unassign_addr >= mhd->padded_ram_size)) {
@@ -269,6 +272,7 @@ static void unassign_storage(SCCB *sccb)
/* find the specified memory region and destroy it */
mr = memory_region_find(sysmem, unassign_addr, 1).mr;
+ memory_region_unref(mr);
if (mr) {
int i;
int is_removable = 1;
@@ -287,8 +291,7 @@ static void unassign_storage(SCCB *sccb)
}
if (is_removable) {
memory_region_del_subregion(sysmem, mr);
- object_unparent(OBJECT(mr));
- g_free(mr);
+ object_unref(OBJECT(mr));
}
}
}
@@ -296,7 +299,7 @@ static void unassign_storage(SCCB *sccb)
}
/* Provide information about the CPU */
-static void sclp_read_cpu_info(SCCB *sccb)
+static void sclp_read_cpu_info(SCLPDevice *sclp, SCCB *sccb)
{
ReadCpuInfo *cpu_info = (ReadCpuInfo *) sccb;
CPUState *cpu;
@@ -323,34 +326,35 @@ static void sclp_read_cpu_info(SCCB *sccb)
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION);
}
-static void sclp_execute(SCCB *sccb, uint32_t code)
+static void sclp_execute(SCLPDevice *sclp, SCCB *sccb, uint32_t code)
{
- SCLPEventFacility *ef = get_event_facility();
+ SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp);
+ SCLPEventFacility *ef = sclp->event_facility;
SCLPEventFacilityClass *efc = EVENT_FACILITY_GET_CLASS(ef);
switch (code & SCLP_CMD_CODE_MASK) {
case SCLP_CMDW_READ_SCP_INFO:
case SCLP_CMDW_READ_SCP_INFO_FORCED:
- read_SCP_info(sccb);
+ sclp_c->read_SCP_info(sclp, sccb);
break;
case SCLP_CMDW_READ_CPU_INFO:
- sclp_read_cpu_info(sccb);
+ sclp_c->read_cpu_info(sclp, sccb);
break;
case SCLP_READ_STORAGE_ELEMENT_INFO:
if (code & 0xff00) {
- read_storage_element1_info(sccb);
+ sclp_c->read_storage_element1_info(sclp, sccb);
} else {
- read_storage_element0_info(sccb);
+ sclp_c->read_storage_element0_info(sclp, sccb);
}
break;
case SCLP_ATTACH_STORAGE_ELEMENT:
- attach_storage_element(sccb, (code & 0xff00) >> 8);
+ sclp_c->attach_storage_element(sclp, sccb, (code & 0xff00) >> 8);
break;
case SCLP_ASSIGN_STORAGE:
- assign_storage(sccb);
+ sclp_c->assign_storage(sclp, sccb);
break;
case SCLP_UNASSIGN_STORAGE:
- unassign_storage(sccb);
+ sclp_c->unassign_storage(sclp, sccb);
break;
case SCLP_CMDW_CONFIGURE_PCI:
s390_pci_sclp_configure(1, sccb);
@@ -366,6 +370,8 @@ static void sclp_execute(SCCB *sccb, uint32_t code)
int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code)
{
+ SCLPDevice *sclp = get_sclp_device();
+ SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp);
int r = 0;
SCCB work_sccb;
@@ -400,20 +406,20 @@ int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code)
goto out;
}
- sclp_execute((SCCB *)&work_sccb, code);
+ sclp_c->execute(sclp, (SCCB *)&work_sccb, code);
cpu_physical_memory_write(sccb, &work_sccb,
be16_to_cpu(work_sccb.h.length));
- sclp_service_interrupt(sccb);
+ sclp_c->service_interrupt(sclp, sccb);
out:
return r;
}
-void sclp_service_interrupt(uint32_t sccb)
+static void service_interrupt(SCLPDevice *sclp, uint32_t sccb)
{
- SCLPEventFacility *ef = get_event_facility();
+ SCLPEventFacility *ef = sclp->event_facility;
SCLPEventFacilityClass *efc = EVENT_FACILITY_GET_CLASS(ef);
uint32_t param = sccb & ~3;
@@ -428,17 +434,149 @@ void sclp_service_interrupt(uint32_t sccb)
s390_sclp_extint(param);
}
+void sclp_service_interrupt(uint32_t sccb)
+{
+ SCLPDevice *sclp = get_sclp_device();
+ SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp);
+
+ sclp_c->service_interrupt(sclp, sccb);
+}
+
/* qemu object creation and initialization functions */
void s390_sclp_init(void)
{
- DeviceState *dev = qdev_create(NULL, TYPE_SCLP_EVENT_FACILITY);
+ Object *new = object_new(TYPE_SCLP);
- object_property_add_child(qdev_get_machine(), TYPE_SCLP_EVENT_FACILITY,
- OBJECT(dev), NULL);
- qdev_init_nofail(dev);
+ object_property_add_child(qdev_get_machine(), TYPE_SCLP, new,
+ NULL);
+ object_unref(OBJECT(new));
+ qdev_init_nofail(DEVICE(new));
}
+static void sclp_realize(DeviceState *dev, Error **errp)
+{
+ MachineState *machine = MACHINE(qdev_get_machine());
+ SCLPDevice *sclp = SCLP(dev);
+ Error *err = NULL;
+ uint64_t hw_limit;
+ int ret;
+
+ object_property_set_bool(OBJECT(sclp->event_facility), true, "realized",
+ &err);
+ if (err) {
+ goto out;
+ }
+ /*
+ * qdev_device_add searches the sysbus for TYPE_SCLP_EVENTS_BUS. As long
+ * as we can't find a fitting bus via the qom tree, we have to add the
+ * event facility to the sysbus, so e.g. a sclp console can be created.
+ */
+ qdev_set_parent_bus(DEVICE(sclp->event_facility), sysbus_get_default());
+
+ ret = s390_set_memory_limit(machine->maxram_size, &hw_limit);
+ if (ret == -E2BIG) {
+ error_setg(&err, "qemu: host supports a maximum of %" PRIu64 " GB",
+ hw_limit >> 30);
+ } else if (ret) {
+ error_setg(&err, "qemu: setting the guest size failed");
+ }
+
+out:
+ error_propagate(errp, err);
+}
+
+static void sclp_memory_init(SCLPDevice *sclp)
+{
+ MachineState *machine = MACHINE(qdev_get_machine());
+ ram_addr_t initial_mem = machine->ram_size;
+ ram_addr_t max_mem = machine->maxram_size;
+ ram_addr_t standby_mem = max_mem - initial_mem;
+ ram_addr_t pad_mem = 0;
+ int increment_size = 20;
+
+ /* The storage increment size is a multiple of 1M and is a power of 2.
+ * The number of storage increments must be MAX_STORAGE_INCREMENTS or fewer.
+ * The variable 'increment_size' is an exponent of 2 that can be
+ * used to calculate the size (in bytes) of an increment. */
+ while ((initial_mem >> increment_size) > MAX_STORAGE_INCREMENTS) {
+ increment_size++;
+ }
+ if (machine->ram_slots) {
+ while ((standby_mem >> increment_size) > MAX_STORAGE_INCREMENTS) {
+ increment_size++;
+ }
+ }
+ sclp->increment_size = increment_size;
+
+ /* The core and standby memory areas need to be aligned with
+ * the increment size. In effect, this can cause the
+ * user-specified memory size to be rounded down to align
+ * with the nearest increment boundary. */
+ initial_mem = initial_mem >> increment_size << increment_size;
+ standby_mem = standby_mem >> increment_size << increment_size;
+
+ /* If the size of ram is not on a MEM_SECTION_SIZE boundary,
+ calculate the pad size necessary to force this boundary. */
+ if (machine->ram_slots && standby_mem) {
+ sclpMemoryHotplugDev *mhd = init_sclp_memory_hotplug_dev();
+
+ if (initial_mem % MEM_SECTION_SIZE) {
+ pad_mem = MEM_SECTION_SIZE - initial_mem % MEM_SECTION_SIZE;
+ }
+ mhd->increment_size = increment_size;
+ mhd->pad_size = pad_mem;
+ mhd->standby_mem_size = standby_mem;
+ }
+ machine->ram_size = initial_mem;
+ machine->maxram_size = initial_mem + pad_mem + standby_mem;
+ /* let's propagate the changed ram size into the global variable. */
+ ram_size = initial_mem;
+}
+
+static void sclp_init(Object *obj)
+{
+ SCLPDevice *sclp = SCLP(obj);
+ Object *new;
+
+ new = object_new(TYPE_SCLP_EVENT_FACILITY);
+ object_property_add_child(obj, TYPE_SCLP_EVENT_FACILITY, new, NULL);
+ object_unref(new);
+ sclp->event_facility = EVENT_FACILITY(new);
+
+ sclp_memory_init(sclp);
+}
+
+static void sclp_class_init(ObjectClass *oc, void *data)
+{
+ SCLPDeviceClass *sc = SCLP_CLASS(oc);
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->desc = "SCLP (Service-Call Logical Processor)";
+ dc->realize = sclp_realize;
+ dc->hotpluggable = false;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+
+ sc->read_SCP_info = read_SCP_info;
+ sc->read_storage_element0_info = read_storage_element0_info;
+ sc->read_storage_element1_info = read_storage_element1_info;
+ sc->attach_storage_element = attach_storage_element;
+ sc->assign_storage = assign_storage;
+ sc->unassign_storage = unassign_storage;
+ sc->read_cpu_info = sclp_read_cpu_info;
+ sc->execute = sclp_execute;
+ sc->service_interrupt = service_interrupt;
+}
+
+static TypeInfo sclp_info = {
+ .name = TYPE_SCLP,
+ .parent = TYPE_DEVICE,
+ .instance_init = sclp_init,
+ .instance_size = sizeof(SCLPDevice),
+ .class_init = sclp_class_init,
+ .class_size = sizeof(SCLPDeviceClass),
+};
+
sclpMemoryHotplugDev *init_sclp_memory_hotplug_dev(void)
{
DeviceState *dev;
@@ -475,5 +613,6 @@ static TypeInfo sclp_memory_hotplug_dev_info = {
static void register_types(void)
{
type_register_static(&sclp_memory_hotplug_dev_info);
+ type_register_static(&sclp_info);
}
type_init(register_types);
diff --git a/qemu/hw/s390x/sclpcpu.c b/qemu/hw/s390x/sclpcpu.c
index 2fe8b5aa4..b1f3ef8c7 100644
--- a/qemu/hw/s390x/sclpcpu.c
+++ b/qemu/hw/s390x/sclpcpu.c
@@ -12,6 +12,7 @@
* option) any later version. See the COPYING file in the top-level directory.
*
*/
+#include "qemu/osdep.h"
#include "sysemu/sysemu.h"
#include "hw/s390x/sclp.h"
#include "hw/s390x/event-facility.h"
@@ -25,13 +26,16 @@ typedef struct ConfigMgtData {
uint8_t event_qualifier;
} QEMU_PACKED ConfigMgtData;
-static qemu_irq *irq_cpu_hotplug; /* Only used in this file */
-
#define EVENT_QUAL_CPU_CHANGE 1
void raise_irq_cpu_hotplug(void)
{
- qemu_irq_raise(*irq_cpu_hotplug);
+ Object *obj = object_resolve_path_type("", TYPE_SCLP_CPU_HOTPLUG, NULL);
+
+ SCLP_EVENT(obj)->event_pending = true;
+
+ /* Trigger SCLP read operation */
+ sclp_service_interrupt(0);
}
static unsigned int send_mask(void)
@@ -70,36 +74,19 @@ static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr,
return 1;
}
-static void trigger_signal(void *opaque, int n, int level)
-{
- SCLPEvent *event = opaque;
- event->event_pending = true;
-
- /* Trigger SCLP read operation */
- sclp_service_interrupt(0);
-}
-
-static int irq_cpu_hotplug_init(SCLPEvent *event)
-{
- irq_cpu_hotplug = qemu_allocate_irqs(trigger_signal, event, 1);
- return 0;
-}
-
static void cpu_class_init(ObjectClass *oc, void *data)
{
SCLPEventClass *k = SCLP_EVENT_CLASS(oc);
DeviceClass *dc = DEVICE_CLASS(oc);
- k->init = irq_cpu_hotplug_init;
k->get_send_mask = send_mask;
k->get_receive_mask = receive_mask;
k->read_event_data = read_event_data;
- k->write_event_data = NULL;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
}
static const TypeInfo sclp_cpu_info = {
- .name = "sclp-cpu-hotplug",
+ .name = TYPE_SCLP_CPU_HOTPLUG,
.parent = TYPE_SCLP_EVENT,
.instance_size = sizeof(SCLPEvent),
.class_init = cpu_class_init,
diff --git a/qemu/hw/s390x/sclpquiesce.c b/qemu/hw/s390x/sclpquiesce.c
index ffa555313..c0ecab9c3 100644
--- a/qemu/hw/s390x/sclpquiesce.c
+++ b/qemu/hw/s390x/sclpquiesce.c
@@ -11,6 +11,7 @@
* option) any later version. See the COPYING file in the top-level directory.
*
*/
+#include "qemu/osdep.h"
#include <hw/qdev.h>
#include "sysemu/sysemu.h"
#include "hw/s390x/sclp.h"
@@ -66,7 +67,7 @@ static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr,
}
static const VMStateDescription vmstate_sclpquiesce = {
- .name = "sclpquiesce",
+ .name = TYPE_SCLP_QUIESCE,
.version_id = 0,
.minimum_version_id = 0,
.fields = (VMStateField[]) {
@@ -127,7 +128,7 @@ static void quiesce_class_init(ObjectClass *klass, void *data)
}
static const TypeInfo sclp_quiesce_info = {
- .name = "sclpquiesce",
+ .name = TYPE_SCLP_QUIESCE,
.parent = TYPE_SCLP_EVENT,
.instance_size = sizeof(SCLPEvent),
.class_init = quiesce_class_init,
diff --git a/qemu/hw/s390x/virtio-ccw.c b/qemu/hw/s390x/virtio-ccw.c
index d36373e88..d51642db0 100644
--- a/qemu/hw/s390x/virtio-ccw.c
+++ b/qemu/hw/s390x/virtio-ccw.c
@@ -10,6 +10,8 @@
* directory.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
@@ -31,69 +33,6 @@
#include "virtio-ccw.h"
#include "trace.h"
-static QTAILQ_HEAD(, IndAddr) indicator_addresses =
- QTAILQ_HEAD_INITIALIZER(indicator_addresses);
-
-static IndAddr *get_indicator(hwaddr ind_addr, int len)
-{
- IndAddr *indicator;
-
- QTAILQ_FOREACH(indicator, &indicator_addresses, sibling) {
- if (indicator->addr == ind_addr) {
- indicator->refcnt++;
- return indicator;
- }
- }
- indicator = g_new0(IndAddr, 1);
- indicator->addr = ind_addr;
- indicator->len = len;
- indicator->refcnt = 1;
- QTAILQ_INSERT_TAIL(&indicator_addresses, indicator, sibling);
- return indicator;
-}
-
-static int s390_io_adapter_map(AdapterInfo *adapter, uint64_t map_addr,
- bool do_map)
-{
- S390FLICState *fs = s390_get_flic();
- S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
-
- return fsc->io_adapter_map(fs, adapter->adapter_id, map_addr, do_map);
-}
-
-static void release_indicator(AdapterInfo *adapter, IndAddr *indicator)
-{
- assert(indicator->refcnt > 0);
- indicator->refcnt--;
- if (indicator->refcnt > 0) {
- return;
- }
- QTAILQ_REMOVE(&indicator_addresses, indicator, sibling);
- if (indicator->map) {
- s390_io_adapter_map(adapter, indicator->map, false);
- }
- g_free(indicator);
-}
-
-static int map_indicator(AdapterInfo *adapter, IndAddr *indicator)
-{
- int ret;
-
- if (indicator->map) {
- return 0; /* already mapped is not an error */
- }
- indicator->map = indicator->addr;
- ret = s390_io_adapter_map(adapter, indicator->map, true);
- if ((ret != 0) && (ret != -ENOSYS)) {
- goto out_err;
- }
- return 0;
-
-out_err:
- indicator->map = 0;
- return ret;
-}
-
static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size,
VirtioCcwDevice *dev);
@@ -307,11 +246,18 @@ static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info,
if (!desc) {
virtio_queue_set_vector(vdev, index, VIRTIO_NO_VECTOR);
} else {
- /* Fail if we don't have a big enough queue. */
- /* TODO: Add interface to handle vring.num changing */
- if (virtio_queue_get_num(vdev, index) > num) {
+ if (info) {
+ /* virtio-1 allows changing the ring size. */
+ if (virtio_queue_get_num(vdev, index) < num) {
+ /* Fail if we exceed the maximum number. */
+ return -EINVAL;
+ }
+ virtio_queue_set_num(vdev, index, num);
+ } else if (virtio_queue_get_num(vdev, index) > num) {
+ /* Fail if we don't have a big enough queue. */
return -EINVAL;
}
+ /* We ignore possible increased num for legacy for compatibility. */
virtio_queue_set_vector(vdev, index, index);
}
/* tell notify handler in case of config change */
@@ -460,16 +406,19 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
MEMTXATTRS_UNSPECIFIED,
NULL);
if (features.index == 0) {
- features.features = (uint32_t)vdev->host_features;
- } else if (features.index == 1) {
- features.features = (uint32_t)(vdev->host_features >> 32);
+ if (dev->revision >= 1) {
+ /* Don't offer legacy features for modern devices. */
+ features.features = (uint32_t)
+ (vdev->host_features & ~VIRTIO_LEGACY_FEATURES);
+ } else {
+ features.features = (uint32_t)vdev->host_features;
+ }
+ } else if ((features.index == 1) && (dev->revision >= 1)) {
/*
- * Don't offer version 1 to the guest if it did not
- * negotiate at least revision 1.
+ * Only offer feature bits beyond 31 if the guest has
+ * negotiated at least revision 1.
*/
- if (dev->revision <= 0) {
- features.features &= ~(1 << (VIRTIO_F_VERSION_1 - 32));
- }
+ features.features = (uint32_t)(vdev->host_features >> 32);
} else {
/* Return zeroes if the guest supports more feature bits. */
features.features = 0;
@@ -508,14 +457,12 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
virtio_set_features(vdev,
(vdev->guest_features & 0xffffffff00000000ULL) |
features.features);
- } else if (features.index == 1) {
+ } else if ((features.index == 1) && (dev->revision >= 1)) {
/*
- * The guest should not set version 1 if it didn't
- * negotiate a revision >= 1.
+ * If the guest did not negotiate at least revision 1,
+ * we did not offer it any feature bits beyond 31. Such a
+ * guest passing us any bit here is therefore buggy.
*/
- if (dev->revision <= 0) {
- features.features &= ~(1 << (VIRTIO_F_VERSION_1 - 32));
- }
virtio_set_features(vdev,
(vdev->guest_features & 0x00000000ffffffffULL) |
((uint64_t)features.features << 32));
@@ -766,7 +713,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
* need to fetch it here. Nothing to do for now, though.
*/
if (dev->revision >= 0 ||
- revinfo.revision > virtio_ccw_rev_max(vdev)) {
+ revinfo.revision > virtio_ccw_rev_max(dev)) {
ret = -ENOSYS;
break;
}
@@ -1169,7 +1116,8 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector)
SubchDev *sch = dev->sch;
uint64_t indicators;
- if (vector >= 128) {
+ /* queue indicators + secondary indicators */
+ if (vector >= VIRTIO_CCW_QUEUE_MAX + 64) {
return;
}
@@ -1539,10 +1487,25 @@ static void virtio_ccw_device_plugged(DeviceState *d, Error **errp)
sch->id.cu_model = virtio_bus_get_vdev_id(&dev->bus);
+ if (dev->max_rev >= 1) {
+ virtio_add_feature(&vdev->host_features, VIRTIO_F_VERSION_1);
+ }
+
css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid,
d->hotplugged, 1);
}
+static void virtio_ccw_post_plugged(DeviceState *d, Error **errp)
+{
+ VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
+ VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
+
+ if (!virtio_host_has_feature(vdev, VIRTIO_F_VERSION_1)) {
+ /* A backend didn't support modern virtio. */
+ dev->max_rev = 0;
+ }
+}
+
static void virtio_ccw_device_unplugged(DeviceState *d)
{
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
@@ -1555,6 +1518,8 @@ static Property virtio_ccw_net_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
+ VIRTIO_CCW_MAX_REV),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1582,6 +1547,8 @@ static Property virtio_ccw_blk_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
+ VIRTIO_CCW_MAX_REV),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1609,6 +1576,8 @@ static Property virtio_ccw_serial_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
+ VIRTIO_CCW_MAX_REV),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1636,6 +1605,8 @@ static Property virtio_ccw_balloon_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
+ VIRTIO_CCW_MAX_REV),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1663,6 +1634,8 @@ static Property virtio_ccw_scsi_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
+ VIRTIO_CCW_MAX_REV),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1689,6 +1662,8 @@ static const TypeInfo virtio_ccw_scsi = {
#ifdef CONFIG_VHOST_SCSI
static Property vhost_ccw_scsi_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
+ DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
+ VIRTIO_CCW_MAX_REV),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1727,6 +1702,8 @@ static Property virtio_ccw_rng_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
+ VIRTIO_CCW_MAX_REV),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1865,6 +1842,7 @@ static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data)
k->save_config = virtio_ccw_save_config;
k->load_config = virtio_ccw_load_config;
k->device_plugged = virtio_ccw_device_plugged;
+ k->post_plugged = virtio_ccw_post_plugged;
k->device_unplugged = virtio_ccw_device_unplugged;
}
@@ -1880,6 +1858,8 @@ static Property virtio_ccw_9p_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
+ VIRTIO_CCW_MAX_REV),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/qemu/hw/s390x/virtio-ccw.h b/qemu/hw/s390x/virtio-ccw.h
index 692ddd731..66c831ba8 100644
--- a/qemu/hw/s390x/virtio-ccw.h
+++ b/qemu/hw/s390x/virtio-ccw.h
@@ -23,7 +23,8 @@
#include <hw/virtio/virtio-balloon.h>
#include <hw/virtio/virtio-rng.h>
#include <hw/virtio/virtio-bus.h>
-#include <hw/s390x/s390_flic.h>
+
+#include "css.h"
#define VIRTUAL_CSSID 0xfe
@@ -75,19 +76,12 @@ typedef struct VirtIOCCWDeviceClass {
#define VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT 1
#define VIRTIO_CCW_FLAG_USE_IOEVENTFD (1 << VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT)
-typedef struct IndAddr {
- hwaddr addr;
- uint64_t map;
- unsigned long refcnt;
- int len;
- QTAILQ_ENTRY(IndAddr) sibling;
-} IndAddr;
-
struct VirtioCcwDevice {
DeviceState parent_obj;
SubchDev *sch;
char *bus_id;
int revision;
+ uint32_t max_rev;
VirtioBusState bus;
bool ioeventfd_started;
bool ioeventfd_disabled;
@@ -102,9 +96,10 @@ struct VirtioCcwDevice {
};
/* The maximum virtio revision we support. */
-static inline int virtio_ccw_rev_max(VirtIODevice *vdev)
+#define VIRTIO_CCW_MAX_REV 1
+static inline int virtio_ccw_rev_max(VirtioCcwDevice *dev)
{
- return 0;
+ return dev->max_rev;
}
/* virtual css bus type */
@@ -208,7 +203,7 @@ VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch);
typedef struct V9fsCCWState {
VirtioCcwDevice parent_obj;
- V9fsState vdev;
+ V9fsVirtioState vdev;
} V9fsCCWState;
#endif /* CONFIG_VIRTFS */
diff --git a/qemu/hw/scsi/Makefile.objs b/qemu/hw/scsi/Makefile.objs
index 40c79d34c..5a2248be3 100644
--- a/qemu/hw/scsi/Makefile.objs
+++ b/qemu/hw/scsi/Makefile.objs
@@ -1,6 +1,7 @@
common-obj-y += scsi-disk.o
common-obj-y += scsi-generic.o scsi-bus.o
common-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o
+common-obj-$(CONFIG_MPTSAS_SCSI_PCI) += mptsas.o mptconfig.o mptendian.o
common-obj-$(CONFIG_MEGASAS_SCSI_PCI) += megasas.o
common-obj-$(CONFIG_VMW_PVSCSI_SCSI_PCI) += vmw_pvscsi.o
common-obj-$(CONFIG_ESP) += esp.o
diff --git a/qemu/hw/scsi/esp-pci.c b/qemu/hw/scsi/esp-pci.c
index 8d2242d0a..595f88b35 100644
--- a/qemu/hw/scsi/esp-pci.c
+++ b/qemu/hw/scsi/esp-pci.c
@@ -23,10 +23,12 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/pci/pci.h"
#include "hw/nvram/eeprom93xx.h"
#include "hw/scsi/esp.h"
#include "trace.h"
+#include "qapi/error.h"
#include "qemu/log.h"
#define TYPE_AM53C974_DEVICE "am53c974"
diff --git a/qemu/hw/scsi/esp.c b/qemu/hw/scsi/esp.c
index 272d13d63..8961be2f3 100644
--- a/qemu/hw/scsi/esp.c
+++ b/qemu/hw/scsi/esp.c
@@ -23,9 +23,11 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/scsi/esp.h"
#include "trace.h"
+#include "qapi/error.h"
#include "qemu/log.h"
/*
diff --git a/qemu/hw/scsi/lsi53c895a.c b/qemu/hw/scsi/lsi53c895a.c
index c5b0cc5ca..df205cdaf 100644
--- a/qemu/hw/scsi/lsi53c895a.c
+++ b/qemu/hw/scsi/lsi53c895a.c
@@ -13,7 +13,7 @@
* as well-behaved operating systems will not try to use them.
*/
-#include <assert.h>
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
diff --git a/qemu/hw/scsi/megasas.c b/qemu/hw/scsi/megasas.c
index a04369c5a..a63a58155 100644
--- a/qemu/hw/scsi/megasas.c
+++ b/qemu/hw/scsi/megasas.c
@@ -18,6 +18,7 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "sysemu/dma.h"
@@ -431,7 +432,7 @@ static uint64_t megasas_fw_time(void)
static uint64_t megasas_get_sata_addr(uint16_t id)
{
uint64_t addr = (0x1221ULL << 48);
- return addr & (id << 24);
+ return addr | ((uint64_t)id << 24);
}
/*
@@ -718,7 +719,7 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd)
BusChild *kid;
int num_pd_disks = 0;
- memset(&info, 0x0, cmd->iov_size);
+ memset(&info, 0x0, dcmd_size);
if (cmd->iov_size < dcmd_size) {
trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size,
dcmd_size);
@@ -744,7 +745,7 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd)
info.device.type = MFI_INFO_DEV_SAS3G;
info.device.port_count = 8;
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
- SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
+ SCSIDevice *sdev = SCSI_DEVICE(kid->child);
uint16_t pd_id;
if (num_pd_disks < 8) {
@@ -757,7 +758,7 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd)
memcpy(info.product_name, base_class->product_name, 24);
snprintf(info.serial_number, 32, "%s", s->hba_serial);
- snprintf(info.package_version, 0x60, "%s-QEMU", QEMU_VERSION);
+ snprintf(info.package_version, 0x60, "%s-QEMU", qemu_hw_version());
memcpy(info.image_component[0].name, "APP", 3);
snprintf(info.image_component[0].version, 10, "%s-QEMU",
base_class->product_version);
@@ -960,7 +961,7 @@ static int megasas_dcmd_pd_get_list(MegasasState *s, MegasasCmd *cmd)
max_pd_disks = MFI_MAX_SYS_PDS;
}
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
- SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
+ SCSIDevice *sdev = SCSI_DEVICE(kid->child);
uint16_t pd_id;
if (num_pd_disks >= max_pd_disks)
@@ -1136,7 +1137,7 @@ static int megasas_dcmd_ld_get_list(MegasasState *s, MegasasCmd *cmd)
max_ld_disks = MFI_MAX_LD;
}
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
- SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
+ SCSIDevice *sdev = SCSI_DEVICE(kid->child);
if (num_ld_disks >= max_ld_disks) {
break;
@@ -1187,7 +1188,7 @@ static int megasas_dcmd_ld_list_query(MegasasState *s, MegasasCmd *cmd)
max_ld_disks = MFI_MAX_LD;
}
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
- SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
+ SCSIDevice *sdev = SCSI_DEVICE(kid->child);
if (num_ld_disks >= max_ld_disks) {
break;
@@ -1327,7 +1328,7 @@ static int megasas_dcmd_cfg_read(MegasasState *s, MegasasCmd *cmd)
ld_offset = array_offset + sizeof(struct mfi_array) * num_pd_disks;
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
- SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
+ SCSIDevice *sdev = SCSI_DEVICE(kid->child);
uint16_t sdev_id = ((sdev->id & 0xFF) << 8) | (sdev->lun & 0xFF);
struct mfi_array *array;
struct mfi_ld_config *ld;
@@ -2237,7 +2238,7 @@ static void megasas_soft_reset(MegasasState *s)
* after the initial reset.
*/
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
- SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
+ SCSIDevice *sdev = SCSI_DEVICE(kid->child);
sdev->unit_attention = SENSE_CODE(NO_SENSE);
scsi_device_unit_attention_reported(sdev);
diff --git a/qemu/hw/scsi/mpi.h b/qemu/hw/scsi/mpi.h
new file mode 100644
index 000000000..0568e1950
--- /dev/null
+++ b/qemu/hw/scsi/mpi.h
@@ -0,0 +1,1153 @@
+/*-
+ * Based on FreeBSD sys/dev/mpt/mpilib headers.
+ *
+ * Copyright (c) 2000-2010, LSI Logic Corporation and its contributors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon including
+ * a substantially similar Disclaimer requirement for further binary
+ * redistribution.
+ * 3. Neither the name of the LSI Logic Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF THE COPYRIGHT
+ * OWNER OR CONTRIBUTOR IS ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef MPI_H
+#define MPI_H
+
+enum {
+ MPI_FUNCTION_SCSI_IO_REQUEST = 0x00,
+ MPI_FUNCTION_SCSI_TASK_MGMT = 0x01,
+ MPI_FUNCTION_IOC_INIT = 0x02,
+ MPI_FUNCTION_IOC_FACTS = 0x03,
+ MPI_FUNCTION_CONFIG = 0x04,
+ MPI_FUNCTION_PORT_FACTS = 0x05,
+ MPI_FUNCTION_PORT_ENABLE = 0x06,
+ MPI_FUNCTION_EVENT_NOTIFICATION = 0x07,
+ MPI_FUNCTION_EVENT_ACK = 0x08,
+ MPI_FUNCTION_FW_DOWNLOAD = 0x09,
+ MPI_FUNCTION_TARGET_CMD_BUFFER_POST = 0x0A,
+ MPI_FUNCTION_TARGET_ASSIST = 0x0B,
+ MPI_FUNCTION_TARGET_STATUS_SEND = 0x0C,
+ MPI_FUNCTION_TARGET_MODE_ABORT = 0x0D,
+ MPI_FUNCTION_FC_LINK_SRVC_BUF_POST = 0x0E,
+ MPI_FUNCTION_FC_LINK_SRVC_RSP = 0x0F,
+ MPI_FUNCTION_FC_EX_LINK_SRVC_SEND = 0x10,
+ MPI_FUNCTION_FC_ABORT = 0x11,
+ MPI_FUNCTION_FW_UPLOAD = 0x12,
+ MPI_FUNCTION_FC_COMMON_TRANSPORT_SEND = 0x13,
+ MPI_FUNCTION_FC_PRIMITIVE_SEND = 0x14,
+
+ MPI_FUNCTION_RAID_ACTION = 0x15,
+ MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH = 0x16,
+
+ MPI_FUNCTION_TOOLBOX = 0x17,
+
+ MPI_FUNCTION_SCSI_ENCLOSURE_PROCESSOR = 0x18,
+
+ MPI_FUNCTION_MAILBOX = 0x19,
+
+ MPI_FUNCTION_SMP_PASSTHROUGH = 0x1A,
+ MPI_FUNCTION_SAS_IO_UNIT_CONTROL = 0x1B,
+ MPI_FUNCTION_SATA_PASSTHROUGH = 0x1C,
+
+ MPI_FUNCTION_DIAG_BUFFER_POST = 0x1D,
+ MPI_FUNCTION_DIAG_RELEASE = 0x1E,
+
+ MPI_FUNCTION_SCSI_IO_32 = 0x1F,
+
+ MPI_FUNCTION_LAN_SEND = 0x20,
+ MPI_FUNCTION_LAN_RECEIVE = 0x21,
+ MPI_FUNCTION_LAN_RESET = 0x22,
+
+ MPI_FUNCTION_TARGET_ASSIST_EXTENDED = 0x23,
+ MPI_FUNCTION_TARGET_CMD_BUF_BASE_POST = 0x24,
+ MPI_FUNCTION_TARGET_CMD_BUF_LIST_POST = 0x25,
+
+ MPI_FUNCTION_INBAND_BUFFER_POST = 0x28,
+ MPI_FUNCTION_INBAND_SEND = 0x29,
+ MPI_FUNCTION_INBAND_RSP = 0x2A,
+ MPI_FUNCTION_INBAND_ABORT = 0x2B,
+
+ MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET = 0x40,
+ MPI_FUNCTION_IO_UNIT_RESET = 0x41,
+ MPI_FUNCTION_HANDSHAKE = 0x42,
+ MPI_FUNCTION_REPLY_FRAME_REMOVAL = 0x43,
+ MPI_FUNCTION_HOST_PAGEBUF_ACCESS_CONTROL = 0x44,
+};
+
+/****************************************************************************/
+/* Registers */
+/****************************************************************************/
+
+enum {
+ MPI_IOC_STATE_RESET = 0x00000000,
+ MPI_IOC_STATE_READY = 0x10000000,
+ MPI_IOC_STATE_OPERATIONAL = 0x20000000,
+ MPI_IOC_STATE_FAULT = 0x40000000,
+
+ MPI_DOORBELL_OFFSET = 0x00000000,
+ MPI_DOORBELL_ACTIVE = 0x08000000, /* DoorbellUsed */
+ MPI_DOORBELL_WHO_INIT_MASK = 0x07000000,
+ MPI_DOORBELL_WHO_INIT_SHIFT = 24,
+ MPI_DOORBELL_FUNCTION_MASK = 0xFF000000,
+ MPI_DOORBELL_FUNCTION_SHIFT = 24,
+ MPI_DOORBELL_ADD_DWORDS_MASK = 0x00FF0000,
+ MPI_DOORBELL_ADD_DWORDS_SHIFT = 16,
+ MPI_DOORBELL_DATA_MASK = 0x0000FFFF,
+ MPI_DOORBELL_FUNCTION_SPECIFIC_MASK = 0x0000FFFF,
+
+ MPI_DB_HPBAC_VALUE_MASK = 0x0000F000,
+ MPI_DB_HPBAC_ENABLE_ACCESS = 0x01,
+ MPI_DB_HPBAC_DISABLE_ACCESS = 0x02,
+ MPI_DB_HPBAC_FREE_BUFFER = 0x03,
+
+ MPI_WRITE_SEQUENCE_OFFSET = 0x00000004,
+ MPI_WRSEQ_KEY_VALUE_MASK = 0x0000000F,
+ MPI_WRSEQ_1ST_KEY_VALUE = 0x04,
+ MPI_WRSEQ_2ND_KEY_VALUE = 0x0B,
+ MPI_WRSEQ_3RD_KEY_VALUE = 0x02,
+ MPI_WRSEQ_4TH_KEY_VALUE = 0x07,
+ MPI_WRSEQ_5TH_KEY_VALUE = 0x0D,
+
+ MPI_DIAGNOSTIC_OFFSET = 0x00000008,
+ MPI_DIAG_CLEAR_FLASH_BAD_SIG = 0x00000400,
+ MPI_DIAG_PREVENT_IOC_BOOT = 0x00000200,
+ MPI_DIAG_DRWE = 0x00000080,
+ MPI_DIAG_FLASH_BAD_SIG = 0x00000040,
+ MPI_DIAG_RESET_HISTORY = 0x00000020,
+ MPI_DIAG_RW_ENABLE = 0x00000010,
+ MPI_DIAG_RESET_ADAPTER = 0x00000004,
+ MPI_DIAG_DISABLE_ARM = 0x00000002,
+ MPI_DIAG_MEM_ENABLE = 0x00000001,
+
+ MPI_TEST_BASE_ADDRESS_OFFSET = 0x0000000C,
+
+ MPI_DIAG_RW_DATA_OFFSET = 0x00000010,
+
+ MPI_DIAG_RW_ADDRESS_OFFSET = 0x00000014,
+
+ MPI_HOST_INTERRUPT_STATUS_OFFSET = 0x00000030,
+ MPI_HIS_IOP_DOORBELL_STATUS = 0x80000000,
+ MPI_HIS_REPLY_MESSAGE_INTERRUPT = 0x00000008,
+ MPI_HIS_DOORBELL_INTERRUPT = 0x00000001,
+
+ MPI_HOST_INTERRUPT_MASK_OFFSET = 0x00000034,
+ MPI_HIM_RIM = 0x00000008,
+ MPI_HIM_DIM = 0x00000001,
+
+ MPI_REQUEST_QUEUE_OFFSET = 0x00000040,
+ MPI_REQUEST_POST_FIFO_OFFSET = 0x00000040,
+
+ MPI_REPLY_QUEUE_OFFSET = 0x00000044,
+ MPI_REPLY_POST_FIFO_OFFSET = 0x00000044,
+ MPI_REPLY_FREE_FIFO_OFFSET = 0x00000044,
+
+ MPI_HI_PRI_REQUEST_QUEUE_OFFSET = 0x00000048,
+};
+
+#define MPI_ADDRESS_REPLY_A_BIT 0x80000000
+
+/****************************************************************************/
+/* Scatter/gather elements */
+/****************************************************************************/
+
+typedef struct MPISGEntry {
+ uint32_t FlagsLength;
+ union
+ {
+ uint32_t Address32;
+ uint64_t Address64;
+ } u;
+} QEMU_PACKED MPISGEntry;
+
+/* Flags field bit definitions */
+
+enum {
+ MPI_SGE_FLAGS_LAST_ELEMENT = 0x80000000,
+ MPI_SGE_FLAGS_END_OF_BUFFER = 0x40000000,
+ MPI_SGE_FLAGS_ELEMENT_TYPE_MASK = 0x30000000,
+ MPI_SGE_FLAGS_LOCAL_ADDRESS = 0x08000000,
+ MPI_SGE_FLAGS_DIRECTION = 0x04000000,
+ MPI_SGE_FLAGS_64_BIT_ADDRESSING = 0x02000000,
+ MPI_SGE_FLAGS_END_OF_LIST = 0x01000000,
+
+ MPI_SGE_LENGTH_MASK = 0x00FFFFFF,
+ MPI_SGE_CHAIN_LENGTH_MASK = 0x0000FFFF,
+
+ MPI_SGE_FLAGS_TRANSACTION_ELEMENT = 0x00000000,
+ MPI_SGE_FLAGS_SIMPLE_ELEMENT = 0x10000000,
+ MPI_SGE_FLAGS_CHAIN_ELEMENT = 0x30000000,
+
+ /* Direction */
+
+ MPI_SGE_FLAGS_IOC_TO_HOST = 0x00000000,
+ MPI_SGE_FLAGS_HOST_TO_IOC = 0x04000000,
+
+ MPI_SGE_CHAIN_OFFSET_MASK = 0x00FF0000,
+};
+
+#define MPI_SGE_CHAIN_OFFSET_SHIFT 16
+
+/****************************************************************************/
+/* Standard message request header for all request messages */
+/****************************************************************************/
+
+typedef struct MPIRequestHeader {
+ uint8_t Reserved[2]; /* function specific */
+ uint8_t ChainOffset;
+ uint8_t Function;
+ uint8_t Reserved1[3]; /* function specific */
+ uint8_t MsgFlags;
+ uint32_t MsgContext;
+} QEMU_PACKED MPIRequestHeader;
+
+
+typedef struct MPIDefaultReply {
+ uint8_t Reserved[2]; /* function specific */
+ uint8_t MsgLength;
+ uint8_t Function;
+ uint8_t Reserved1[3]; /* function specific */
+ uint8_t MsgFlags;
+ uint32_t MsgContext;
+ uint8_t Reserved2[2]; /* function specific */
+ uint16_t IOCStatus;
+ uint32_t IOCLogInfo;
+} QEMU_PACKED MPIDefaultReply;
+
+/* MsgFlags definition for all replies */
+
+#define MPI_MSGFLAGS_CONTINUATION_REPLY (0x80)
+
+enum {
+
+ /************************************************************************/
+ /* Common IOCStatus values for all replies */
+ /************************************************************************/
+
+ MPI_IOCSTATUS_SUCCESS = 0x0000,
+ MPI_IOCSTATUS_INVALID_FUNCTION = 0x0001,
+ MPI_IOCSTATUS_BUSY = 0x0002,
+ MPI_IOCSTATUS_INVALID_SGL = 0x0003,
+ MPI_IOCSTATUS_INTERNAL_ERROR = 0x0004,
+ MPI_IOCSTATUS_RESERVED = 0x0005,
+ MPI_IOCSTATUS_INSUFFICIENT_RESOURCES = 0x0006,
+ MPI_IOCSTATUS_INVALID_FIELD = 0x0007,
+ MPI_IOCSTATUS_INVALID_STATE = 0x0008,
+ MPI_IOCSTATUS_OP_STATE_NOT_SUPPORTED = 0x0009,
+
+ /************************************************************************/
+ /* Config IOCStatus values */
+ /************************************************************************/
+
+ MPI_IOCSTATUS_CONFIG_INVALID_ACTION = 0x0020,
+ MPI_IOCSTATUS_CONFIG_INVALID_TYPE = 0x0021,
+ MPI_IOCSTATUS_CONFIG_INVALID_PAGE = 0x0022,
+ MPI_IOCSTATUS_CONFIG_INVALID_DATA = 0x0023,
+ MPI_IOCSTATUS_CONFIG_NO_DEFAULTS = 0x0024,
+ MPI_IOCSTATUS_CONFIG_CANT_COMMIT = 0x0025,
+
+ /************************************************************************/
+ /* SCSIIO Reply = SPI & FCP, initiator values */
+ /************************************************************************/
+
+ MPI_IOCSTATUS_SCSI_RECOVERED_ERROR = 0x0040,
+ MPI_IOCSTATUS_SCSI_INVALID_BUS = 0x0041,
+ MPI_IOCSTATUS_SCSI_INVALID_TARGETID = 0x0042,
+ MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE = 0x0043,
+ MPI_IOCSTATUS_SCSI_DATA_OVERRUN = 0x0044,
+ MPI_IOCSTATUS_SCSI_DATA_UNDERRUN = 0x0045,
+ MPI_IOCSTATUS_SCSI_IO_DATA_ERROR = 0x0046,
+ MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR = 0x0047,
+ MPI_IOCSTATUS_SCSI_TASK_TERMINATED = 0x0048,
+ MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH = 0x0049,
+ MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED = 0x004A,
+ MPI_IOCSTATUS_SCSI_IOC_TERMINATED = 0x004B,
+ MPI_IOCSTATUS_SCSI_EXT_TERMINATED = 0x004C,
+
+ /************************************************************************/
+ /* For use by SCSI Initiator and SCSI Target end-to-end data protection*/
+ /************************************************************************/
+
+ MPI_IOCSTATUS_EEDP_GUARD_ERROR = 0x004D,
+ MPI_IOCSTATUS_EEDP_REF_TAG_ERROR = 0x004E,
+ MPI_IOCSTATUS_EEDP_APP_TAG_ERROR = 0x004F,
+
+ /************************************************************************/
+ /* SCSI Target values */
+ /************************************************************************/
+
+ MPI_IOCSTATUS_TARGET_PRIORITY_IO = 0x0060,
+ MPI_IOCSTATUS_TARGET_INVALID_PORT = 0x0061,
+ MPI_IOCSTATUS_TARGET_INVALID_IO_INDEX = 0x0062,
+ MPI_IOCSTATUS_TARGET_ABORTED = 0x0063,
+ MPI_IOCSTATUS_TARGET_NO_CONN_RETRYABLE = 0x0064,
+ MPI_IOCSTATUS_TARGET_NO_CONNECTION = 0x0065,
+ MPI_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH = 0x006A,
+ MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT = 0x006B,
+ MPI_IOCSTATUS_TARGET_DATA_OFFSET_ERROR = 0x006D,
+ MPI_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA = 0x006E,
+ MPI_IOCSTATUS_TARGET_IU_TOO_SHORT = 0x006F,
+ MPI_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT = 0x0070,
+ MPI_IOCSTATUS_TARGET_NAK_RECEIVED = 0x0071,
+
+ /************************************************************************/
+ /* Fibre Channel Direct Access values */
+ /************************************************************************/
+
+ MPI_IOCSTATUS_FC_ABORTED = 0x0066,
+ MPI_IOCSTATUS_FC_RX_ID_INVALID = 0x0067,
+ MPI_IOCSTATUS_FC_DID_INVALID = 0x0068,
+ MPI_IOCSTATUS_FC_NODE_LOGGED_OUT = 0x0069,
+ MPI_IOCSTATUS_FC_EXCHANGE_CANCELED = 0x006C,
+
+ /************************************************************************/
+ /* LAN values */
+ /************************************************************************/
+
+ MPI_IOCSTATUS_LAN_DEVICE_NOT_FOUND = 0x0080,
+ MPI_IOCSTATUS_LAN_DEVICE_FAILURE = 0x0081,
+ MPI_IOCSTATUS_LAN_TRANSMIT_ERROR = 0x0082,
+ MPI_IOCSTATUS_LAN_TRANSMIT_ABORTED = 0x0083,
+ MPI_IOCSTATUS_LAN_RECEIVE_ERROR = 0x0084,
+ MPI_IOCSTATUS_LAN_RECEIVE_ABORTED = 0x0085,
+ MPI_IOCSTATUS_LAN_PARTIAL_PACKET = 0x0086,
+ MPI_IOCSTATUS_LAN_CANCELED = 0x0087,
+
+ /************************************************************************/
+ /* Serial Attached SCSI values */
+ /************************************************************************/
+
+ MPI_IOCSTATUS_SAS_SMP_REQUEST_FAILED = 0x0090,
+ MPI_IOCSTATUS_SAS_SMP_DATA_OVERRUN = 0x0091,
+
+ /************************************************************************/
+ /* Inband values */
+ /************************************************************************/
+
+ MPI_IOCSTATUS_INBAND_ABORTED = 0x0098,
+ MPI_IOCSTATUS_INBAND_NO_CONNECTION = 0x0099,
+
+ /************************************************************************/
+ /* Diagnostic Tools values */
+ /************************************************************************/
+
+ MPI_IOCSTATUS_DIAGNOSTIC_RELEASED = 0x00A0,
+
+ /************************************************************************/
+ /* IOCStatus flag to indicate that log info is available */
+ /************************************************************************/
+
+ MPI_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE = 0x8000,
+ MPI_IOCSTATUS_MASK = 0x7FFF,
+
+ /************************************************************************/
+ /* LogInfo Types */
+ /************************************************************************/
+
+ MPI_IOCLOGINFO_TYPE_MASK = 0xF0000000,
+ MPI_IOCLOGINFO_TYPE_SHIFT = 28,
+ MPI_IOCLOGINFO_TYPE_NONE = 0x0,
+ MPI_IOCLOGINFO_TYPE_SCSI = 0x1,
+ MPI_IOCLOGINFO_TYPE_FC = 0x2,
+ MPI_IOCLOGINFO_TYPE_SAS = 0x3,
+ MPI_IOCLOGINFO_TYPE_ISCSI = 0x4,
+ MPI_IOCLOGINFO_LOG_DATA_MASK = 0x0FFFFFFF,
+};
+
+/****************************************************************************/
+/* SCSI IO messages and associated structures */
+/****************************************************************************/
+
+typedef struct MPIMsgSCSIIORequest {
+ uint8_t TargetID; /* 00h */
+ uint8_t Bus; /* 01h */
+ uint8_t ChainOffset; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t CDBLength; /* 04h */
+ uint8_t SenseBufferLength; /* 05h */
+ uint8_t Reserved; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint8_t LUN[8]; /* 0Ch */
+ uint32_t Control; /* 14h */
+ uint8_t CDB[16]; /* 18h */
+ uint32_t DataLength; /* 28h */
+ uint32_t SenseBufferLowAddr; /* 2Ch */
+} QEMU_PACKED MPIMsgSCSIIORequest;
+
+/* SCSI IO MsgFlags bits */
+
+#define MPI_SCSIIO_MSGFLGS_SENSE_WIDTH (0x01)
+#define MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_32 (0x00)
+#define MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_64 (0x01)
+
+#define MPI_SCSIIO_MSGFLGS_SENSE_LOCATION (0x02)
+#define MPI_SCSIIO_MSGFLGS_SENSE_LOC_HOST (0x00)
+#define MPI_SCSIIO_MSGFLGS_SENSE_LOC_IOC (0x02)
+
+#define MPI_SCSIIO_MSGFLGS_CMD_DETERMINES_DATA_DIR (0x04)
+
+/* SCSI IO LUN fields */
+
+#define MPI_SCSIIO_LUN_FIRST_LEVEL_ADDRESSING (0x0000FFFF)
+#define MPI_SCSIIO_LUN_SECOND_LEVEL_ADDRESSING (0xFFFF0000)
+#define MPI_SCSIIO_LUN_THIRD_LEVEL_ADDRESSING (0x0000FFFF)
+#define MPI_SCSIIO_LUN_FOURTH_LEVEL_ADDRESSING (0xFFFF0000)
+#define MPI_SCSIIO_LUN_LEVEL_1_WORD (0xFF00)
+#define MPI_SCSIIO_LUN_LEVEL_1_DWORD (0x0000FF00)
+
+/* SCSI IO Control bits */
+
+#define MPI_SCSIIO_CONTROL_DATADIRECTION_MASK (0x03000000)
+#define MPI_SCSIIO_CONTROL_NODATATRANSFER (0x00000000)
+#define MPI_SCSIIO_CONTROL_WRITE (0x01000000)
+#define MPI_SCSIIO_CONTROL_READ (0x02000000)
+
+#define MPI_SCSIIO_CONTROL_ADDCDBLEN_MASK (0x3C000000)
+#define MPI_SCSIIO_CONTROL_ADDCDBLEN_SHIFT (26)
+
+#define MPI_SCSIIO_CONTROL_TASKATTRIBUTE_MASK (0x00000700)
+#define MPI_SCSIIO_CONTROL_SIMPLEQ (0x00000000)
+#define MPI_SCSIIO_CONTROL_HEADOFQ (0x00000100)
+#define MPI_SCSIIO_CONTROL_ORDEREDQ (0x00000200)
+#define MPI_SCSIIO_CONTROL_ACAQ (0x00000400)
+#define MPI_SCSIIO_CONTROL_UNTAGGED (0x00000500)
+#define MPI_SCSIIO_CONTROL_NO_DISCONNECT (0x00000700)
+
+#define MPI_SCSIIO_CONTROL_TASKMANAGE_MASK (0x00FF0000)
+#define MPI_SCSIIO_CONTROL_OBSOLETE (0x00800000)
+#define MPI_SCSIIO_CONTROL_CLEAR_ACA_RSV (0x00400000)
+#define MPI_SCSIIO_CONTROL_TARGET_RESET (0x00200000)
+#define MPI_SCSIIO_CONTROL_LUN_RESET_RSV (0x00100000)
+#define MPI_SCSIIO_CONTROL_RESERVED (0x00080000)
+#define MPI_SCSIIO_CONTROL_CLR_TASK_SET_RSV (0x00040000)
+#define MPI_SCSIIO_CONTROL_ABORT_TASK_SET (0x00020000)
+#define MPI_SCSIIO_CONTROL_RESERVED2 (0x00010000)
+
+/* SCSI IO reply structure */
+typedef struct MPIMsgSCSIIOReply
+{
+ uint8_t TargetID; /* 00h */
+ uint8_t Bus; /* 01h */
+ uint8_t MsgLength; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t CDBLength; /* 04h */
+ uint8_t SenseBufferLength; /* 05h */
+ uint8_t Reserved; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint8_t SCSIStatus; /* 0Ch */
+ uint8_t SCSIState; /* 0Dh */
+ uint16_t IOCStatus; /* 0Eh */
+ uint32_t IOCLogInfo; /* 10h */
+ uint32_t TransferCount; /* 14h */
+ uint32_t SenseCount; /* 18h */
+ uint32_t ResponseInfo; /* 1Ch */
+ uint16_t TaskTag; /* 20h */
+ uint16_t Reserved1; /* 22h */
+} QEMU_PACKED MPIMsgSCSIIOReply;
+
+/* SCSI IO Reply SCSIStatus values (SAM-2 status codes) */
+
+#define MPI_SCSI_STATUS_SUCCESS (0x00)
+#define MPI_SCSI_STATUS_CHECK_CONDITION (0x02)
+#define MPI_SCSI_STATUS_CONDITION_MET (0x04)
+#define MPI_SCSI_STATUS_BUSY (0x08)
+#define MPI_SCSI_STATUS_INTERMEDIATE (0x10)
+#define MPI_SCSI_STATUS_INTERMEDIATE_CONDMET (0x14)
+#define MPI_SCSI_STATUS_RESERVATION_CONFLICT (0x18)
+#define MPI_SCSI_STATUS_COMMAND_TERMINATED (0x22)
+#define MPI_SCSI_STATUS_TASK_SET_FULL (0x28)
+#define MPI_SCSI_STATUS_ACA_ACTIVE (0x30)
+
+#define MPI_SCSI_STATUS_FCPEXT_DEVICE_LOGGED_OUT (0x80)
+#define MPI_SCSI_STATUS_FCPEXT_NO_LINK (0x81)
+#define MPI_SCSI_STATUS_FCPEXT_UNASSIGNED (0x82)
+
+
+/* SCSI IO Reply SCSIState values */
+
+#define MPI_SCSI_STATE_AUTOSENSE_VALID (0x01)
+#define MPI_SCSI_STATE_AUTOSENSE_FAILED (0x02)
+#define MPI_SCSI_STATE_NO_SCSI_STATUS (0x04)
+#define MPI_SCSI_STATE_TERMINATED (0x08)
+#define MPI_SCSI_STATE_RESPONSE_INFO_VALID (0x10)
+#define MPI_SCSI_STATE_QUEUE_TAG_REJECTED (0x20)
+
+/* SCSI IO Reply ResponseInfo values */
+/* (FCP-1 RSP_CODE values and SPI-3 Packetized Failure codes) */
+
+#define MPI_SCSI_RSP_INFO_FUNCTION_COMPLETE (0x00000000)
+#define MPI_SCSI_RSP_INFO_FCP_BURST_LEN_ERROR (0x01000000)
+#define MPI_SCSI_RSP_INFO_CMND_FIELDS_INVALID (0x02000000)
+#define MPI_SCSI_RSP_INFO_FCP_DATA_RO_ERROR (0x03000000)
+#define MPI_SCSI_RSP_INFO_TASK_MGMT_UNSUPPORTED (0x04000000)
+#define MPI_SCSI_RSP_INFO_TASK_MGMT_FAILED (0x05000000)
+#define MPI_SCSI_RSP_INFO_SPI_LQ_INVALID_TYPE (0x06000000)
+
+#define MPI_SCSI_TASKTAG_UNKNOWN (0xFFFF)
+
+
+/****************************************************************************/
+/* SCSI Task Management messages */
+/****************************************************************************/
+
+typedef struct MPIMsgSCSITaskMgmt {
+ uint8_t TargetID; /* 00h */
+ uint8_t Bus; /* 01h */
+ uint8_t ChainOffset; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t Reserved; /* 04h */
+ uint8_t TaskType; /* 05h */
+ uint8_t Reserved1; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint8_t LUN[8]; /* 0Ch */
+ uint32_t Reserved2[7]; /* 14h */
+ uint32_t TaskMsgContext; /* 30h */
+} QEMU_PACKED MPIMsgSCSITaskMgmt;
+
+enum {
+ /* TaskType values */
+
+ MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK = 0x01,
+ MPI_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET = 0x02,
+ MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET = 0x03,
+ MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS = 0x04,
+ MPI_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET = 0x05,
+ MPI_SCSITASKMGMT_TASKTYPE_CLEAR_TASK_SET = 0x06,
+ MPI_SCSITASKMGMT_TASKTYPE_QUERY_TASK = 0x07,
+ MPI_SCSITASKMGMT_TASKTYPE_CLR_ACA = 0x08,
+
+ /* MsgFlags bits */
+
+ MPI_SCSITASKMGMT_MSGFLAGS_DO_NOT_SEND_TASK_IU = 0x01,
+
+ MPI_SCSITASKMGMT_MSGFLAGS_TARGET_RESET_OPTION = 0x00,
+ MPI_SCSITASKMGMT_MSGFLAGS_LIP_RESET_OPTION = 0x02,
+ MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION = 0x04,
+
+ MPI_SCSITASKMGMT_MSGFLAGS_SOFT_RESET_OPTION = 0x08,
+};
+
+/* SCSI Task Management Reply */
+typedef struct MPIMsgSCSITaskMgmtReply {
+ uint8_t TargetID; /* 00h */
+ uint8_t Bus; /* 01h */
+ uint8_t MsgLength; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t ResponseCode; /* 04h */
+ uint8_t TaskType; /* 05h */
+ uint8_t Reserved1; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint8_t Reserved2[2]; /* 0Ch */
+ uint16_t IOCStatus; /* 0Eh */
+ uint32_t IOCLogInfo; /* 10h */
+ uint32_t TerminationCount; /* 14h */
+} QEMU_PACKED MPIMsgSCSITaskMgmtReply;
+
+/* ResponseCode values */
+enum {
+ MPI_SCSITASKMGMT_RSP_TM_COMPLETE = 0x00,
+ MPI_SCSITASKMGMT_RSP_INVALID_FRAME = 0x02,
+ MPI_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED = 0x04,
+ MPI_SCSITASKMGMT_RSP_TM_FAILED = 0x05,
+ MPI_SCSITASKMGMT_RSP_TM_SUCCEEDED = 0x08,
+ MPI_SCSITASKMGMT_RSP_TM_INVALID_LUN = 0x09,
+ MPI_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC = 0x80,
+};
+
+/****************************************************************************/
+/* IOCInit message */
+/****************************************************************************/
+
+typedef struct MPIMsgIOCInit {
+ uint8_t WhoInit; /* 00h */
+ uint8_t Reserved; /* 01h */
+ uint8_t ChainOffset; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t Flags; /* 04h */
+ uint8_t MaxDevices; /* 05h */
+ uint8_t MaxBuses; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint16_t ReplyFrameSize; /* 0Ch */
+ uint8_t Reserved1[2]; /* 0Eh */
+ uint32_t HostMfaHighAddr; /* 10h */
+ uint32_t SenseBufferHighAddr; /* 14h */
+ uint32_t ReplyFifoHostSignalingAddr; /* 18h */
+ MPISGEntry HostPageBufferSGE; /* 1Ch */
+ uint16_t MsgVersion; /* 28h */
+ uint16_t HeaderVersion; /* 2Ah */
+} QEMU_PACKED MPIMsgIOCInit;
+
+enum {
+ /* WhoInit values */
+
+ MPI_WHOINIT_NO_ONE = 0x00,
+ MPI_WHOINIT_SYSTEM_BIOS = 0x01,
+ MPI_WHOINIT_ROM_BIOS = 0x02,
+ MPI_WHOINIT_PCI_PEER = 0x03,
+ MPI_WHOINIT_HOST_DRIVER = 0x04,
+ MPI_WHOINIT_MANUFACTURER = 0x05,
+
+ /* Flags values */
+
+ MPI_IOCINIT_FLAGS_HOST_PAGE_BUFFER_PERSISTENT = 0x04,
+ MPI_IOCINIT_FLAGS_REPLY_FIFO_HOST_SIGNAL = 0x02,
+ MPI_IOCINIT_FLAGS_DISCARD_FW_IMAGE = 0x01,
+
+ /* MsgVersion */
+
+ MPI_IOCINIT_MSGVERSION_MAJOR_MASK = 0xFF00,
+ MPI_IOCINIT_MSGVERSION_MAJOR_SHIFT = 8,
+ MPI_IOCINIT_MSGVERSION_MINOR_MASK = 0x00FF,
+ MPI_IOCINIT_MSGVERSION_MINOR_SHIFT = 0,
+
+ /* HeaderVersion */
+
+ MPI_IOCINIT_HEADERVERSION_UNIT_MASK = 0xFF00,
+ MPI_IOCINIT_HEADERVERSION_UNIT_SHIFT = 8,
+ MPI_IOCINIT_HEADERVERSION_DEV_MASK = 0x00FF,
+ MPI_IOCINIT_HEADERVERSION_DEV_SHIFT = 0,
+};
+
+typedef struct MPIMsgIOCInitReply {
+ uint8_t WhoInit; /* 00h */
+ uint8_t Reserved; /* 01h */
+ uint8_t MsgLength; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t Flags; /* 04h */
+ uint8_t MaxDevices; /* 05h */
+ uint8_t MaxBuses; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint16_t Reserved2; /* 0Ch */
+ uint16_t IOCStatus; /* 0Eh */
+ uint32_t IOCLogInfo; /* 10h */
+} QEMU_PACKED MPIMsgIOCInitReply;
+
+
+
+/****************************************************************************/
+/* IOC Facts message */
+/****************************************************************************/
+
+typedef struct MPIMsgIOCFacts {
+ uint8_t Reserved[2]; /* 00h */
+ uint8_t ChainOffset; /* 01h */
+ uint8_t Function; /* 02h */
+ uint8_t Reserved1[3]; /* 03h */
+ uint8_t MsgFlags; /* 04h */
+ uint32_t MsgContext; /* 08h */
+} QEMU_PACKED MPIMsgIOCFacts;
+
+/* IOC Facts Reply */
+typedef struct MPIMsgIOCFactsReply {
+ uint16_t MsgVersion; /* 00h */
+ uint8_t MsgLength; /* 02h */
+ uint8_t Function; /* 03h */
+ uint16_t HeaderVersion; /* 04h */
+ uint8_t IOCNumber; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint16_t IOCExceptions; /* 0Ch */
+ uint16_t IOCStatus; /* 0Eh */
+ uint32_t IOCLogInfo; /* 10h */
+ uint8_t MaxChainDepth; /* 14h */
+ uint8_t WhoInit; /* 15h */
+ uint8_t BlockSize; /* 16h */
+ uint8_t Flags; /* 17h */
+ uint16_t ReplyQueueDepth; /* 18h */
+ uint16_t RequestFrameSize; /* 1Ah */
+ uint16_t Reserved_0101_FWVersion; /* 1Ch */ /* obsolete 16-bit FWVersion */
+ uint16_t ProductID; /* 1Eh */
+ uint32_t CurrentHostMfaHighAddr; /* 20h */
+ uint16_t GlobalCredits; /* 24h */
+ uint8_t NumberOfPorts; /* 26h */
+ uint8_t EventState; /* 27h */
+ uint32_t CurrentSenseBufferHighAddr; /* 28h */
+ uint16_t CurReplyFrameSize; /* 2Ch */
+ uint8_t MaxDevices; /* 2Eh */
+ uint8_t MaxBuses; /* 2Fh */
+ uint32_t FWImageSize; /* 30h */
+ uint32_t IOCCapabilities; /* 34h */
+ uint8_t FWVersionDev; /* 38h */
+ uint8_t FWVersionUnit; /* 39h */
+ uint8_t FWVersionMinor; /* 3ah */
+ uint8_t FWVersionMajor; /* 3bh */
+ uint16_t HighPriorityQueueDepth; /* 3Ch */
+ uint16_t Reserved2; /* 3Eh */
+ MPISGEntry HostPageBufferSGE; /* 40h */
+ uint32_t ReplyFifoHostSignalingAddr; /* 4Ch */
+} QEMU_PACKED MPIMsgIOCFactsReply;
+
+enum {
+ MPI_IOCFACTS_MSGVERSION_MAJOR_MASK = 0xFF00,
+ MPI_IOCFACTS_MSGVERSION_MAJOR_SHIFT = 8,
+ MPI_IOCFACTS_MSGVERSION_MINOR_MASK = 0x00FF,
+ MPI_IOCFACTS_MSGVERSION_MINOR_SHIFT = 0,
+
+ MPI_IOCFACTS_HDRVERSION_UNIT_MASK = 0xFF00,
+ MPI_IOCFACTS_HDRVERSION_UNIT_SHIFT = 8,
+ MPI_IOCFACTS_HDRVERSION_DEV_MASK = 0x00FF,
+ MPI_IOCFACTS_HDRVERSION_DEV_SHIFT = 0,
+
+ MPI_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL = 0x0001,
+ MPI_IOCFACTS_EXCEPT_RAID_CONFIG_INVALID = 0x0002,
+ MPI_IOCFACTS_EXCEPT_FW_CHECKSUM_FAIL = 0x0004,
+ MPI_IOCFACTS_EXCEPT_PERSISTENT_TABLE_FULL = 0x0008,
+ MPI_IOCFACTS_EXCEPT_METADATA_UNSUPPORTED = 0x0010,
+
+ MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT = 0x01,
+ MPI_IOCFACTS_FLAGS_REPLY_FIFO_HOST_SIGNAL = 0x02,
+ MPI_IOCFACTS_FLAGS_HOST_PAGE_BUFFER_PERSISTENT = 0x04,
+
+ MPI_IOCFACTS_EVENTSTATE_DISABLED = 0x00,
+ MPI_IOCFACTS_EVENTSTATE_ENABLED = 0x01,
+
+ MPI_IOCFACTS_CAPABILITY_HIGH_PRI_Q = 0x00000001,
+ MPI_IOCFACTS_CAPABILITY_REPLY_HOST_SIGNAL = 0x00000002,
+ MPI_IOCFACTS_CAPABILITY_QUEUE_FULL_HANDLING = 0x00000004,
+ MPI_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER = 0x00000008,
+ MPI_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER = 0x00000010,
+ MPI_IOCFACTS_CAPABILITY_EXTENDED_BUFFER = 0x00000020,
+ MPI_IOCFACTS_CAPABILITY_EEDP = 0x00000040,
+ MPI_IOCFACTS_CAPABILITY_BIDIRECTIONAL = 0x00000080,
+ MPI_IOCFACTS_CAPABILITY_MULTICAST = 0x00000100,
+ MPI_IOCFACTS_CAPABILITY_SCSIIO32 = 0x00000200,
+ MPI_IOCFACTS_CAPABILITY_NO_SCSIIO16 = 0x00000400,
+ MPI_IOCFACTS_CAPABILITY_TLR = 0x00000800,
+};
+
+/****************************************************************************/
+/* Port Facts message and Reply */
+/****************************************************************************/
+
+typedef struct MPIMsgPortFacts {
+ uint8_t Reserved[2]; /* 00h */
+ uint8_t ChainOffset; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t Reserved1[2]; /* 04h */
+ uint8_t PortNumber; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+} QEMU_PACKED MPIMsgPortFacts;
+
+typedef struct MPIMsgPortFactsReply {
+ uint16_t Reserved; /* 00h */
+ uint8_t MsgLength; /* 02h */
+ uint8_t Function; /* 03h */
+ uint16_t Reserved1; /* 04h */
+ uint8_t PortNumber; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint16_t Reserved2; /* 0Ch */
+ uint16_t IOCStatus; /* 0Eh */
+ uint32_t IOCLogInfo; /* 10h */
+ uint8_t Reserved3; /* 14h */
+ uint8_t PortType; /* 15h */
+ uint16_t MaxDevices; /* 16h */
+ uint16_t PortSCSIID; /* 18h */
+ uint16_t ProtocolFlags; /* 1Ah */
+ uint16_t MaxPostedCmdBuffers; /* 1Ch */
+ uint16_t MaxPersistentIDs; /* 1Eh */
+ uint16_t MaxLanBuckets; /* 20h */
+ uint8_t MaxInitiators; /* 22h */
+ uint8_t Reserved4; /* 23h */
+ uint32_t Reserved5; /* 24h */
+} QEMU_PACKED MPIMsgPortFactsReply;
+
+
+enum {
+ /* PortTypes values */
+ MPI_PORTFACTS_PORTTYPE_INACTIVE = 0x00,
+ MPI_PORTFACTS_PORTTYPE_SCSI = 0x01,
+ MPI_PORTFACTS_PORTTYPE_FC = 0x10,
+ MPI_PORTFACTS_PORTTYPE_ISCSI = 0x20,
+ MPI_PORTFACTS_PORTTYPE_SAS = 0x30,
+
+ /* ProtocolFlags values */
+ MPI_PORTFACTS_PROTOCOL_LOGBUSADDR = 0x01,
+ MPI_PORTFACTS_PROTOCOL_LAN = 0x02,
+ MPI_PORTFACTS_PROTOCOL_TARGET = 0x04,
+ MPI_PORTFACTS_PROTOCOL_INITIATOR = 0x08,
+};
+
+
+/****************************************************************************/
+/* Port Enable Message */
+/****************************************************************************/
+
+typedef struct MPIMsgPortEnable {
+ uint8_t Reserved[2]; /* 00h */
+ uint8_t ChainOffset; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t Reserved1[2]; /* 04h */
+ uint8_t PortNumber; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+} QEMU_PACKED MPIMsgPortEnable;
+
+typedef struct MPIMsgPortEnableReply {
+ uint8_t Reserved[2]; /* 00h */
+ uint8_t MsgLength; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t Reserved1[2]; /* 04h */
+ uint8_t PortNumber; /* 05h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint16_t Reserved2; /* 0Ch */
+ uint16_t IOCStatus; /* 0Eh */
+ uint32_t IOCLogInfo; /* 10h */
+} QEMU_PACKED MPIMsgPortEnableReply;
+
+/****************************************************************************/
+/* Event Notification messages */
+/****************************************************************************/
+
+typedef struct MPIMsgEventNotify {
+ uint8_t Switch; /* 00h */
+ uint8_t Reserved; /* 01h */
+ uint8_t ChainOffset; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t Reserved1[3]; /* 04h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+} QEMU_PACKED MPIMsgEventNotify;
+
+/* Event Notification Reply */
+
+typedef struct MPIMsgEventNotifyReply {
+ uint16_t EventDataLength; /* 00h */
+ uint8_t MsgLength; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t Reserved1[2]; /* 04h */
+ uint8_t AckRequired; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint8_t Reserved2[2]; /* 0Ch */
+ uint16_t IOCStatus; /* 0Eh */
+ uint32_t IOCLogInfo; /* 10h */
+ uint32_t Event; /* 14h */
+ uint32_t EventContext; /* 18h */
+ uint32_t Data[1]; /* 1Ch */
+} QEMU_PACKED MPIMsgEventNotifyReply;
+
+/* Event Acknowledge */
+
+typedef struct MPIMsgEventAck {
+ uint8_t Reserved[2]; /* 00h */
+ uint8_t ChainOffset; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t Reserved1[3]; /* 04h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint32_t Event; /* 0Ch */
+ uint32_t EventContext; /* 10h */
+} QEMU_PACKED MPIMsgEventAck;
+
+typedef struct MPIMsgEventAckReply {
+ uint8_t Reserved[2]; /* 00h */
+ uint8_t MsgLength; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t Reserved1[3]; /* 04h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint16_t Reserved2; /* 0Ch */
+ uint16_t IOCStatus; /* 0Eh */
+ uint32_t IOCLogInfo; /* 10h */
+} QEMU_PACKED MPIMsgEventAckReply;
+
+enum {
+ /* Switch */
+
+ MPI_EVENT_NOTIFICATION_SWITCH_OFF = 0x00,
+ MPI_EVENT_NOTIFICATION_SWITCH_ON = 0x01,
+
+ /* Event */
+
+ MPI_EVENT_NONE = 0x00000000,
+ MPI_EVENT_LOG_DATA = 0x00000001,
+ MPI_EVENT_STATE_CHANGE = 0x00000002,
+ MPI_EVENT_UNIT_ATTENTION = 0x00000003,
+ MPI_EVENT_IOC_BUS_RESET = 0x00000004,
+ MPI_EVENT_EXT_BUS_RESET = 0x00000005,
+ MPI_EVENT_RESCAN = 0x00000006,
+ MPI_EVENT_LINK_STATUS_CHANGE = 0x00000007,
+ MPI_EVENT_LOOP_STATE_CHANGE = 0x00000008,
+ MPI_EVENT_LOGOUT = 0x00000009,
+ MPI_EVENT_EVENT_CHANGE = 0x0000000A,
+ MPI_EVENT_INTEGRATED_RAID = 0x0000000B,
+ MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE = 0x0000000C,
+ MPI_EVENT_ON_BUS_TIMER_EXPIRED = 0x0000000D,
+ MPI_EVENT_QUEUE_FULL = 0x0000000E,
+ MPI_EVENT_SAS_DEVICE_STATUS_CHANGE = 0x0000000F,
+ MPI_EVENT_SAS_SES = 0x00000010,
+ MPI_EVENT_PERSISTENT_TABLE_FULL = 0x00000011,
+ MPI_EVENT_SAS_PHY_LINK_STATUS = 0x00000012,
+ MPI_EVENT_SAS_DISCOVERY_ERROR = 0x00000013,
+ MPI_EVENT_IR_RESYNC_UPDATE = 0x00000014,
+ MPI_EVENT_IR2 = 0x00000015,
+ MPI_EVENT_SAS_DISCOVERY = 0x00000016,
+ MPI_EVENT_SAS_BROADCAST_PRIMITIVE = 0x00000017,
+ MPI_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE = 0x00000018,
+ MPI_EVENT_SAS_INIT_TABLE_OVERFLOW = 0x00000019,
+ MPI_EVENT_SAS_SMP_ERROR = 0x0000001A,
+ MPI_EVENT_SAS_EXPANDER_STATUS_CHANGE = 0x0000001B,
+ MPI_EVENT_LOG_ENTRY_ADDED = 0x00000021,
+
+ /* AckRequired field values */
+
+ MPI_EVENT_NOTIFICATION_ACK_NOT_REQUIRED = 0x00,
+ MPI_EVENT_NOTIFICATION_ACK_REQUIRED = 0x01,
+};
+
+/****************************************************************************
+* Config Request Message
+****************************************************************************/
+
+typedef struct MPIMsgConfig {
+ uint8_t Action; /* 00h */
+ uint8_t Reserved; /* 01h */
+ uint8_t ChainOffset; /* 02h */
+ uint8_t Function; /* 03h */
+ uint16_t ExtPageLength; /* 04h */
+ uint8_t ExtPageType; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint8_t Reserved2[8]; /* 0Ch */
+ uint8_t PageVersion; /* 14h */
+ uint8_t PageLength; /* 15h */
+ uint8_t PageNumber; /* 16h */
+ uint8_t PageType; /* 17h */
+ uint32_t PageAddress; /* 18h */
+ MPISGEntry PageBufferSGE; /* 1Ch */
+} QEMU_PACKED MPIMsgConfig;
+
+/* Action field values */
+
+enum {
+ MPI_CONFIG_ACTION_PAGE_HEADER = 0x00,
+ MPI_CONFIG_ACTION_PAGE_READ_CURRENT = 0x01,
+ MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT = 0x02,
+ MPI_CONFIG_ACTION_PAGE_DEFAULT = 0x03,
+ MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM = 0x04,
+ MPI_CONFIG_ACTION_PAGE_READ_DEFAULT = 0x05,
+ MPI_CONFIG_ACTION_PAGE_READ_NVRAM = 0x06,
+};
+
+
+/* Config Reply Message */
+typedef struct MPIMsgConfigReply {
+ uint8_t Action; /* 00h */
+ uint8_t Reserved; /* 01h */
+ uint8_t MsgLength; /* 02h */
+ uint8_t Function; /* 03h */
+ uint16_t ExtPageLength; /* 04h */
+ uint8_t ExtPageType; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint8_t Reserved2[2]; /* 0Ch */
+ uint16_t IOCStatus; /* 0Eh */
+ uint32_t IOCLogInfo; /* 10h */
+ uint8_t PageVersion; /* 14h */
+ uint8_t PageLength; /* 15h */
+ uint8_t PageNumber; /* 16h */
+ uint8_t PageType; /* 17h */
+} QEMU_PACKED MPIMsgConfigReply;
+
+enum {
+ /* PageAddress field values */
+ MPI_CONFIG_PAGEATTR_READ_ONLY = 0x00,
+ MPI_CONFIG_PAGEATTR_CHANGEABLE = 0x10,
+ MPI_CONFIG_PAGEATTR_PERSISTENT = 0x20,
+ MPI_CONFIG_PAGEATTR_RO_PERSISTENT = 0x30,
+ MPI_CONFIG_PAGEATTR_MASK = 0xF0,
+
+ MPI_CONFIG_PAGETYPE_IO_UNIT = 0x00,
+ MPI_CONFIG_PAGETYPE_IOC = 0x01,
+ MPI_CONFIG_PAGETYPE_BIOS = 0x02,
+ MPI_CONFIG_PAGETYPE_SCSI_PORT = 0x03,
+ MPI_CONFIG_PAGETYPE_SCSI_DEVICE = 0x04,
+ MPI_CONFIG_PAGETYPE_FC_PORT = 0x05,
+ MPI_CONFIG_PAGETYPE_FC_DEVICE = 0x06,
+ MPI_CONFIG_PAGETYPE_LAN = 0x07,
+ MPI_CONFIG_PAGETYPE_RAID_VOLUME = 0x08,
+ MPI_CONFIG_PAGETYPE_MANUFACTURING = 0x09,
+ MPI_CONFIG_PAGETYPE_RAID_PHYSDISK = 0x0A,
+ MPI_CONFIG_PAGETYPE_INBAND = 0x0B,
+ MPI_CONFIG_PAGETYPE_EXTENDED = 0x0F,
+ MPI_CONFIG_PAGETYPE_MASK = 0x0F,
+
+ MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT = 0x10,
+ MPI_CONFIG_EXTPAGETYPE_SAS_EXPANDER = 0x11,
+ MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE = 0x12,
+ MPI_CONFIG_EXTPAGETYPE_SAS_PHY = 0x13,
+ MPI_CONFIG_EXTPAGETYPE_LOG = 0x14,
+ MPI_CONFIG_EXTPAGETYPE_ENCLOSURE = 0x15,
+
+ MPI_SCSI_PORT_PGAD_PORT_MASK = 0x000000FF,
+
+ MPI_SCSI_DEVICE_FORM_MASK = 0xF0000000,
+ MPI_SCSI_DEVICE_FORM_BUS_TID = 0x00000000,
+ MPI_SCSI_DEVICE_TARGET_ID_MASK = 0x000000FF,
+ MPI_SCSI_DEVICE_TARGET_ID_SHIFT = 0,
+ MPI_SCSI_DEVICE_BUS_MASK = 0x0000FF00,
+ MPI_SCSI_DEVICE_BUS_SHIFT = 8,
+ MPI_SCSI_DEVICE_FORM_TARGET_MODE = 0x10000000,
+ MPI_SCSI_DEVICE_TM_RESPOND_ID_MASK = 0x000000FF,
+ MPI_SCSI_DEVICE_TM_RESPOND_ID_SHIFT = 0,
+ MPI_SCSI_DEVICE_TM_BUS_MASK = 0x0000FF00,
+ MPI_SCSI_DEVICE_TM_BUS_SHIFT = 8,
+ MPI_SCSI_DEVICE_TM_INIT_ID_MASK = 0x00FF0000,
+ MPI_SCSI_DEVICE_TM_INIT_ID_SHIFT = 16,
+
+ MPI_FC_PORT_PGAD_PORT_MASK = 0xF0000000,
+ MPI_FC_PORT_PGAD_PORT_SHIFT = 28,
+ MPI_FC_PORT_PGAD_FORM_MASK = 0x0F000000,
+ MPI_FC_PORT_PGAD_FORM_INDEX = 0x01000000,
+ MPI_FC_PORT_PGAD_INDEX_MASK = 0x0000FFFF,
+ MPI_FC_PORT_PGAD_INDEX_SHIFT = 0,
+
+ MPI_FC_DEVICE_PGAD_PORT_MASK = 0xF0000000,
+ MPI_FC_DEVICE_PGAD_PORT_SHIFT = 28,
+ MPI_FC_DEVICE_PGAD_FORM_MASK = 0x0F000000,
+ MPI_FC_DEVICE_PGAD_FORM_NEXT_DID = 0x00000000,
+ MPI_FC_DEVICE_PGAD_ND_PORT_MASK = 0xF0000000,
+ MPI_FC_DEVICE_PGAD_ND_PORT_SHIFT = 28,
+ MPI_FC_DEVICE_PGAD_ND_DID_MASK = 0x00FFFFFF,
+ MPI_FC_DEVICE_PGAD_ND_DID_SHIFT = 0,
+ MPI_FC_DEVICE_PGAD_FORM_BUS_TID = 0x01000000,
+ MPI_FC_DEVICE_PGAD_BT_BUS_MASK = 0x0000FF00,
+ MPI_FC_DEVICE_PGAD_BT_BUS_SHIFT = 8,
+ MPI_FC_DEVICE_PGAD_BT_TID_MASK = 0x000000FF,
+ MPI_FC_DEVICE_PGAD_BT_TID_SHIFT = 0,
+
+ MPI_PHYSDISK_PGAD_PHYSDISKNUM_MASK = 0x000000FF,
+ MPI_PHYSDISK_PGAD_PHYSDISKNUM_SHIFT = 0,
+
+ MPI_SAS_EXPAND_PGAD_FORM_MASK = 0xF0000000,
+ MPI_SAS_EXPAND_PGAD_FORM_SHIFT = 28,
+ MPI_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE = 0x00000000,
+ MPI_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM = 0x00000001,
+ MPI_SAS_EXPAND_PGAD_FORM_HANDLE = 0x00000002,
+ MPI_SAS_EXPAND_PGAD_GNH_MASK_HANDLE = 0x0000FFFF,
+ MPI_SAS_EXPAND_PGAD_GNH_SHIFT_HANDLE = 0,
+ MPI_SAS_EXPAND_PGAD_HPN_MASK_PHY = 0x00FF0000,
+ MPI_SAS_EXPAND_PGAD_HPN_SHIFT_PHY = 16,
+ MPI_SAS_EXPAND_PGAD_HPN_MASK_HANDLE = 0x0000FFFF,
+ MPI_SAS_EXPAND_PGAD_HPN_SHIFT_HANDLE = 0,
+ MPI_SAS_EXPAND_PGAD_H_MASK_HANDLE = 0x0000FFFF,
+ MPI_SAS_EXPAND_PGAD_H_SHIFT_HANDLE = 0,
+
+ MPI_SAS_DEVICE_PGAD_FORM_MASK = 0xF0000000,
+ MPI_SAS_DEVICE_PGAD_FORM_SHIFT = 28,
+ MPI_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE = 0x00000000,
+ MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID = 0x00000001,
+ MPI_SAS_DEVICE_PGAD_FORM_HANDLE = 0x00000002,
+ MPI_SAS_DEVICE_PGAD_GNH_HANDLE_MASK = 0x0000FFFF,
+ MPI_SAS_DEVICE_PGAD_GNH_HANDLE_SHIFT = 0,
+ MPI_SAS_DEVICE_PGAD_BT_BUS_MASK = 0x0000FF00,
+ MPI_SAS_DEVICE_PGAD_BT_BUS_SHIFT = 8,
+ MPI_SAS_DEVICE_PGAD_BT_TID_MASK = 0x000000FF,
+ MPI_SAS_DEVICE_PGAD_BT_TID_SHIFT = 0,
+ MPI_SAS_DEVICE_PGAD_H_HANDLE_MASK = 0x0000FFFF,
+ MPI_SAS_DEVICE_PGAD_H_HANDLE_SHIFT = 0,
+
+ MPI_SAS_PHY_PGAD_FORM_MASK = 0xF0000000,
+ MPI_SAS_PHY_PGAD_FORM_SHIFT = 28,
+ MPI_SAS_PHY_PGAD_FORM_PHY_NUMBER = 0x0,
+ MPI_SAS_PHY_PGAD_FORM_PHY_TBL_INDEX = 0x1,
+ MPI_SAS_PHY_PGAD_PHY_NUMBER_MASK = 0x000000FF,
+ MPI_SAS_PHY_PGAD_PHY_NUMBER_SHIFT = 0,
+ MPI_SAS_PHY_PGAD_PHY_TBL_INDEX_MASK = 0x0000FFFF,
+ MPI_SAS_PHY_PGAD_PHY_TBL_INDEX_SHIFT = 0,
+
+ MPI_SAS_ENCLOS_PGAD_FORM_MASK = 0xF0000000,
+ MPI_SAS_ENCLOS_PGAD_FORM_SHIFT = 28,
+ MPI_SAS_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE = 0x00000000,
+ MPI_SAS_ENCLOS_PGAD_FORM_HANDLE = 0x00000001,
+ MPI_SAS_ENCLOS_PGAD_GNH_HANDLE_MASK = 0x0000FFFF,
+ MPI_SAS_ENCLOS_PGAD_GNH_HANDLE_SHIFT = 0,
+ MPI_SAS_ENCLOS_PGAD_H_HANDLE_MASK = 0x0000FFFF,
+ MPI_SAS_ENCLOS_PGAD_H_HANDLE_SHIFT = 0,
+};
+
+/* Too many structs and definitions... see mptconfig.c for the few
+ * that are used.
+ */
+
+/****************************************************************************/
+/* Firmware Upload message and associated structures */
+/****************************************************************************/
+
+enum {
+ /* defines for using the ProductId field */
+ MPI_FW_HEADER_PID_TYPE_MASK = 0xF000,
+ MPI_FW_HEADER_PID_TYPE_SCSI = 0x0000,
+ MPI_FW_HEADER_PID_TYPE_FC = 0x1000,
+ MPI_FW_HEADER_PID_TYPE_SAS = 0x2000,
+
+ MPI_FW_HEADER_PID_PROD_MASK = 0x0F00,
+ MPI_FW_HEADER_PID_PROD_INITIATOR_SCSI = 0x0100,
+ MPI_FW_HEADER_PID_PROD_TARGET_INITIATOR_SCSI = 0x0200,
+ MPI_FW_HEADER_PID_PROD_TARGET_SCSI = 0x0300,
+ MPI_FW_HEADER_PID_PROD_IM_SCSI = 0x0400,
+ MPI_FW_HEADER_PID_PROD_IS_SCSI = 0x0500,
+ MPI_FW_HEADER_PID_PROD_CTX_SCSI = 0x0600,
+ MPI_FW_HEADER_PID_PROD_IR_SCSI = 0x0700,
+
+ MPI_FW_HEADER_PID_FAMILY_MASK = 0x00FF,
+
+ /* SCSI */
+ MPI_FW_HEADER_PID_FAMILY_1030A0_SCSI = 0x0001,
+ MPI_FW_HEADER_PID_FAMILY_1030B0_SCSI = 0x0002,
+ MPI_FW_HEADER_PID_FAMILY_1030B1_SCSI = 0x0003,
+ MPI_FW_HEADER_PID_FAMILY_1030C0_SCSI = 0x0004,
+ MPI_FW_HEADER_PID_FAMILY_1020A0_SCSI = 0x0005,
+ MPI_FW_HEADER_PID_FAMILY_1020B0_SCSI = 0x0006,
+ MPI_FW_HEADER_PID_FAMILY_1020B1_SCSI = 0x0007,
+ MPI_FW_HEADER_PID_FAMILY_1020C0_SCSI = 0x0008,
+ MPI_FW_HEADER_PID_FAMILY_1035A0_SCSI = 0x0009,
+ MPI_FW_HEADER_PID_FAMILY_1035B0_SCSI = 0x000A,
+ MPI_FW_HEADER_PID_FAMILY_1030TA0_SCSI = 0x000B,
+ MPI_FW_HEADER_PID_FAMILY_1020TA0_SCSI = 0x000C,
+
+ /* Fibre Channel */
+ MPI_FW_HEADER_PID_FAMILY_909_FC = 0x0000,
+ MPI_FW_HEADER_PID_FAMILY_919_FC = 0x0001, /* 919 and 929 */
+ MPI_FW_HEADER_PID_FAMILY_919X_FC = 0x0002, /* 919X and 929X */
+ MPI_FW_HEADER_PID_FAMILY_919XL_FC = 0x0003, /* 919XL and 929XL */
+ MPI_FW_HEADER_PID_FAMILY_939X_FC = 0x0004, /* 939X and 949X */
+ MPI_FW_HEADER_PID_FAMILY_959_FC = 0x0005,
+ MPI_FW_HEADER_PID_FAMILY_949E_FC = 0x0006,
+
+ /* SAS */
+ MPI_FW_HEADER_PID_FAMILY_1064_SAS = 0x0001,
+ MPI_FW_HEADER_PID_FAMILY_1068_SAS = 0x0002,
+ MPI_FW_HEADER_PID_FAMILY_1078_SAS = 0x0003,
+ MPI_FW_HEADER_PID_FAMILY_106xE_SAS = 0x0004, /* 1068E, 1066E, and 1064E */
+};
+
+#endif
diff --git a/qemu/hw/scsi/mptconfig.c b/qemu/hw/scsi/mptconfig.c
new file mode 100644
index 000000000..707185469
--- /dev/null
+++ b/qemu/hw/scsi/mptconfig.c
@@ -0,0 +1,905 @@
+/*
+ * QEMU LSI SAS1068 Host Bus Adapter emulation - configuration pages
+ *
+ * Copyright (c) 2016 Red Hat, Inc.
+ *
+ * Author: Paolo Bonzini
+ *
+ * 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 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.
+ */
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "hw/scsi/scsi.h"
+
+#include "mptsas.h"
+#include "mpi.h"
+#include "trace.h"
+
+/* Generic functions for marshaling and unmarshaling. */
+
+#define repl1(x) x
+#define repl2(x) x x
+#define repl3(x) x x x
+#define repl4(x) x x x x
+#define repl5(x) x x x x x
+#define repl6(x) x x x x x x
+#define repl7(x) x x x x x x x
+#define repl8(x) x x x x x x x x
+
+#define repl(n, x) glue(repl, n)(x)
+
+typedef union PackValue {
+ uint64_t ll;
+ char *str;
+} PackValue;
+
+static size_t vfill(uint8_t *data, size_t size, const char *fmt, va_list ap)
+{
+ size_t ofs;
+ PackValue val;
+ const char *p;
+
+ ofs = 0;
+ p = fmt;
+ while (*p) {
+ memset(&val, 0, sizeof(val));
+ switch (*p) {
+ case '*':
+ p++;
+ break;
+ case 'b':
+ case 'w':
+ case 'l':
+ val.ll = va_arg(ap, int);
+ break;
+ case 'q':
+ val.ll = va_arg(ap, int64_t);
+ break;
+ case 's':
+ val.str = va_arg(ap, void *);
+ break;
+ }
+ switch (*p++) {
+ case 'b':
+ if (data) {
+ stb_p(data + ofs, val.ll);
+ }
+ ofs++;
+ break;
+ case 'w':
+ if (data) {
+ stw_le_p(data + ofs, val.ll);
+ }
+ ofs += 2;
+ break;
+ case 'l':
+ if (data) {
+ stl_le_p(data + ofs, val.ll);
+ }
+ ofs += 4;
+ break;
+ case 'q':
+ if (data) {
+ stq_le_p(data + ofs, val.ll);
+ }
+ ofs += 8;
+ break;
+ case 's':
+ {
+ int cnt = atoi(p);
+ if (data) {
+ if (val.str) {
+ strncpy((void *)data + ofs, val.str, cnt);
+ } else {
+ memset((void *)data + ofs, 0, cnt);
+ }
+ }
+ ofs += cnt;
+ break;
+ }
+ }
+ }
+
+ return ofs;
+}
+
+static size_t vpack(uint8_t **p_data, const char *fmt, va_list ap1)
+{
+ size_t size = 0;
+ uint8_t *data = NULL;
+
+ if (p_data) {
+ va_list ap2;
+
+ va_copy(ap2, ap1);
+ size = vfill(NULL, 0, fmt, ap2);
+ *p_data = data = g_malloc(size);
+ va_end(ap2);
+ }
+ return vfill(data, size, fmt, ap1);
+}
+
+static size_t fill(uint8_t *data, size_t size, const char *fmt, ...)
+{
+ va_list ap;
+ size_t ret;
+
+ va_start(ap, fmt);
+ ret = vfill(data, size, fmt, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+/* Functions to build the page header and fill in the length, always used
+ * through the macros.
+ */
+
+#define MPTSAS_CONFIG_PACK(number, type, version, fmt, ...) \
+ mptsas_config_pack(data, "b*bbb" fmt, version, number, type, \
+ ## __VA_ARGS__)
+
+static size_t mptsas_config_pack(uint8_t **data, const char *fmt, ...)
+{
+ va_list ap;
+ size_t ret;
+
+ va_start(ap, fmt);
+ ret = vpack(data, fmt, ap);
+ va_end(ap);
+
+ if (data) {
+ assert(ret < 256 && (ret % 4) == 0);
+ stb_p(*data + 1, ret / 4);
+ }
+ return ret;
+}
+
+#define MPTSAS_CONFIG_PACK_EXT(number, type, version, fmt, ...) \
+ mptsas_config_pack_ext(data, "b*bbb*wb*b" fmt, version, number, \
+ MPI_CONFIG_PAGETYPE_EXTENDED, type, ## __VA_ARGS__)
+
+static size_t mptsas_config_pack_ext(uint8_t **data, const char *fmt, ...)
+{
+ va_list ap;
+ size_t ret;
+
+ va_start(ap, fmt);
+ ret = vpack(data, fmt, ap);
+ va_end(ap);
+
+ if (data) {
+ assert(ret < 65536 && (ret % 4) == 0);
+ stw_le_p(*data + 4, ret / 4);
+ }
+ return ret;
+}
+
+/* Manufacturing pages */
+
+static
+size_t mptsas_config_manufacturing_0(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(0, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
+ "s16s8s16s16s16",
+ "QEMU MPT Fusion",
+ "2.5",
+ "QEMU MPT Fusion",
+ "QEMU",
+ "0000111122223333");
+}
+
+static
+size_t mptsas_config_manufacturing_1(MPTSASState *s, uint8_t **data, int address)
+{
+ /* VPD - all zeros */
+ return MPTSAS_CONFIG_PACK(1, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
+ "s256");
+}
+
+static
+size_t mptsas_config_manufacturing_2(MPTSASState *s, uint8_t **data, int address)
+{
+ PCIDeviceClass *pcic = PCI_DEVICE_GET_CLASS(s);
+ return MPTSAS_CONFIG_PACK(2, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
+ "wb*b*l",
+ pcic->device_id, pcic->revision);
+}
+
+static
+size_t mptsas_config_manufacturing_3(MPTSASState *s, uint8_t **data, int address)
+{
+ PCIDeviceClass *pcic = PCI_DEVICE_GET_CLASS(s);
+ return MPTSAS_CONFIG_PACK(3, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
+ "wb*b*l",
+ pcic->device_id, pcic->revision);
+}
+
+static
+size_t mptsas_config_manufacturing_4(MPTSASState *s, uint8_t **data, int address)
+{
+ /* All zeros */
+ return MPTSAS_CONFIG_PACK(4, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x05,
+ "*l*b*b*b*b*b*b*w*s56*l*l*l*l*l*l"
+ "*b*b*w*b*b*w*l*l");
+}
+
+static
+size_t mptsas_config_manufacturing_5(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(5, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x02,
+ "q*b*b*w*l*l", s->sas_addr);
+}
+
+static
+size_t mptsas_config_manufacturing_6(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(6, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
+ "*l");
+}
+
+static
+size_t mptsas_config_manufacturing_7(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(7, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
+ "*l*l*l*s16*b*b*w", MPTSAS_NUM_PORTS);
+}
+
+static
+size_t mptsas_config_manufacturing_8(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(8, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
+ "*l");
+}
+
+static
+size_t mptsas_config_manufacturing_9(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(9, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
+ "*l");
+}
+
+static
+size_t mptsas_config_manufacturing_10(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(10, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
+ "*l");
+}
+
+/* I/O unit pages */
+
+static
+size_t mptsas_config_io_unit_0(MPTSASState *s, uint8_t **data, int address)
+{
+ PCIDevice *pci = PCI_DEVICE(s);
+ uint64_t unique_value = 0x53504D554D4551LL; /* "QEMUMPTx" */
+
+ unique_value |= (uint64_t)pci->devfn << 56;
+ return MPTSAS_CONFIG_PACK(0, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x00,
+ "q", unique_value);
+}
+
+static
+size_t mptsas_config_io_unit_1(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(1, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x02, "l",
+ 0x41 /* single function, RAID disabled */ );
+}
+
+static
+size_t mptsas_config_io_unit_2(MPTSASState *s, uint8_t **data, int address)
+{
+ PCIDevice *pci = PCI_DEVICE(s);
+ uint8_t devfn = pci->devfn;
+ return MPTSAS_CONFIG_PACK(2, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x02,
+ "llbbw*b*b*w*b*b*w*b*b*w*l",
+ 0, 0x100, 0 /* pci bus? */, devfn, 0);
+}
+
+static
+size_t mptsas_config_io_unit_3(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(3, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x01,
+ "*b*b*w*l");
+}
+
+static
+size_t mptsas_config_io_unit_4(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(4, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x00, "*l*l*q");
+}
+
+/* I/O controller pages */
+
+static
+size_t mptsas_config_ioc_0(MPTSASState *s, uint8_t **data, int address)
+{
+ PCIDeviceClass *pcic = PCI_DEVICE_GET_CLASS(s);
+
+ return MPTSAS_CONFIG_PACK(0, MPI_CONFIG_PAGETYPE_IOC, 0x01,
+ "*l*lwwb*b*b*blww",
+ pcic->vendor_id, pcic->device_id, pcic->revision,
+ pcic->subsystem_vendor_id,
+ pcic->subsystem_id);
+}
+
+static
+size_t mptsas_config_ioc_1(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(1, MPI_CONFIG_PAGETYPE_IOC, 0x03,
+ "*l*l*b*b*b*b");
+}
+
+static
+size_t mptsas_config_ioc_2(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(2, MPI_CONFIG_PAGETYPE_IOC, 0x04,
+ "*l*b*b*b*b");
+}
+
+static
+size_t mptsas_config_ioc_3(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(3, MPI_CONFIG_PAGETYPE_IOC, 0x00,
+ "*b*b*w");
+}
+
+static
+size_t mptsas_config_ioc_4(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(4, MPI_CONFIG_PAGETYPE_IOC, 0x00,
+ "*b*b*w");
+}
+
+static
+size_t mptsas_config_ioc_5(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(5, MPI_CONFIG_PAGETYPE_IOC, 0x00,
+ "*l*b*b*w");
+}
+
+static
+size_t mptsas_config_ioc_6(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(6, MPI_CONFIG_PAGETYPE_IOC, 0x01,
+ "*l*b*b*b*b*b*b*b*b*b*b*w*l*l*l*l*b*b*w"
+ "*w*w*w*w*l*l*l");
+}
+
+/* SAS I/O unit pages (extended) */
+
+#define MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE 16
+
+#define MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION 0x02
+#define MPI_SAS_IOUNIT0_RATE_1_5 0x08
+#define MPI_SAS_IOUNIT0_RATE_3_0 0x09
+
+#define MPI_SAS_DEVICE_INFO_NO_DEVICE 0x00000000
+#define MPI_SAS_DEVICE_INFO_END_DEVICE 0x00000001
+#define MPI_SAS_DEVICE_INFO_SSP_TARGET 0x00000400
+
+#define MPI_SAS_DEVICE0_ASTATUS_NO_ERRORS 0x00
+
+#define MPI_SAS_DEVICE0_FLAGS_DEVICE_PRESENT 0x0001
+#define MPI_SAS_DEVICE0_FLAGS_DEVICE_MAPPED 0x0002
+#define MPI_SAS_DEVICE0_FLAGS_MAPPING_PERSISTENT 0x0004
+
+
+
+static SCSIDevice *mptsas_phy_get_device(MPTSASState *s, int i,
+ int *phy_handle, int *dev_handle)
+{
+ SCSIDevice *d = scsi_device_find(&s->bus, 0, i, 0);
+
+ if (phy_handle) {
+ *phy_handle = i + 1;
+ }
+ if (dev_handle) {
+ *dev_handle = d ? i + 1 + MPTSAS_NUM_PORTS : 0;
+ }
+ return d;
+}
+
+static
+size_t mptsas_config_sas_io_unit_0(MPTSASState *s, uint8_t **data, int address)
+{
+ size_t size = MPTSAS_CONFIG_PACK_EXT(0, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x04,
+ "*w*wb*b*w"
+ repl(MPTSAS_NUM_PORTS, "*s16"),
+ MPTSAS_NUM_PORTS);
+
+ if (data) {
+ size_t ofs = size - MPTSAS_NUM_PORTS * MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE;
+ int i;
+
+ for (i = 0; i < MPTSAS_NUM_PORTS; i++) {
+ int phy_handle, dev_handle;
+ SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle);
+
+ fill(*data + ofs, MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE,
+ "bbbblwwl", i, 0, 0,
+ (dev
+ ? MPI_SAS_IOUNIT0_RATE_3_0
+ : MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION),
+ (dev
+ ? MPI_SAS_DEVICE_INFO_END_DEVICE | MPI_SAS_DEVICE_INFO_SSP_TARGET
+ : MPI_SAS_DEVICE_INFO_NO_DEVICE),
+ dev_handle,
+ dev_handle,
+ 0);
+ ofs += MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE;
+ }
+ assert(ofs == size);
+ }
+ return size;
+}
+
+#define MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE 12
+
+static
+size_t mptsas_config_sas_io_unit_1(MPTSASState *s, uint8_t **data, int address)
+{
+ size_t size = MPTSAS_CONFIG_PACK_EXT(1, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x07,
+ "*w*w*w*wb*b*b*b"
+ repl(MPTSAS_NUM_PORTS, "*s12"),
+ MPTSAS_NUM_PORTS);
+
+ if (data) {
+ size_t ofs = size - MPTSAS_NUM_PORTS * MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE;
+ int i;
+
+ for (i = 0; i < MPTSAS_NUM_PORTS; i++) {
+ SCSIDevice *dev = mptsas_phy_get_device(s, i, NULL, NULL);
+ fill(*data + ofs, MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE,
+ "bbbblww", i, 0, 0,
+ (MPI_SAS_IOUNIT0_RATE_3_0 << 4) | MPI_SAS_IOUNIT0_RATE_1_5,
+ (dev
+ ? MPI_SAS_DEVICE_INFO_END_DEVICE | MPI_SAS_DEVICE_INFO_SSP_TARGET
+ : MPI_SAS_DEVICE_INFO_NO_DEVICE),
+ 0, 0);
+ ofs += MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE;
+ }
+ assert(ofs == size);
+ }
+ return size;
+}
+
+static
+size_t mptsas_config_sas_io_unit_2(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK_EXT(2, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x06,
+ "*b*b*w*w*w*b*b*w");
+}
+
+static
+size_t mptsas_config_sas_io_unit_3(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK_EXT(3, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x06,
+ "*l*l*l*l*l*l*l*l*l");
+}
+
+/* SAS PHY pages (extended) */
+
+static int mptsas_phy_addr_get(MPTSASState *s, int address)
+{
+ int i;
+ if ((address >> MPI_SAS_PHY_PGAD_FORM_SHIFT) == 0) {
+ i = address & 255;
+ } else if ((address >> MPI_SAS_PHY_PGAD_FORM_SHIFT) == 1) {
+ i = address & 65535;
+ } else {
+ return -EINVAL;
+ }
+
+ if (i >= MPTSAS_NUM_PORTS) {
+ return -EINVAL;
+ }
+
+ return i;
+}
+
+static
+size_t mptsas_config_phy_0(MPTSASState *s, uint8_t **data, int address)
+{
+ int phy_handle = -1;
+ int dev_handle = -1;
+ int i = mptsas_phy_addr_get(s, address);
+ SCSIDevice *dev;
+
+ if (i < 0) {
+ trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 0);
+ return i;
+ }
+
+ dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle);
+ trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 0);
+
+ return MPTSAS_CONFIG_PACK_EXT(0, MPI_CONFIG_EXTPAGETYPE_SAS_PHY, 0x01,
+ "w*wqwb*blbb*b*b*l",
+ dev_handle, s->sas_addr, dev_handle, i,
+ (dev
+ ? MPI_SAS_DEVICE_INFO_END_DEVICE /* | MPI_SAS_DEVICE_INFO_SSP_TARGET?? */
+ : MPI_SAS_DEVICE_INFO_NO_DEVICE),
+ (MPI_SAS_IOUNIT0_RATE_3_0 << 4) | MPI_SAS_IOUNIT0_RATE_1_5,
+ (MPI_SAS_IOUNIT0_RATE_3_0 << 4) | MPI_SAS_IOUNIT0_RATE_1_5);
+}
+
+static
+size_t mptsas_config_phy_1(MPTSASState *s, uint8_t **data, int address)
+{
+ int phy_handle = -1;
+ int dev_handle = -1;
+ int i = mptsas_phy_addr_get(s, address);
+
+ if (i < 0) {
+ trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 1);
+ return i;
+ }
+
+ (void) mptsas_phy_get_device(s, i, &phy_handle, &dev_handle);
+ trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 1);
+
+ return MPTSAS_CONFIG_PACK_EXT(1, MPI_CONFIG_EXTPAGETYPE_SAS_PHY, 0x01,
+ "*l*l*l*l*l");
+}
+
+/* SAS device pages (extended) */
+
+static int mptsas_device_addr_get(MPTSASState *s, int address)
+{
+ uint32_t handle, i;
+ uint32_t form = address >> MPI_SAS_PHY_PGAD_FORM_SHIFT;
+ if (form == MPI_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE) {
+ handle = address & MPI_SAS_DEVICE_PGAD_GNH_HANDLE_MASK;
+ do {
+ if (handle == 65535) {
+ handle = MPTSAS_NUM_PORTS + 1;
+ } else {
+ ++handle;
+ }
+ i = handle - 1 - MPTSAS_NUM_PORTS;
+ } while (i < MPTSAS_NUM_PORTS && !scsi_device_find(&s->bus, 0, i, 0));
+
+ } else if (form == MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID) {
+ if (address & MPI_SAS_DEVICE_PGAD_BT_BUS_MASK) {
+ return -EINVAL;
+ }
+ i = address & MPI_SAS_DEVICE_PGAD_BT_TID_MASK;
+
+ } else if (form == MPI_SAS_DEVICE_PGAD_FORM_HANDLE) {
+ handle = address & MPI_SAS_DEVICE_PGAD_H_HANDLE_MASK;
+ i = handle - 1 - MPTSAS_NUM_PORTS;
+
+ } else {
+ return -EINVAL;
+ }
+
+ if (i >= MPTSAS_NUM_PORTS) {
+ return -EINVAL;
+ }
+
+ return i;
+}
+
+static
+size_t mptsas_config_sas_device_0(MPTSASState *s, uint8_t **data, int address)
+{
+ int phy_handle = -1;
+ int dev_handle = -1;
+ int i = mptsas_device_addr_get(s, address);
+ SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle);
+
+ trace_mptsas_config_sas_device(s, address, i, phy_handle, dev_handle, 0);
+ if (!dev) {
+ return -ENOENT;
+ }
+
+ return MPTSAS_CONFIG_PACK_EXT(0, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0x05,
+ "*w*wqwbbwbblwb*b",
+ dev->wwn, phy_handle, i,
+ MPI_SAS_DEVICE0_ASTATUS_NO_ERRORS,
+ dev_handle, i, 0,
+ MPI_SAS_DEVICE_INFO_END_DEVICE | MPI_SAS_DEVICE_INFO_SSP_TARGET,
+ (MPI_SAS_DEVICE0_FLAGS_DEVICE_PRESENT |
+ MPI_SAS_DEVICE0_FLAGS_DEVICE_MAPPED |
+ MPI_SAS_DEVICE0_FLAGS_MAPPING_PERSISTENT), i);
+}
+
+static
+size_t mptsas_config_sas_device_1(MPTSASState *s, uint8_t **data, int address)
+{
+ int phy_handle = -1;
+ int dev_handle = -1;
+ int i = mptsas_device_addr_get(s, address);
+ SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle);
+
+ trace_mptsas_config_sas_device(s, address, i, phy_handle, dev_handle, 1);
+ if (!dev) {
+ return -ENOENT;
+ }
+
+ return MPTSAS_CONFIG_PACK_EXT(1, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0x00,
+ "*lq*lwbb*s20",
+ dev->wwn, dev_handle, i, 0);
+}
+
+static
+size_t mptsas_config_sas_device_2(MPTSASState *s, uint8_t **data, int address)
+{
+ int phy_handle = -1;
+ int dev_handle = -1;
+ int i = mptsas_device_addr_get(s, address);
+ SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle);
+
+ trace_mptsas_config_sas_device(s, address, i, phy_handle, dev_handle, 2);
+ if (!dev) {
+ return -ENOENT;
+ }
+
+ return MPTSAS_CONFIG_PACK_EXT(2, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0x01,
+ "ql", dev->wwn, 0);
+}
+
+typedef struct MPTSASConfigPage {
+ uint8_t number;
+ uint8_t type;
+ size_t (*mpt_config_build)(MPTSASState *s, uint8_t **data, int address);
+} MPTSASConfigPage;
+
+static const MPTSASConfigPage mptsas_config_pages[] = {
+ {
+ 0, MPI_CONFIG_PAGETYPE_MANUFACTURING,
+ mptsas_config_manufacturing_0,
+ }, {
+ 1, MPI_CONFIG_PAGETYPE_MANUFACTURING,
+ mptsas_config_manufacturing_1,
+ }, {
+ 2, MPI_CONFIG_PAGETYPE_MANUFACTURING,
+ mptsas_config_manufacturing_2,
+ }, {
+ 3, MPI_CONFIG_PAGETYPE_MANUFACTURING,
+ mptsas_config_manufacturing_3,
+ }, {
+ 4, MPI_CONFIG_PAGETYPE_MANUFACTURING,
+ mptsas_config_manufacturing_4,
+ }, {
+ 5, MPI_CONFIG_PAGETYPE_MANUFACTURING,
+ mptsas_config_manufacturing_5,
+ }, {
+ 6, MPI_CONFIG_PAGETYPE_MANUFACTURING,
+ mptsas_config_manufacturing_6,
+ }, {
+ 7, MPI_CONFIG_PAGETYPE_MANUFACTURING,
+ mptsas_config_manufacturing_7,
+ }, {
+ 8, MPI_CONFIG_PAGETYPE_MANUFACTURING,
+ mptsas_config_manufacturing_8,
+ }, {
+ 9, MPI_CONFIG_PAGETYPE_MANUFACTURING,
+ mptsas_config_manufacturing_9,
+ }, {
+ 10, MPI_CONFIG_PAGETYPE_MANUFACTURING,
+ mptsas_config_manufacturing_10,
+ }, {
+ 0, MPI_CONFIG_PAGETYPE_IO_UNIT,
+ mptsas_config_io_unit_0,
+ }, {
+ 1, MPI_CONFIG_PAGETYPE_IO_UNIT,
+ mptsas_config_io_unit_1,
+ }, {
+ 2, MPI_CONFIG_PAGETYPE_IO_UNIT,
+ mptsas_config_io_unit_2,
+ }, {
+ 3, MPI_CONFIG_PAGETYPE_IO_UNIT,
+ mptsas_config_io_unit_3,
+ }, {
+ 4, MPI_CONFIG_PAGETYPE_IO_UNIT,
+ mptsas_config_io_unit_4,
+ }, {
+ 0, MPI_CONFIG_PAGETYPE_IOC,
+ mptsas_config_ioc_0,
+ }, {
+ 1, MPI_CONFIG_PAGETYPE_IOC,
+ mptsas_config_ioc_1,
+ }, {
+ 2, MPI_CONFIG_PAGETYPE_IOC,
+ mptsas_config_ioc_2,
+ }, {
+ 3, MPI_CONFIG_PAGETYPE_IOC,
+ mptsas_config_ioc_3,
+ }, {
+ 4, MPI_CONFIG_PAGETYPE_IOC,
+ mptsas_config_ioc_4,
+ }, {
+ 5, MPI_CONFIG_PAGETYPE_IOC,
+ mptsas_config_ioc_5,
+ }, {
+ 6, MPI_CONFIG_PAGETYPE_IOC,
+ mptsas_config_ioc_6,
+ }, {
+ 0, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
+ mptsas_config_sas_io_unit_0,
+ }, {
+ 1, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
+ mptsas_config_sas_io_unit_1,
+ }, {
+ 2, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
+ mptsas_config_sas_io_unit_2,
+ }, {
+ 3, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
+ mptsas_config_sas_io_unit_3,
+ }, {
+ 0, MPI_CONFIG_EXTPAGETYPE_SAS_PHY,
+ mptsas_config_phy_0,
+ }, {
+ 1, MPI_CONFIG_EXTPAGETYPE_SAS_PHY,
+ mptsas_config_phy_1,
+ }, {
+ 0, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE,
+ mptsas_config_sas_device_0,
+ }, {
+ 1, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE,
+ mptsas_config_sas_device_1,
+ }, {
+ 2, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE,
+ mptsas_config_sas_device_2,
+ }
+};
+
+static const MPTSASConfigPage *mptsas_find_config_page(int type, int number)
+{
+ const MPTSASConfigPage *page;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mptsas_config_pages); i++) {
+ page = &mptsas_config_pages[i];
+ if (page->type == type && page->number == number) {
+ return page;
+ }
+ }
+
+ return NULL;
+}
+
+void mptsas_process_config(MPTSASState *s, MPIMsgConfig *req)
+{
+ PCIDevice *pci = PCI_DEVICE(s);
+
+ MPIMsgConfigReply reply;
+ const MPTSASConfigPage *page;
+ size_t length;
+ uint8_t type;
+ uint8_t *data = NULL;
+ uint32_t flags_and_length;
+ uint32_t dmalen;
+ uint64_t pa;
+
+ mptsas_fix_config_endianness(req);
+
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply));
+
+ /* Copy common bits from the request into the reply. */
+ memset(&reply, 0, sizeof(reply));
+ reply.Action = req->Action;
+ reply.Function = req->Function;
+ reply.MsgContext = req->MsgContext;
+ reply.MsgLength = sizeof(reply) / 4;
+ reply.PageType = req->PageType;
+ reply.PageNumber = req->PageNumber;
+ reply.PageLength = req->PageLength;
+ reply.PageVersion = req->PageVersion;
+
+ type = req->PageType & MPI_CONFIG_PAGETYPE_MASK;
+ if (type == MPI_CONFIG_PAGETYPE_EXTENDED) {
+ type = req->ExtPageType;
+ if (type <= MPI_CONFIG_PAGETYPE_MASK) {
+ reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_TYPE;
+ goto out;
+ }
+
+ reply.ExtPageType = req->ExtPageType;
+ }
+
+ page = mptsas_find_config_page(type, req->PageNumber);
+
+ switch(req->Action) {
+ case MPI_CONFIG_ACTION_PAGE_DEFAULT:
+ case MPI_CONFIG_ACTION_PAGE_HEADER:
+ case MPI_CONFIG_ACTION_PAGE_READ_NVRAM:
+ case MPI_CONFIG_ACTION_PAGE_READ_CURRENT:
+ case MPI_CONFIG_ACTION_PAGE_READ_DEFAULT:
+ case MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT:
+ case MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM:
+ break;
+
+ default:
+ reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_ACTION;
+ goto out;
+ }
+
+ if (!page) {
+ page = mptsas_find_config_page(type, 1);
+ if (page) {
+ reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE;
+ } else {
+ reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_TYPE;
+ }
+ goto out;
+ }
+
+ if (req->Action == MPI_CONFIG_ACTION_PAGE_DEFAULT ||
+ req->Action == MPI_CONFIG_ACTION_PAGE_HEADER) {
+ length = page->mpt_config_build(s, NULL, req->PageAddress);
+ if ((ssize_t)length < 0) {
+ reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE;
+ goto out;
+ } else {
+ goto done;
+ }
+ }
+
+ if (req->Action == MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT ||
+ req->Action == MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM) {
+ length = page->mpt_config_build(s, NULL, req->PageAddress);
+ if ((ssize_t)length < 0) {
+ reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE;
+ } else {
+ reply.IOCStatus = MPI_IOCSTATUS_CONFIG_CANT_COMMIT;
+ }
+ goto out;
+ }
+
+ flags_and_length = req->PageBufferSGE.FlagsLength;
+ dmalen = flags_and_length & MPI_SGE_LENGTH_MASK;
+ if (dmalen == 0) {
+ length = page->mpt_config_build(s, NULL, req->PageAddress);
+ if ((ssize_t)length < 0) {
+ reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE;
+ goto out;
+ } else {
+ goto done;
+ }
+ }
+
+ if (flags_and_length & MPI_SGE_FLAGS_64_BIT_ADDRESSING) {
+ pa = req->PageBufferSGE.u.Address64;
+ } else {
+ pa = req->PageBufferSGE.u.Address32;
+ }
+
+ /* Only read actions left. */
+ length = page->mpt_config_build(s, &data, req->PageAddress);
+ if ((ssize_t)length < 0) {
+ reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE;
+ goto out;
+ } else {
+ assert(data[2] == page->number);
+ pci_dma_write(pci, pa, data, MIN(length, dmalen));
+ goto done;
+ }
+
+ abort();
+
+done:
+ if (type > MPI_CONFIG_PAGETYPE_MASK) {
+ reply.ExtPageLength = length / 4;
+ reply.ExtPageType = req->ExtPageType;
+ } else {
+ reply.PageLength = length / 4;
+ }
+
+out:
+ mptsas_fix_config_reply_endianness(&reply);
+ mptsas_reply(s, (MPIDefaultReply *)&reply);
+ g_free(data);
+}
diff --git a/qemu/hw/scsi/mptendian.c b/qemu/hw/scsi/mptendian.c
new file mode 100644
index 000000000..b7fe2a2a3
--- /dev/null
+++ b/qemu/hw/scsi/mptendian.c
@@ -0,0 +1,204 @@
+/*
+ * QEMU LSI SAS1068 Host Bus Adapter emulation
+ * Endianness conversion for MPI data structures
+ *
+ * Copyright (c) 2016 Red Hat, Inc.
+ *
+ * Authors: Paolo Bonzini <pbonzini@redhat.com>
+ *
+ * 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 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "sysemu/dma.h"
+#include "sysemu/block-backend.h"
+#include "hw/pci/msi.h"
+#include "qemu/iov.h"
+#include "hw/scsi/scsi.h"
+#include "block/scsi.h"
+#include "trace.h"
+
+#include "mptsas.h"
+#include "mpi.h"
+
+static void mptsas_fix_sgentry_endianness(MPISGEntry *sge)
+{
+ le32_to_cpus(&sge->FlagsLength);
+ if (sge->FlagsLength & MPI_SGE_FLAGS_64_BIT_ADDRESSING) {
+ le64_to_cpus(&sge->u.Address64);
+ } else {
+ le32_to_cpus(&sge->u.Address32);
+ }
+}
+
+static void mptsas_fix_sgentry_endianness_reply(MPISGEntry *sge)
+{
+ if (sge->FlagsLength & MPI_SGE_FLAGS_64_BIT_ADDRESSING) {
+ cpu_to_le64s(&sge->u.Address64);
+ } else {
+ cpu_to_le32s(&sge->u.Address32);
+ }
+ cpu_to_le32s(&sge->FlagsLength);
+}
+
+void mptsas_fix_scsi_io_endianness(MPIMsgSCSIIORequest *req)
+{
+ le32_to_cpus(&req->MsgContext);
+ le32_to_cpus(&req->Control);
+ le32_to_cpus(&req->DataLength);
+ le32_to_cpus(&req->SenseBufferLowAddr);
+}
+
+void mptsas_fix_scsi_io_reply_endianness(MPIMsgSCSIIOReply *reply)
+{
+ cpu_to_le32s(&reply->MsgContext);
+ cpu_to_le16s(&reply->IOCStatus);
+ cpu_to_le32s(&reply->IOCLogInfo);
+ cpu_to_le32s(&reply->TransferCount);
+ cpu_to_le32s(&reply->SenseCount);
+ cpu_to_le32s(&reply->ResponseInfo);
+ cpu_to_le16s(&reply->TaskTag);
+}
+
+void mptsas_fix_scsi_task_mgmt_endianness(MPIMsgSCSITaskMgmt *req)
+{
+ le32_to_cpus(&req->MsgContext);
+ le32_to_cpus(&req->TaskMsgContext);
+}
+
+void mptsas_fix_scsi_task_mgmt_reply_endianness(MPIMsgSCSITaskMgmtReply *reply)
+{
+ cpu_to_le32s(&reply->MsgContext);
+ cpu_to_le16s(&reply->IOCStatus);
+ cpu_to_le32s(&reply->IOCLogInfo);
+ cpu_to_le32s(&reply->TerminationCount);
+}
+
+void mptsas_fix_ioc_init_endianness(MPIMsgIOCInit *req)
+{
+ le32_to_cpus(&req->MsgContext);
+ le16_to_cpus(&req->ReplyFrameSize);
+ le32_to_cpus(&req->HostMfaHighAddr);
+ le32_to_cpus(&req->SenseBufferHighAddr);
+ le32_to_cpus(&req->ReplyFifoHostSignalingAddr);
+ mptsas_fix_sgentry_endianness(&req->HostPageBufferSGE);
+ le16_to_cpus(&req->MsgVersion);
+ le16_to_cpus(&req->HeaderVersion);
+}
+
+void mptsas_fix_ioc_init_reply_endianness(MPIMsgIOCInitReply *reply)
+{
+ cpu_to_le32s(&reply->MsgContext);
+ cpu_to_le16s(&reply->IOCStatus);
+ cpu_to_le32s(&reply->IOCLogInfo);
+}
+
+void mptsas_fix_ioc_facts_endianness(MPIMsgIOCFacts *req)
+{
+ le32_to_cpus(&req->MsgContext);
+}
+
+void mptsas_fix_ioc_facts_reply_endianness(MPIMsgIOCFactsReply *reply)
+{
+ cpu_to_le16s(&reply->MsgVersion);
+ cpu_to_le16s(&reply->HeaderVersion);
+ cpu_to_le32s(&reply->MsgContext);
+ cpu_to_le16s(&reply->IOCExceptions);
+ cpu_to_le16s(&reply->IOCStatus);
+ cpu_to_le32s(&reply->IOCLogInfo);
+ cpu_to_le16s(&reply->ReplyQueueDepth);
+ cpu_to_le16s(&reply->RequestFrameSize);
+ cpu_to_le16s(&reply->ProductID);
+ cpu_to_le32s(&reply->CurrentHostMfaHighAddr);
+ cpu_to_le16s(&reply->GlobalCredits);
+ cpu_to_le32s(&reply->CurrentSenseBufferHighAddr);
+ cpu_to_le16s(&reply->CurReplyFrameSize);
+ cpu_to_le32s(&reply->FWImageSize);
+ cpu_to_le32s(&reply->IOCCapabilities);
+ cpu_to_le16s(&reply->HighPriorityQueueDepth);
+ mptsas_fix_sgentry_endianness_reply(&reply->HostPageBufferSGE);
+ cpu_to_le32s(&reply->ReplyFifoHostSignalingAddr);
+}
+
+void mptsas_fix_config_endianness(MPIMsgConfig *req)
+{
+ le16_to_cpus(&req->ExtPageLength);
+ le32_to_cpus(&req->MsgContext);
+ le32_to_cpus(&req->PageAddress);
+ mptsas_fix_sgentry_endianness(&req->PageBufferSGE);
+}
+
+void mptsas_fix_config_reply_endianness(MPIMsgConfigReply *reply)
+{
+ cpu_to_le16s(&reply->ExtPageLength);
+ cpu_to_le32s(&reply->MsgContext);
+ cpu_to_le16s(&reply->IOCStatus);
+ cpu_to_le32s(&reply->IOCLogInfo);
+}
+
+void mptsas_fix_port_facts_endianness(MPIMsgPortFacts *req)
+{
+ le32_to_cpus(&req->MsgContext);
+}
+
+void mptsas_fix_port_facts_reply_endianness(MPIMsgPortFactsReply *reply)
+{
+ cpu_to_le32s(&reply->MsgContext);
+ cpu_to_le16s(&reply->IOCStatus);
+ cpu_to_le32s(&reply->IOCLogInfo);
+ cpu_to_le16s(&reply->MaxDevices);
+ cpu_to_le16s(&reply->PortSCSIID);
+ cpu_to_le16s(&reply->ProtocolFlags);
+ cpu_to_le16s(&reply->MaxPostedCmdBuffers);
+ cpu_to_le16s(&reply->MaxPersistentIDs);
+ cpu_to_le16s(&reply->MaxLanBuckets);
+}
+
+void mptsas_fix_port_enable_endianness(MPIMsgPortEnable *req)
+{
+ le32_to_cpus(&req->MsgContext);
+}
+
+void mptsas_fix_port_enable_reply_endianness(MPIMsgPortEnableReply *reply)
+{
+ cpu_to_le32s(&reply->MsgContext);
+ cpu_to_le16s(&reply->IOCStatus);
+ cpu_to_le32s(&reply->IOCLogInfo);
+}
+
+void mptsas_fix_event_notification_endianness(MPIMsgEventNotify *req)
+{
+ le32_to_cpus(&req->MsgContext);
+}
+
+void mptsas_fix_event_notification_reply_endianness(MPIMsgEventNotifyReply *reply)
+{
+ int length = reply->EventDataLength;
+ int i;
+
+ cpu_to_le16s(&reply->EventDataLength);
+ cpu_to_le32s(&reply->MsgContext);
+ cpu_to_le16s(&reply->IOCStatus);
+ cpu_to_le32s(&reply->IOCLogInfo);
+ cpu_to_le32s(&reply->Event);
+ cpu_to_le32s(&reply->EventContext);
+
+ /* Really depends on the event kind. This will do for now. */
+ for (i = 0; i < length; i++) {
+ cpu_to_le32s(&reply->Data[i]);
+ }
+}
+
diff --git a/qemu/hw/scsi/mptsas.c b/qemu/hw/scsi/mptsas.c
new file mode 100644
index 000000000..499c1465a
--- /dev/null
+++ b/qemu/hw/scsi/mptsas.c
@@ -0,0 +1,1442 @@
+/*
+ * QEMU LSI SAS1068 Host Bus Adapter emulation
+ * Based on the QEMU Megaraid emulator
+ *
+ * Copyright (c) 2009-2012 Hannes Reinecke, SUSE Labs
+ * Copyright (c) 2012 Verizon, Inc.
+ * Copyright (c) 2016 Red Hat, Inc.
+ *
+ * Authors: Don Slutz, Paolo Bonzini
+ *
+ * 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 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "sysemu/dma.h"
+#include "sysemu/block-backend.h"
+#include "hw/pci/msi.h"
+#include "qemu/iov.h"
+#include "hw/scsi/scsi.h"
+#include "block/scsi.h"
+#include "trace.h"
+
+#include "mptsas.h"
+#include "mpi.h"
+
+#define NAA_LOCALLY_ASSIGNED_ID 0x3ULL
+#define IEEE_COMPANY_LOCALLY_ASSIGNED 0x525400
+
+#define TYPE_MPTSAS1068 "mptsas1068"
+
+#define MPT_SAS(obj) \
+ OBJECT_CHECK(MPTSASState, (obj), TYPE_MPTSAS1068)
+
+#define MPTSAS1068_PRODUCT_ID \
+ (MPI_FW_HEADER_PID_FAMILY_1068_SAS | \
+ MPI_FW_HEADER_PID_PROD_INITIATOR_SCSI | \
+ MPI_FW_HEADER_PID_TYPE_SAS)
+
+struct MPTSASRequest {
+ MPIMsgSCSIIORequest scsi_io;
+ SCSIRequest *sreq;
+ QEMUSGList qsg;
+ MPTSASState *dev;
+
+ QTAILQ_ENTRY(MPTSASRequest) next;
+};
+
+static void mptsas_update_interrupt(MPTSASState *s)
+{
+ PCIDevice *pci = (PCIDevice *) s;
+ uint32_t state = s->intr_status & ~(s->intr_mask | MPI_HIS_IOP_DOORBELL_STATUS);
+
+ if (s->msi_in_use && msi_enabled(pci)) {
+ if (state) {
+ trace_mptsas_irq_msi(s);
+ msi_notify(pci, 0);
+ }
+ }
+
+ trace_mptsas_irq_intx(s, !!state);
+ pci_set_irq(pci, !!state);
+}
+
+static void mptsas_set_fault(MPTSASState *s, uint32_t code)
+{
+ if ((s->state & MPI_IOC_STATE_FAULT) == 0) {
+ s->state = MPI_IOC_STATE_FAULT | code;
+ }
+}
+
+#define MPTSAS_FIFO_INVALID(s, name) \
+ ((s)->name##_head > ARRAY_SIZE((s)->name) || \
+ (s)->name##_tail > ARRAY_SIZE((s)->name))
+
+#define MPTSAS_FIFO_EMPTY(s, name) \
+ ((s)->name##_head == (s)->name##_tail)
+
+#define MPTSAS_FIFO_FULL(s, name) \
+ ((s)->name##_head == ((s)->name##_tail + 1) % ARRAY_SIZE((s)->name))
+
+#define MPTSAS_FIFO_GET(s, name) ({ \
+ uint32_t _val = (s)->name[(s)->name##_head++]; \
+ (s)->name##_head %= ARRAY_SIZE((s)->name); \
+ _val; \
+})
+
+#define MPTSAS_FIFO_PUT(s, name, val) do { \
+ (s)->name[(s)->name##_tail++] = (val); \
+ (s)->name##_tail %= ARRAY_SIZE((s)->name); \
+} while(0)
+
+static void mptsas_post_reply(MPTSASState *s, MPIDefaultReply *reply)
+{
+ PCIDevice *pci = (PCIDevice *) s;
+ uint32_t addr_lo;
+
+ if (MPTSAS_FIFO_EMPTY(s, reply_free) || MPTSAS_FIFO_FULL(s, reply_post)) {
+ mptsas_set_fault(s, MPI_IOCSTATUS_INSUFFICIENT_RESOURCES);
+ return;
+ }
+
+ addr_lo = MPTSAS_FIFO_GET(s, reply_free);
+
+ pci_dma_write(pci, addr_lo | s->host_mfa_high_addr, reply,
+ MIN(s->reply_frame_size, 4 * reply->MsgLength));
+
+ MPTSAS_FIFO_PUT(s, reply_post, MPI_ADDRESS_REPLY_A_BIT | (addr_lo >> 1));
+
+ s->intr_status |= MPI_HIS_REPLY_MESSAGE_INTERRUPT;
+ if (s->doorbell_state == DOORBELL_WRITE) {
+ s->doorbell_state = DOORBELL_NONE;
+ s->intr_status |= MPI_HIS_DOORBELL_INTERRUPT;
+ }
+ mptsas_update_interrupt(s);
+}
+
+void mptsas_reply(MPTSASState *s, MPIDefaultReply *reply)
+{
+ if (s->doorbell_state == DOORBELL_WRITE) {
+ /* The reply is sent out in 16 bit chunks, while the size
+ * in the reply is in 32 bit units.
+ */
+ s->doorbell_state = DOORBELL_READ;
+ s->doorbell_reply_idx = 0;
+ s->doorbell_reply_size = reply->MsgLength * 2;
+ memcpy(s->doorbell_reply, reply, s->doorbell_reply_size * 2);
+ s->intr_status |= MPI_HIS_DOORBELL_INTERRUPT;
+ mptsas_update_interrupt(s);
+ } else {
+ mptsas_post_reply(s, reply);
+ }
+}
+
+static void mptsas_turbo_reply(MPTSASState *s, uint32_t msgctx)
+{
+ if (MPTSAS_FIFO_FULL(s, reply_post)) {
+ mptsas_set_fault(s, MPI_IOCSTATUS_INSUFFICIENT_RESOURCES);
+ return;
+ }
+
+ /* The reply is just the message context ID (bit 31 = clear). */
+ MPTSAS_FIFO_PUT(s, reply_post, msgctx);
+
+ s->intr_status |= MPI_HIS_REPLY_MESSAGE_INTERRUPT;
+ mptsas_update_interrupt(s);
+}
+
+#define MPTSAS_MAX_REQUEST_SIZE 52
+
+static const int mpi_request_sizes[] = {
+ [MPI_FUNCTION_SCSI_IO_REQUEST] = sizeof(MPIMsgSCSIIORequest),
+ [MPI_FUNCTION_SCSI_TASK_MGMT] = sizeof(MPIMsgSCSITaskMgmt),
+ [MPI_FUNCTION_IOC_INIT] = sizeof(MPIMsgIOCInit),
+ [MPI_FUNCTION_IOC_FACTS] = sizeof(MPIMsgIOCFacts),
+ [MPI_FUNCTION_CONFIG] = sizeof(MPIMsgConfig),
+ [MPI_FUNCTION_PORT_FACTS] = sizeof(MPIMsgPortFacts),
+ [MPI_FUNCTION_PORT_ENABLE] = sizeof(MPIMsgPortEnable),
+ [MPI_FUNCTION_EVENT_NOTIFICATION] = sizeof(MPIMsgEventNotify),
+};
+
+static dma_addr_t mptsas_ld_sg_base(MPTSASState *s, uint32_t flags_and_length,
+ dma_addr_t *sgaddr)
+{
+ PCIDevice *pci = (PCIDevice *) s;
+ dma_addr_t addr;
+
+ if (flags_and_length & MPI_SGE_FLAGS_64_BIT_ADDRESSING) {
+ addr = ldq_le_pci_dma(pci, *sgaddr + 4);
+ *sgaddr += 12;
+ } else {
+ addr = ldl_le_pci_dma(pci, *sgaddr + 4);
+ *sgaddr += 8;
+ }
+ return addr;
+}
+
+static int mptsas_build_sgl(MPTSASState *s, MPTSASRequest *req, hwaddr addr)
+{
+ PCIDevice *pci = (PCIDevice *) s;
+ hwaddr next_chain_addr;
+ uint32_t left;
+ hwaddr sgaddr;
+ uint32_t chain_offset;
+
+ chain_offset = req->scsi_io.ChainOffset;
+ next_chain_addr = addr + chain_offset * sizeof(uint32_t);
+ sgaddr = addr + sizeof(MPIMsgSCSIIORequest);
+ pci_dma_sglist_init(&req->qsg, pci, 4);
+ left = req->scsi_io.DataLength;
+
+ for(;;) {
+ dma_addr_t addr, len;
+ uint32_t flags_and_length;
+
+ flags_and_length = ldl_le_pci_dma(pci, sgaddr);
+ len = flags_and_length & MPI_SGE_LENGTH_MASK;
+ if ((flags_and_length & MPI_SGE_FLAGS_ELEMENT_TYPE_MASK)
+ != MPI_SGE_FLAGS_SIMPLE_ELEMENT ||
+ (!len &&
+ !(flags_and_length & MPI_SGE_FLAGS_END_OF_LIST) &&
+ !(flags_and_length & MPI_SGE_FLAGS_END_OF_BUFFER))) {
+ return MPI_IOCSTATUS_INVALID_SGL;
+ }
+
+ len = MIN(len, left);
+ if (!len) {
+ /* We reached the desired transfer length, ignore extra
+ * elements of the s/g list.
+ */
+ break;
+ }
+
+ addr = mptsas_ld_sg_base(s, flags_and_length, &sgaddr);
+ qemu_sglist_add(&req->qsg, addr, len);
+ left -= len;
+
+ if (flags_and_length & MPI_SGE_FLAGS_END_OF_LIST) {
+ break;
+ }
+
+ if (flags_and_length & MPI_SGE_FLAGS_LAST_ELEMENT) {
+ if (!chain_offset) {
+ break;
+ }
+
+ flags_and_length = ldl_le_pci_dma(pci, next_chain_addr);
+ if ((flags_and_length & MPI_SGE_FLAGS_ELEMENT_TYPE_MASK)
+ != MPI_SGE_FLAGS_CHAIN_ELEMENT) {
+ return MPI_IOCSTATUS_INVALID_SGL;
+ }
+
+ sgaddr = mptsas_ld_sg_base(s, flags_and_length, &next_chain_addr);
+ chain_offset =
+ (flags_and_length & MPI_SGE_CHAIN_OFFSET_MASK) >> MPI_SGE_CHAIN_OFFSET_SHIFT;
+ next_chain_addr = sgaddr + chain_offset * sizeof(uint32_t);
+ }
+ }
+ return 0;
+}
+
+static void mptsas_free_request(MPTSASRequest *req)
+{
+ MPTSASState *s = req->dev;
+
+ if (req->sreq != NULL) {
+ req->sreq->hba_private = NULL;
+ scsi_req_unref(req->sreq);
+ req->sreq = NULL;
+ QTAILQ_REMOVE(&s->pending, req, next);
+ }
+ qemu_sglist_destroy(&req->qsg);
+ g_free(req);
+}
+
+static int mptsas_scsi_device_find(MPTSASState *s, int bus, int target,
+ uint8_t *lun, SCSIDevice **sdev)
+{
+ if (bus != 0) {
+ return MPI_IOCSTATUS_SCSI_INVALID_BUS;
+ }
+
+ if (target >= s->max_devices) {
+ return MPI_IOCSTATUS_SCSI_INVALID_TARGETID;
+ }
+
+ *sdev = scsi_device_find(&s->bus, bus, target, lun[1]);
+ if (!*sdev) {
+ return MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE;
+ }
+
+ return 0;
+}
+
+static int mptsas_process_scsi_io_request(MPTSASState *s,
+ MPIMsgSCSIIORequest *scsi_io,
+ hwaddr addr)
+{
+ MPTSASRequest *req;
+ MPIMsgSCSIIOReply reply;
+ SCSIDevice *sdev;
+ int status;
+
+ mptsas_fix_scsi_io_endianness(scsi_io);
+
+ trace_mptsas_process_scsi_io_request(s, scsi_io->Bus, scsi_io->TargetID,
+ scsi_io->LUN[1], scsi_io->DataLength);
+
+ status = mptsas_scsi_device_find(s, scsi_io->Bus, scsi_io->TargetID,
+ scsi_io->LUN, &sdev);
+ if (status) {
+ goto bad;
+ }
+
+ req = g_new(MPTSASRequest, 1);
+ QTAILQ_INSERT_TAIL(&s->pending, req, next);
+ req->scsi_io = *scsi_io;
+ req->dev = s;
+
+ status = mptsas_build_sgl(s, req, addr);
+ if (status) {
+ goto free_bad;
+ }
+
+ if (req->qsg.size < scsi_io->DataLength) {
+ trace_mptsas_sgl_overflow(s, scsi_io->MsgContext, scsi_io->DataLength,
+ req->qsg.size);
+ status = MPI_IOCSTATUS_INVALID_SGL;
+ goto free_bad;
+ }
+
+ req->sreq = scsi_req_new(sdev, scsi_io->MsgContext,
+ scsi_io->LUN[1], scsi_io->CDB, req);
+
+ if (req->sreq->cmd.xfer > scsi_io->DataLength) {
+ goto overrun;
+ }
+ switch (scsi_io->Control & MPI_SCSIIO_CONTROL_DATADIRECTION_MASK) {
+ case MPI_SCSIIO_CONTROL_NODATATRANSFER:
+ if (req->sreq->cmd.mode != SCSI_XFER_NONE) {
+ goto overrun;
+ }
+ break;
+
+ case MPI_SCSIIO_CONTROL_WRITE:
+ if (req->sreq->cmd.mode != SCSI_XFER_TO_DEV) {
+ goto overrun;
+ }
+ break;
+
+ case MPI_SCSIIO_CONTROL_READ:
+ if (req->sreq->cmd.mode != SCSI_XFER_FROM_DEV) {
+ goto overrun;
+ }
+ break;
+ }
+
+ if (scsi_req_enqueue(req->sreq)) {
+ scsi_req_continue(req->sreq);
+ }
+ return 0;
+
+overrun:
+ trace_mptsas_scsi_overflow(s, scsi_io->MsgContext, req->sreq->cmd.xfer,
+ scsi_io->DataLength);
+ status = MPI_IOCSTATUS_SCSI_DATA_OVERRUN;
+free_bad:
+ mptsas_free_request(req);
+bad:
+ memset(&reply, 0, sizeof(reply));
+ reply.TargetID = scsi_io->TargetID;
+ reply.Bus = scsi_io->Bus;
+ reply.MsgLength = sizeof(reply) / 4;
+ reply.Function = scsi_io->Function;
+ reply.CDBLength = scsi_io->CDBLength;
+ reply.SenseBufferLength = scsi_io->SenseBufferLength;
+ reply.MsgContext = scsi_io->MsgContext;
+ reply.SCSIState = MPI_SCSI_STATE_NO_SCSI_STATUS;
+ reply.IOCStatus = status;
+
+ mptsas_fix_scsi_io_reply_endianness(&reply);
+ mptsas_reply(s, (MPIDefaultReply *)&reply);
+
+ return 0;
+}
+
+typedef struct {
+ Notifier notifier;
+ MPTSASState *s;
+ MPIMsgSCSITaskMgmtReply *reply;
+} MPTSASCancelNotifier;
+
+static void mptsas_cancel_notify(Notifier *notifier, void *data)
+{
+ MPTSASCancelNotifier *n = container_of(notifier,
+ MPTSASCancelNotifier,
+ notifier);
+
+ /* Abusing IOCLogInfo to store the expected number of requests... */
+ if (++n->reply->TerminationCount == n->reply->IOCLogInfo) {
+ n->reply->IOCLogInfo = 0;
+ mptsas_fix_scsi_task_mgmt_reply_endianness(n->reply);
+ mptsas_post_reply(n->s, (MPIDefaultReply *)n->reply);
+ g_free(n->reply);
+ }
+ g_free(n);
+}
+
+static void mptsas_process_scsi_task_mgmt(MPTSASState *s, MPIMsgSCSITaskMgmt *req)
+{
+ MPIMsgSCSITaskMgmtReply reply;
+ MPIMsgSCSITaskMgmtReply *reply_async;
+ int status, count;
+ SCSIDevice *sdev;
+ SCSIRequest *r, *next;
+ BusChild *kid;
+
+ mptsas_fix_scsi_task_mgmt_endianness(req);
+
+ QEMU_BUILD_BUG_ON(MPTSAS_MAX_REQUEST_SIZE < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply));
+
+ memset(&reply, 0, sizeof(reply));
+ reply.TargetID = req->TargetID;
+ reply.Bus = req->Bus;
+ reply.MsgLength = sizeof(reply) / 4;
+ reply.Function = req->Function;
+ reply.TaskType = req->TaskType;
+ reply.MsgContext = req->MsgContext;
+
+ switch (req->TaskType) {
+ case MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK:
+ case MPI_SCSITASKMGMT_TASKTYPE_QUERY_TASK:
+ status = mptsas_scsi_device_find(s, req->Bus, req->TargetID,
+ req->LUN, &sdev);
+ if (status) {
+ reply.IOCStatus = status;
+ goto out;
+ }
+ if (sdev->lun != req->LUN[1]) {
+ reply.ResponseCode = MPI_SCSITASKMGMT_RSP_TM_INVALID_LUN;
+ goto out;
+ }
+
+ QTAILQ_FOREACH_SAFE(r, &sdev->requests, next, next) {
+ MPTSASRequest *cmd_req = r->hba_private;
+ if (cmd_req && cmd_req->scsi_io.MsgContext == req->TaskMsgContext) {
+ break;
+ }
+ }
+ if (r) {
+ /*
+ * Assert that the request has not been completed yet, we
+ * check for it in the loop above.
+ */
+ assert(r->hba_private);
+ if (req->TaskType == MPI_SCSITASKMGMT_TASKTYPE_QUERY_TASK) {
+ /* "If the specified command is present in the task set, then
+ * return a service response set to FUNCTION SUCCEEDED".
+ */
+ reply.ResponseCode = MPI_SCSITASKMGMT_RSP_TM_SUCCEEDED;
+ } else {
+ MPTSASCancelNotifier *notifier;
+
+ reply_async = g_memdup(&reply, sizeof(MPIMsgSCSITaskMgmtReply));
+ reply_async->IOCLogInfo = INT_MAX;
+
+ count = 1;
+ notifier = g_new(MPTSASCancelNotifier, 1);
+ notifier->s = s;
+ notifier->reply = reply_async;
+ notifier->notifier.notify = mptsas_cancel_notify;
+ scsi_req_cancel_async(r, &notifier->notifier);
+ goto reply_maybe_async;
+ }
+ }
+ break;
+
+ case MPI_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET:
+ case MPI_SCSITASKMGMT_TASKTYPE_CLEAR_TASK_SET:
+ status = mptsas_scsi_device_find(s, req->Bus, req->TargetID,
+ req->LUN, &sdev);
+ if (status) {
+ reply.IOCStatus = status;
+ goto out;
+ }
+ if (sdev->lun != req->LUN[1]) {
+ reply.ResponseCode = MPI_SCSITASKMGMT_RSP_TM_INVALID_LUN;
+ goto out;
+ }
+
+ reply_async = g_memdup(&reply, sizeof(MPIMsgSCSITaskMgmtReply));
+ reply_async->IOCLogInfo = INT_MAX;
+
+ count = 0;
+ QTAILQ_FOREACH_SAFE(r, &sdev->requests, next, next) {
+ if (r->hba_private) {
+ MPTSASCancelNotifier *notifier;
+
+ count++;
+ notifier = g_new(MPTSASCancelNotifier, 1);
+ notifier->s = s;
+ notifier->reply = reply_async;
+ notifier->notifier.notify = mptsas_cancel_notify;
+ scsi_req_cancel_async(r, &notifier->notifier);
+ }
+ }
+
+reply_maybe_async:
+ if (reply_async->TerminationCount < count) {
+ reply_async->IOCLogInfo = count;
+ return;
+ }
+ g_free(reply_async);
+ reply.TerminationCount = count;
+ break;
+
+ case MPI_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET:
+ status = mptsas_scsi_device_find(s, req->Bus, req->TargetID,
+ req->LUN, &sdev);
+ if (status) {
+ reply.IOCStatus = status;
+ goto out;
+ }
+ if (sdev->lun != req->LUN[1]) {
+ reply.ResponseCode = MPI_SCSITASKMGMT_RSP_TM_INVALID_LUN;
+ goto out;
+ }
+ qdev_reset_all(&sdev->qdev);
+ break;
+
+ case MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET:
+ if (req->Bus != 0) {
+ reply.IOCStatus = MPI_IOCSTATUS_SCSI_INVALID_BUS;
+ goto out;
+ }
+ if (req->TargetID > s->max_devices) {
+ reply.IOCStatus = MPI_IOCSTATUS_SCSI_INVALID_TARGETID;
+ goto out;
+ }
+
+ QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
+ sdev = SCSI_DEVICE(kid->child);
+ if (sdev->channel == 0 && sdev->id == req->TargetID) {
+ qdev_reset_all(kid->child);
+ }
+ }
+ break;
+
+ case MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS:
+ qbus_reset_all(&s->bus.qbus);
+ break;
+
+ default:
+ reply.ResponseCode = MPI_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED;
+ break;
+ }
+
+out:
+ mptsas_fix_scsi_task_mgmt_reply_endianness(&reply);
+ mptsas_post_reply(s, (MPIDefaultReply *)&reply);
+}
+
+static void mptsas_process_ioc_init(MPTSASState *s, MPIMsgIOCInit *req)
+{
+ MPIMsgIOCInitReply reply;
+
+ mptsas_fix_ioc_init_endianness(req);
+
+ QEMU_BUILD_BUG_ON(MPTSAS_MAX_REQUEST_SIZE < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply));
+
+ s->who_init = req->WhoInit;
+ s->reply_frame_size = req->ReplyFrameSize;
+ s->max_buses = req->MaxBuses;
+ s->max_devices = req->MaxDevices ? req->MaxDevices : 256;
+ s->host_mfa_high_addr = (hwaddr)req->HostMfaHighAddr << 32;
+ s->sense_buffer_high_addr = (hwaddr)req->SenseBufferHighAddr << 32;
+
+ if (s->state == MPI_IOC_STATE_READY) {
+ s->state = MPI_IOC_STATE_OPERATIONAL;
+ }
+
+ memset(&reply, 0, sizeof(reply));
+ reply.WhoInit = s->who_init;
+ reply.MsgLength = sizeof(reply) / 4;
+ reply.Function = req->Function;
+ reply.MaxDevices = s->max_devices;
+ reply.MaxBuses = s->max_buses;
+ reply.MsgContext = req->MsgContext;
+
+ mptsas_fix_ioc_init_reply_endianness(&reply);
+ mptsas_reply(s, (MPIDefaultReply *)&reply);
+}
+
+static void mptsas_process_ioc_facts(MPTSASState *s,
+ MPIMsgIOCFacts *req)
+{
+ MPIMsgIOCFactsReply reply;
+
+ mptsas_fix_ioc_facts_endianness(req);
+
+ QEMU_BUILD_BUG_ON(MPTSAS_MAX_REQUEST_SIZE < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply));
+
+ memset(&reply, 0, sizeof(reply));
+ reply.MsgVersion = 0x0105;
+ reply.MsgLength = sizeof(reply) / 4;
+ reply.Function = req->Function;
+ reply.MsgContext = req->MsgContext;
+ reply.MaxChainDepth = MPTSAS_MAXIMUM_CHAIN_DEPTH;
+ reply.WhoInit = s->who_init;
+ reply.BlockSize = MPTSAS_MAX_REQUEST_SIZE / sizeof(uint32_t);
+ reply.ReplyQueueDepth = ARRAY_SIZE(s->reply_post) - 1;
+ QEMU_BUILD_BUG_ON(ARRAY_SIZE(s->reply_post) != ARRAY_SIZE(s->reply_free));
+
+ reply.RequestFrameSize = 128;
+ reply.ProductID = MPTSAS1068_PRODUCT_ID;
+ reply.CurrentHostMfaHighAddr = s->host_mfa_high_addr >> 32;
+ reply.GlobalCredits = ARRAY_SIZE(s->request_post) - 1;
+ reply.NumberOfPorts = MPTSAS_NUM_PORTS;
+ reply.CurrentSenseBufferHighAddr = s->sense_buffer_high_addr >> 32;
+ reply.CurReplyFrameSize = s->reply_frame_size;
+ reply.MaxDevices = s->max_devices;
+ reply.MaxBuses = s->max_buses;
+ reply.FWVersionDev = 0;
+ reply.FWVersionUnit = 0x92;
+ reply.FWVersionMinor = 0x32;
+ reply.FWVersionMajor = 0x1;
+
+ mptsas_fix_ioc_facts_reply_endianness(&reply);
+ mptsas_reply(s, (MPIDefaultReply *)&reply);
+}
+
+static void mptsas_process_port_facts(MPTSASState *s,
+ MPIMsgPortFacts *req)
+{
+ MPIMsgPortFactsReply reply;
+
+ mptsas_fix_port_facts_endianness(req);
+
+ QEMU_BUILD_BUG_ON(MPTSAS_MAX_REQUEST_SIZE < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply));
+
+ memset(&reply, 0, sizeof(reply));
+ reply.MsgLength = sizeof(reply) / 4;
+ reply.Function = req->Function;
+ reply.PortNumber = req->PortNumber;
+ reply.MsgContext = req->MsgContext;
+
+ if (req->PortNumber < MPTSAS_NUM_PORTS) {
+ reply.PortType = MPI_PORTFACTS_PORTTYPE_SAS;
+ reply.MaxDevices = MPTSAS_NUM_PORTS;
+ reply.PortSCSIID = MPTSAS_NUM_PORTS;
+ reply.ProtocolFlags = MPI_PORTFACTS_PROTOCOL_LOGBUSADDR | MPI_PORTFACTS_PROTOCOL_INITIATOR;
+ }
+
+ mptsas_fix_port_facts_reply_endianness(&reply);
+ mptsas_reply(s, (MPIDefaultReply *)&reply);
+}
+
+static void mptsas_process_port_enable(MPTSASState *s,
+ MPIMsgPortEnable *req)
+{
+ MPIMsgPortEnableReply reply;
+
+ mptsas_fix_port_enable_endianness(req);
+
+ QEMU_BUILD_BUG_ON(MPTSAS_MAX_REQUEST_SIZE < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply));
+
+ memset(&reply, 0, sizeof(reply));
+ reply.MsgLength = sizeof(reply) / 4;
+ reply.PortNumber = req->PortNumber;
+ reply.Function = req->Function;
+ reply.MsgContext = req->MsgContext;
+
+ mptsas_fix_port_enable_reply_endianness(&reply);
+ mptsas_reply(s, (MPIDefaultReply *)&reply);
+}
+
+static void mptsas_process_event_notification(MPTSASState *s,
+ MPIMsgEventNotify *req)
+{
+ MPIMsgEventNotifyReply reply;
+
+ mptsas_fix_event_notification_endianness(req);
+
+ QEMU_BUILD_BUG_ON(MPTSAS_MAX_REQUEST_SIZE < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply));
+
+ /* Don't even bother storing whether event notification is enabled,
+ * since it is not accessible.
+ */
+
+ memset(&reply, 0, sizeof(reply));
+ reply.EventDataLength = sizeof(reply.Data) / 4;
+ reply.MsgLength = sizeof(reply) / 4;
+ reply.Function = req->Function;
+
+ /* This is set because events are sent through the reply FIFOs. */
+ reply.MsgFlags = MPI_MSGFLAGS_CONTINUATION_REPLY;
+
+ reply.MsgContext = req->MsgContext;
+ reply.Event = MPI_EVENT_EVENT_CHANGE;
+ reply.Data[0] = !!req->Switch;
+
+ mptsas_fix_event_notification_reply_endianness(&reply);
+ mptsas_reply(s, (MPIDefaultReply *)&reply);
+}
+
+static void mptsas_process_message(MPTSASState *s, MPIRequestHeader *req)
+{
+ trace_mptsas_process_message(s, req->Function, req->MsgContext);
+ switch (req->Function) {
+ case MPI_FUNCTION_SCSI_TASK_MGMT:
+ mptsas_process_scsi_task_mgmt(s, (MPIMsgSCSITaskMgmt *)req);
+ break;
+
+ case MPI_FUNCTION_IOC_INIT:
+ mptsas_process_ioc_init(s, (MPIMsgIOCInit *)req);
+ break;
+
+ case MPI_FUNCTION_IOC_FACTS:
+ mptsas_process_ioc_facts(s, (MPIMsgIOCFacts *)req);
+ break;
+
+ case MPI_FUNCTION_PORT_FACTS:
+ mptsas_process_port_facts(s, (MPIMsgPortFacts *)req);
+ break;
+
+ case MPI_FUNCTION_PORT_ENABLE:
+ mptsas_process_port_enable(s, (MPIMsgPortEnable *)req);
+ break;
+
+ case MPI_FUNCTION_EVENT_NOTIFICATION:
+ mptsas_process_event_notification(s, (MPIMsgEventNotify *)req);
+ break;
+
+ case MPI_FUNCTION_CONFIG:
+ mptsas_process_config(s, (MPIMsgConfig *)req);
+ break;
+
+ default:
+ trace_mptsas_unhandled_cmd(s, req->Function, 0);
+ mptsas_set_fault(s, MPI_IOCSTATUS_INVALID_FUNCTION);
+ break;
+ }
+}
+
+static void mptsas_fetch_request(MPTSASState *s)
+{
+ PCIDevice *pci = (PCIDevice *) s;
+ char req[MPTSAS_MAX_REQUEST_SIZE];
+ MPIRequestHeader *hdr = (MPIRequestHeader *)req;
+ hwaddr addr;
+ int size;
+
+ if (s->state != MPI_IOC_STATE_OPERATIONAL) {
+ mptsas_set_fault(s, MPI_IOCSTATUS_INVALID_STATE);
+ return;
+ }
+
+ /* Read the message header from the guest first. */
+ addr = s->host_mfa_high_addr | MPTSAS_FIFO_GET(s, request_post);
+ pci_dma_read(pci, addr, req, sizeof(hdr));
+
+ if (hdr->Function < ARRAY_SIZE(mpi_request_sizes) &&
+ mpi_request_sizes[hdr->Function]) {
+ /* Read the rest of the request based on the type. Do not
+ * reread everything, as that could cause a TOC/TOU mismatch
+ * and leak data from the QEMU stack.
+ */
+ size = mpi_request_sizes[hdr->Function];
+ assert(size <= MPTSAS_MAX_REQUEST_SIZE);
+ pci_dma_read(pci, addr + sizeof(hdr), &req[sizeof(hdr)],
+ size - sizeof(hdr));
+ }
+
+ if (hdr->Function == MPI_FUNCTION_SCSI_IO_REQUEST) {
+ /* SCSI I/O requests are separate from mptsas_process_message
+ * because they cannot be sent through the doorbell yet.
+ */
+ mptsas_process_scsi_io_request(s, (MPIMsgSCSIIORequest *)req, addr);
+ } else {
+ mptsas_process_message(s, (MPIRequestHeader *)req);
+ }
+}
+
+static void mptsas_fetch_requests(void *opaque)
+{
+ MPTSASState *s = opaque;
+
+ while (!MPTSAS_FIFO_EMPTY(s, request_post)) {
+ mptsas_fetch_request(s);
+ }
+}
+
+static void mptsas_soft_reset(MPTSASState *s)
+{
+ uint32_t save_mask;
+
+ trace_mptsas_reset(s);
+
+ /* Temporarily disable interrupts */
+ save_mask = s->intr_mask;
+ s->intr_mask = MPI_HIM_DIM | MPI_HIM_RIM;
+ mptsas_update_interrupt(s);
+
+ qbus_reset_all(&s->bus.qbus);
+ s->intr_status = 0;
+ s->intr_mask = save_mask;
+
+ s->reply_free_tail = 0;
+ s->reply_free_head = 0;
+ s->reply_post_tail = 0;
+ s->reply_post_head = 0;
+ s->request_post_tail = 0;
+ s->request_post_head = 0;
+ qemu_bh_cancel(s->request_bh);
+
+ s->state = MPI_IOC_STATE_READY;
+}
+
+static uint32_t mptsas_doorbell_read(MPTSASState *s)
+{
+ uint32_t ret;
+
+ ret = (s->who_init << MPI_DOORBELL_WHO_INIT_SHIFT) & MPI_DOORBELL_WHO_INIT_MASK;
+ ret |= s->state;
+ switch (s->doorbell_state) {
+ case DOORBELL_NONE:
+ break;
+
+ case DOORBELL_WRITE:
+ ret |= MPI_DOORBELL_ACTIVE;
+ break;
+
+ case DOORBELL_READ:
+ /* Get rid of the IOC fault code. */
+ ret &= ~MPI_DOORBELL_DATA_MASK;
+
+ assert(s->intr_status & MPI_HIS_DOORBELL_INTERRUPT);
+ assert(s->doorbell_reply_idx <= s->doorbell_reply_size);
+
+ ret |= MPI_DOORBELL_ACTIVE;
+ if (s->doorbell_reply_idx < s->doorbell_reply_size) {
+ /* For more information about this endian switch, see the
+ * commit message for commit 36b62ae ("fw_cfg: fix endianness in
+ * fw_cfg_data_mem_read() / _write()", 2015-01-16).
+ */
+ ret |= le16_to_cpu(s->doorbell_reply[s->doorbell_reply_idx++]);
+ }
+ break;
+
+ default:
+ abort();
+ }
+
+ return ret;
+}
+
+static void mptsas_doorbell_write(MPTSASState *s, uint32_t val)
+{
+ if (s->doorbell_state == DOORBELL_WRITE) {
+ if (s->doorbell_idx < s->doorbell_cnt) {
+ /* For more information about this endian switch, see the
+ * commit message for commit 36b62ae ("fw_cfg: fix endianness in
+ * fw_cfg_data_mem_read() / _write()", 2015-01-16).
+ */
+ s->doorbell_msg[s->doorbell_idx++] = cpu_to_le32(val);
+ if (s->doorbell_idx == s->doorbell_cnt) {
+ mptsas_process_message(s, (MPIRequestHeader *)s->doorbell_msg);
+ }
+ }
+ return;
+ }
+
+ switch ((val & MPI_DOORBELL_FUNCTION_MASK) >> MPI_DOORBELL_FUNCTION_SHIFT) {
+ case MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET:
+ mptsas_soft_reset(s);
+ break;
+ case MPI_FUNCTION_IO_UNIT_RESET:
+ break;
+ case MPI_FUNCTION_HANDSHAKE:
+ s->doorbell_state = DOORBELL_WRITE;
+ s->doorbell_idx = 0;
+ s->doorbell_cnt = (val & MPI_DOORBELL_ADD_DWORDS_MASK)
+ >> MPI_DOORBELL_ADD_DWORDS_SHIFT;
+ s->intr_status |= MPI_HIS_DOORBELL_INTERRUPT;
+ mptsas_update_interrupt(s);
+ break;
+ default:
+ trace_mptsas_unhandled_doorbell_cmd(s, val);
+ break;
+ }
+}
+
+static void mptsas_write_sequence_write(MPTSASState *s, uint32_t val)
+{
+ /* If the diagnostic register is enabled, any write to this register
+ * will disable it. Otherwise, the guest has to do a magic five-write
+ * sequence.
+ */
+ if (s->diagnostic & MPI_DIAG_DRWE) {
+ goto disable;
+ }
+
+ switch (s->diagnostic_idx) {
+ case 0:
+ if ((val & MPI_WRSEQ_KEY_VALUE_MASK) != MPI_WRSEQ_1ST_KEY_VALUE) {
+ goto disable;
+ }
+ break;
+ case 1:
+ if ((val & MPI_WRSEQ_KEY_VALUE_MASK) != MPI_WRSEQ_2ND_KEY_VALUE) {
+ goto disable;
+ }
+ break;
+ case 2:
+ if ((val & MPI_WRSEQ_KEY_VALUE_MASK) != MPI_WRSEQ_3RD_KEY_VALUE) {
+ goto disable;
+ }
+ break;
+ case 3:
+ if ((val & MPI_WRSEQ_KEY_VALUE_MASK) != MPI_WRSEQ_4TH_KEY_VALUE) {
+ goto disable;
+ }
+ break;
+ case 4:
+ if ((val & MPI_WRSEQ_KEY_VALUE_MASK) != MPI_WRSEQ_5TH_KEY_VALUE) {
+ goto disable;
+ }
+ /* Prepare Spaceball One for departure, and change the
+ * combination on my luggage!
+ */
+ s->diagnostic |= MPI_DIAG_DRWE;
+ break;
+ }
+ s->diagnostic_idx++;
+ return;
+
+disable:
+ s->diagnostic &= ~MPI_DIAG_DRWE;
+ s->diagnostic_idx = 0;
+}
+
+static int mptsas_hard_reset(MPTSASState *s)
+{
+ mptsas_soft_reset(s);
+
+ s->intr_mask = MPI_HIM_DIM | MPI_HIM_RIM;
+
+ s->host_mfa_high_addr = 0;
+ s->sense_buffer_high_addr = 0;
+ s->reply_frame_size = 0;
+ s->max_devices = MPTSAS_NUM_PORTS;
+ s->max_buses = 1;
+
+ return 0;
+}
+
+static void mptsas_interrupt_status_write(MPTSASState *s)
+{
+ switch (s->doorbell_state) {
+ case DOORBELL_NONE:
+ case DOORBELL_WRITE:
+ s->intr_status &= ~MPI_HIS_DOORBELL_INTERRUPT;
+ break;
+
+ case DOORBELL_READ:
+ /* The reply can be read continuously, so leave the interrupt up. */
+ assert(s->intr_status & MPI_HIS_DOORBELL_INTERRUPT);
+ if (s->doorbell_reply_idx == s->doorbell_reply_size) {
+ s->doorbell_state = DOORBELL_NONE;
+ }
+ break;
+
+ default:
+ abort();
+ }
+ mptsas_update_interrupt(s);
+}
+
+static uint32_t mptsas_reply_post_read(MPTSASState *s)
+{
+ uint32_t ret;
+
+ if (!MPTSAS_FIFO_EMPTY(s, reply_post)) {
+ ret = MPTSAS_FIFO_GET(s, reply_post);
+ } else {
+ ret = -1;
+ s->intr_status &= ~MPI_HIS_REPLY_MESSAGE_INTERRUPT;
+ mptsas_update_interrupt(s);
+ }
+
+ return ret;
+}
+
+static uint64_t mptsas_mmio_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ MPTSASState *s = opaque;
+ uint32_t ret = 0;
+
+ switch (addr & ~3) {
+ case MPI_DOORBELL_OFFSET:
+ ret = mptsas_doorbell_read(s);
+ break;
+
+ case MPI_DIAGNOSTIC_OFFSET:
+ ret = s->diagnostic;
+ break;
+
+ case MPI_HOST_INTERRUPT_STATUS_OFFSET:
+ ret = s->intr_status;
+ break;
+
+ case MPI_HOST_INTERRUPT_MASK_OFFSET:
+ ret = s->intr_mask;
+ break;
+
+ case MPI_REPLY_POST_FIFO_OFFSET:
+ ret = mptsas_reply_post_read(s);
+ break;
+
+ default:
+ trace_mptsas_mmio_unhandled_read(s, addr);
+ break;
+ }
+ trace_mptsas_mmio_read(s, addr, ret);
+ return ret;
+}
+
+static void mptsas_mmio_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ MPTSASState *s = opaque;
+
+ trace_mptsas_mmio_write(s, addr, val);
+ switch (addr) {
+ case MPI_DOORBELL_OFFSET:
+ mptsas_doorbell_write(s, val);
+ break;
+
+ case MPI_WRITE_SEQUENCE_OFFSET:
+ mptsas_write_sequence_write(s, val);
+ break;
+
+ case MPI_DIAGNOSTIC_OFFSET:
+ if (val & MPI_DIAG_RESET_ADAPTER) {
+ mptsas_hard_reset(s);
+ }
+ break;
+
+ case MPI_HOST_INTERRUPT_STATUS_OFFSET:
+ mptsas_interrupt_status_write(s);
+ break;
+
+ case MPI_HOST_INTERRUPT_MASK_OFFSET:
+ s->intr_mask = val & (MPI_HIM_RIM | MPI_HIM_DIM);
+ mptsas_update_interrupt(s);
+ break;
+
+ case MPI_REQUEST_POST_FIFO_OFFSET:
+ if (MPTSAS_FIFO_FULL(s, request_post)) {
+ mptsas_set_fault(s, MPI_IOCSTATUS_INSUFFICIENT_RESOURCES);
+ } else {
+ MPTSAS_FIFO_PUT(s, request_post, val & ~0x03);
+ qemu_bh_schedule(s->request_bh);
+ }
+ break;
+
+ case MPI_REPLY_FREE_FIFO_OFFSET:
+ if (MPTSAS_FIFO_FULL(s, reply_free)) {
+ mptsas_set_fault(s, MPI_IOCSTATUS_INSUFFICIENT_RESOURCES);
+ } else {
+ MPTSAS_FIFO_PUT(s, reply_free, val);
+ }
+ break;
+
+ default:
+ trace_mptsas_mmio_unhandled_write(s, addr, val);
+ break;
+ }
+}
+
+static const MemoryRegionOps mptsas_mmio_ops = {
+ .read = mptsas_mmio_read,
+ .write = mptsas_mmio_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ }
+};
+
+static const MemoryRegionOps mptsas_port_ops = {
+ .read = mptsas_mmio_read,
+ .write = mptsas_mmio_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ }
+};
+
+static uint64_t mptsas_diag_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ MPTSASState *s = opaque;
+ trace_mptsas_diag_read(s, addr, 0);
+ return 0;
+}
+
+static void mptsas_diag_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ MPTSASState *s = opaque;
+ trace_mptsas_diag_write(s, addr, val);
+}
+
+static const MemoryRegionOps mptsas_diag_ops = {
+ .read = mptsas_diag_read,
+ .write = mptsas_diag_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ }
+};
+
+static QEMUSGList *mptsas_get_sg_list(SCSIRequest *sreq)
+{
+ MPTSASRequest *req = sreq->hba_private;
+
+ return &req->qsg;
+}
+
+static void mptsas_command_complete(SCSIRequest *sreq,
+ uint32_t status, size_t resid)
+{
+ MPTSASRequest *req = sreq->hba_private;
+ MPTSASState *s = req->dev;
+ uint8_t sense_buf[SCSI_SENSE_BUF_SIZE];
+ uint8_t sense_len;
+
+ hwaddr sense_buffer_addr = req->dev->sense_buffer_high_addr |
+ req->scsi_io.SenseBufferLowAddr;
+
+ trace_mptsas_command_complete(s, req->scsi_io.MsgContext, status, resid);
+
+ sense_len = scsi_req_get_sense(sreq, sense_buf, SCSI_SENSE_BUF_SIZE);
+ if (sense_len > 0) {
+ pci_dma_write(PCI_DEVICE(s), sense_buffer_addr, sense_buf,
+ MIN(req->scsi_io.SenseBufferLength, sense_len));
+ }
+
+ if (sreq->status != GOOD || resid ||
+ req->dev->doorbell_state == DOORBELL_WRITE) {
+ MPIMsgSCSIIOReply reply;
+
+ memset(&reply, 0, sizeof(reply));
+ reply.TargetID = req->scsi_io.TargetID;
+ reply.Bus = req->scsi_io.Bus;
+ reply.MsgLength = sizeof(reply) / 4;
+ reply.Function = req->scsi_io.Function;
+ reply.CDBLength = req->scsi_io.CDBLength;
+ reply.SenseBufferLength = req->scsi_io.SenseBufferLength;
+ reply.MsgFlags = req->scsi_io.MsgFlags;
+ reply.MsgContext = req->scsi_io.MsgContext;
+ reply.SCSIStatus = sreq->status;
+ if (sreq->status == GOOD) {
+ reply.TransferCount = req->scsi_io.DataLength - resid;
+ if (resid) {
+ reply.IOCStatus = MPI_IOCSTATUS_SCSI_DATA_UNDERRUN;
+ }
+ } else {
+ reply.SCSIState = MPI_SCSI_STATE_AUTOSENSE_VALID;
+ reply.SenseCount = sense_len;
+ reply.IOCStatus = MPI_IOCSTATUS_SCSI_DATA_UNDERRUN;
+ }
+
+ mptsas_fix_scsi_io_reply_endianness(&reply);
+ mptsas_post_reply(req->dev, (MPIDefaultReply *)&reply);
+ } else {
+ mptsas_turbo_reply(req->dev, req->scsi_io.MsgContext);
+ }
+
+ mptsas_free_request(req);
+}
+
+static void mptsas_request_cancelled(SCSIRequest *sreq)
+{
+ MPTSASRequest *req = sreq->hba_private;
+ MPIMsgSCSIIOReply reply;
+
+ memset(&reply, 0, sizeof(reply));
+ reply.TargetID = req->scsi_io.TargetID;
+ reply.Bus = req->scsi_io.Bus;
+ reply.MsgLength = sizeof(reply) / 4;
+ reply.Function = req->scsi_io.Function;
+ reply.CDBLength = req->scsi_io.CDBLength;
+ reply.SenseBufferLength = req->scsi_io.SenseBufferLength;
+ reply.MsgFlags = req->scsi_io.MsgFlags;
+ reply.MsgContext = req->scsi_io.MsgContext;
+ reply.SCSIState = MPI_SCSI_STATE_NO_SCSI_STATUS;
+ reply.IOCStatus = MPI_IOCSTATUS_SCSI_TASK_TERMINATED;
+
+ mptsas_fix_scsi_io_reply_endianness(&reply);
+ mptsas_post_reply(req->dev, (MPIDefaultReply *)&reply);
+ mptsas_free_request(req);
+}
+
+static void mptsas_save_request(QEMUFile *f, SCSIRequest *sreq)
+{
+ MPTSASRequest *req = sreq->hba_private;
+ int i;
+
+ qemu_put_buffer(f, (unsigned char *)&req->scsi_io, sizeof(req->scsi_io));
+ qemu_put_be32(f, req->qsg.nsg);
+ for (i = 0; i < req->qsg.nsg; i++) {
+ qemu_put_be64(f, req->qsg.sg[i].base);
+ qemu_put_be64(f, req->qsg.sg[i].len);
+ }
+}
+
+static void *mptsas_load_request(QEMUFile *f, SCSIRequest *sreq)
+{
+ SCSIBus *bus = sreq->bus;
+ MPTSASState *s = container_of(bus, MPTSASState, bus);
+ PCIDevice *pci = PCI_DEVICE(s);
+ MPTSASRequest *req;
+ int i, n;
+
+ req = g_new(MPTSASRequest, 1);
+ qemu_get_buffer(f, (unsigned char *)&req->scsi_io, sizeof(req->scsi_io));
+
+ n = qemu_get_be32(f);
+ /* TODO: add a way for SCSIBusInfo's load_request to fail,
+ * and fail migration instead of asserting here.
+ * When we do, we might be able to re-enable NDEBUG below.
+ */
+#ifdef NDEBUG
+#error building with NDEBUG is not supported
+#endif
+ assert(n >= 0);
+
+ pci_dma_sglist_init(&req->qsg, pci, n);
+ for (i = 0; i < n; i++) {
+ uint64_t base = qemu_get_be64(f);
+ uint64_t len = qemu_get_be64(f);
+ qemu_sglist_add(&req->qsg, base, len);
+ }
+
+ scsi_req_ref(sreq);
+ req->sreq = sreq;
+ req->dev = s;
+
+ return req;
+}
+
+static const struct SCSIBusInfo mptsas_scsi_info = {
+ .tcq = true,
+ .max_target = MPTSAS_NUM_PORTS,
+ .max_lun = 1,
+
+ .get_sg_list = mptsas_get_sg_list,
+ .complete = mptsas_command_complete,
+ .cancel = mptsas_request_cancelled,
+ .save_request = mptsas_save_request,
+ .load_request = mptsas_load_request,
+};
+
+static void mptsas_scsi_init(PCIDevice *dev, Error **errp)
+{
+ DeviceState *d = DEVICE(dev);
+ MPTSASState *s = MPT_SAS(dev);
+
+ dev->config[PCI_LATENCY_TIMER] = 0;
+ dev->config[PCI_INTERRUPT_PIN] = 0x01;
+
+ memory_region_init_io(&s->mmio_io, OBJECT(s), &mptsas_mmio_ops, s,
+ "mptsas-mmio", 0x4000);
+ memory_region_init_io(&s->port_io, OBJECT(s), &mptsas_port_ops, s,
+ "mptsas-io", 256);
+ memory_region_init_io(&s->diag_io, OBJECT(s), &mptsas_diag_ops, s,
+ "mptsas-diag", 0x10000);
+
+ if (s->msi_available &&
+ msi_init(dev, 0, 1, true, false) >= 0) {
+ s->msi_in_use = true;
+ }
+
+ pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->port_io);
+ pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY |
+ PCI_BASE_ADDRESS_MEM_TYPE_32, &s->mmio_io);
+ pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY |
+ PCI_BASE_ADDRESS_MEM_TYPE_32, &s->diag_io);
+
+ if (!s->sas_addr) {
+ s->sas_addr = ((NAA_LOCALLY_ASSIGNED_ID << 24) |
+ IEEE_COMPANY_LOCALLY_ASSIGNED) << 36;
+ s->sas_addr |= (pci_bus_num(dev->bus) << 16);
+ s->sas_addr |= (PCI_SLOT(dev->devfn) << 8);
+ s->sas_addr |= PCI_FUNC(dev->devfn);
+ }
+ s->max_devices = MPTSAS_NUM_PORTS;
+
+ s->request_bh = qemu_bh_new(mptsas_fetch_requests, s);
+
+ QTAILQ_INIT(&s->pending);
+
+ scsi_bus_new(&s->bus, sizeof(s->bus), &dev->qdev, &mptsas_scsi_info, NULL);
+ if (!d->hotplugged) {
+ scsi_bus_legacy_handle_cmdline(&s->bus, errp);
+ }
+}
+
+static void mptsas_scsi_uninit(PCIDevice *dev)
+{
+ MPTSASState *s = MPT_SAS(dev);
+
+ qemu_bh_delete(s->request_bh);
+ if (s->msi_in_use) {
+ msi_uninit(dev);
+ }
+}
+
+static void mptsas_reset(DeviceState *dev)
+{
+ MPTSASState *s = MPT_SAS(dev);
+
+ mptsas_hard_reset(s);
+}
+
+static int mptsas_post_load(void *opaque, int version_id)
+{
+ MPTSASState *s = opaque;
+
+ if (s->doorbell_idx > s->doorbell_cnt ||
+ s->doorbell_cnt > ARRAY_SIZE(s->doorbell_msg) ||
+ s->doorbell_reply_idx > s->doorbell_reply_size ||
+ s->doorbell_reply_size > ARRAY_SIZE(s->doorbell_reply) ||
+ MPTSAS_FIFO_INVALID(s, request_post) ||
+ MPTSAS_FIFO_INVALID(s, reply_post) ||
+ MPTSAS_FIFO_INVALID(s, reply_free) ||
+ s->diagnostic_idx > 4) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_mptsas = {
+ .name = "mptsas",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = mptsas_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(dev, MPTSASState),
+ VMSTATE_BOOL(msi_in_use, MPTSASState),
+
+ VMSTATE_UINT32(state, MPTSASState),
+ VMSTATE_UINT8(who_init, MPTSASState),
+ VMSTATE_UINT8(doorbell_state, MPTSASState),
+ VMSTATE_UINT32_ARRAY(doorbell_msg, MPTSASState, 256),
+ VMSTATE_INT32(doorbell_idx, MPTSASState),
+ VMSTATE_INT32(doorbell_cnt, MPTSASState),
+
+ VMSTATE_UINT16_ARRAY(doorbell_reply, MPTSASState, 256),
+ VMSTATE_INT32(doorbell_reply_idx, MPTSASState),
+ VMSTATE_INT32(doorbell_reply_size, MPTSASState),
+
+ VMSTATE_UINT32(diagnostic, MPTSASState),
+ VMSTATE_UINT8(diagnostic_idx, MPTSASState),
+
+ VMSTATE_UINT32(intr_status, MPTSASState),
+ VMSTATE_UINT32(intr_mask, MPTSASState),
+
+ VMSTATE_UINT32_ARRAY(request_post, MPTSASState,
+ MPTSAS_REQUEST_QUEUE_DEPTH + 1),
+ VMSTATE_UINT16(request_post_head, MPTSASState),
+ VMSTATE_UINT16(request_post_tail, MPTSASState),
+
+ VMSTATE_UINT32_ARRAY(reply_post, MPTSASState,
+ MPTSAS_REPLY_QUEUE_DEPTH + 1),
+ VMSTATE_UINT16(reply_post_head, MPTSASState),
+ VMSTATE_UINT16(reply_post_tail, MPTSASState),
+
+ VMSTATE_UINT32_ARRAY(reply_free, MPTSASState,
+ MPTSAS_REPLY_QUEUE_DEPTH + 1),
+ VMSTATE_UINT16(reply_free_head, MPTSASState),
+ VMSTATE_UINT16(reply_free_tail, MPTSASState),
+
+ VMSTATE_UINT16(max_buses, MPTSASState),
+ VMSTATE_UINT16(max_devices, MPTSASState),
+ VMSTATE_UINT16(reply_frame_size, MPTSASState),
+ VMSTATE_UINT64(host_mfa_high_addr, MPTSASState),
+ VMSTATE_UINT64(sense_buffer_high_addr, MPTSASState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property mptsas_properties[] = {
+ DEFINE_PROP_UINT64("sas_address", MPTSASState, sas_addr, 0),
+ /* TODO: test MSI support under Windows */
+ DEFINE_PROP_BIT("msi", MPTSASState, msi_available, 0, true),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void mptsas1068_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
+
+ pc->realize = mptsas_scsi_init;
+ pc->exit = mptsas_scsi_uninit;
+ pc->romfile = 0;
+ pc->vendor_id = PCI_VENDOR_ID_LSI_LOGIC;
+ pc->device_id = PCI_DEVICE_ID_LSI_SAS1068;
+ pc->subsystem_vendor_id = PCI_VENDOR_ID_LSI_LOGIC;
+ pc->subsystem_id = 0x8000;
+ pc->class_id = PCI_CLASS_STORAGE_SCSI;
+ dc->props = mptsas_properties;
+ dc->reset = mptsas_reset;
+ dc->vmsd = &vmstate_mptsas;
+ dc->desc = "LSI SAS 1068";
+}
+
+static const TypeInfo mptsas_info = {
+ .name = TYPE_MPTSAS1068,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(MPTSASState),
+ .class_init = mptsas1068_class_init,
+};
+
+static void mptsas_register_types(void)
+{
+ type_register(&mptsas_info);
+}
+
+type_init(mptsas_register_types)
diff --git a/qemu/hw/scsi/mptsas.h b/qemu/hw/scsi/mptsas.h
new file mode 100644
index 000000000..595f81fb5
--- /dev/null
+++ b/qemu/hw/scsi/mptsas.h
@@ -0,0 +1,100 @@
+#ifndef MPTSAS_H
+#define MPTSAS_H
+
+#include "mpi.h"
+
+#define MPTSAS_NUM_PORTS 8
+#define MPTSAS_MAX_FRAMES 2048 /* Firmware limit at 65535 */
+
+#define MPTSAS_REQUEST_QUEUE_DEPTH 128
+#define MPTSAS_REPLY_QUEUE_DEPTH 128
+
+#define MPTSAS_MAXIMUM_CHAIN_DEPTH 0x22
+
+typedef struct MPTSASState MPTSASState;
+typedef struct MPTSASRequest MPTSASRequest;
+
+enum {
+ DOORBELL_NONE,
+ DOORBELL_WRITE,
+ DOORBELL_READ
+};
+
+struct MPTSASState {
+ PCIDevice dev;
+ MemoryRegion mmio_io;
+ MemoryRegion port_io;
+ MemoryRegion diag_io;
+ QEMUBH *request_bh;
+
+ uint32_t msi_available;
+ uint64_t sas_addr;
+
+ bool msi_in_use;
+
+ /* Doorbell register */
+ uint32_t state;
+ uint8_t who_init;
+ uint8_t doorbell_state;
+
+ /* Buffer for requests that are sent through the doorbell register. */
+ uint32_t doorbell_msg[256];
+ int doorbell_idx;
+ int doorbell_cnt;
+
+ uint16_t doorbell_reply[256];
+ int doorbell_reply_idx;
+ int doorbell_reply_size;
+
+ /* Other registers */
+ uint8_t diagnostic_idx;
+ uint32_t diagnostic;
+ uint32_t intr_mask;
+ uint32_t intr_status;
+
+ /* Request queues */
+ uint32_t request_post[MPTSAS_REQUEST_QUEUE_DEPTH + 1];
+ uint16_t request_post_head;
+ uint16_t request_post_tail;
+
+ uint32_t reply_post[MPTSAS_REPLY_QUEUE_DEPTH + 1];
+ uint16_t reply_post_head;
+ uint16_t reply_post_tail;
+
+ uint32_t reply_free[MPTSAS_REPLY_QUEUE_DEPTH + 1];
+ uint16_t reply_free_head;
+ uint16_t reply_free_tail;
+
+ /* IOC Facts */
+ hwaddr host_mfa_high_addr;
+ hwaddr sense_buffer_high_addr;
+ uint16_t max_devices;
+ uint16_t max_buses;
+ uint16_t reply_frame_size;
+
+ SCSIBus bus;
+ QTAILQ_HEAD(, MPTSASRequest) pending;
+};
+
+void mptsas_fix_scsi_io_endianness(MPIMsgSCSIIORequest *req);
+void mptsas_fix_scsi_io_reply_endianness(MPIMsgSCSIIOReply *reply);
+void mptsas_fix_scsi_task_mgmt_endianness(MPIMsgSCSITaskMgmt *req);
+void mptsas_fix_scsi_task_mgmt_reply_endianness(MPIMsgSCSITaskMgmtReply *reply);
+void mptsas_fix_ioc_init_endianness(MPIMsgIOCInit *req);
+void mptsas_fix_ioc_init_reply_endianness(MPIMsgIOCInitReply *reply);
+void mptsas_fix_ioc_facts_endianness(MPIMsgIOCFacts *req);
+void mptsas_fix_ioc_facts_reply_endianness(MPIMsgIOCFactsReply *reply);
+void mptsas_fix_config_endianness(MPIMsgConfig *req);
+void mptsas_fix_config_reply_endianness(MPIMsgConfigReply *reply);
+void mptsas_fix_port_facts_endianness(MPIMsgPortFacts *req);
+void mptsas_fix_port_facts_reply_endianness(MPIMsgPortFactsReply *reply);
+void mptsas_fix_port_enable_endianness(MPIMsgPortEnable *req);
+void mptsas_fix_port_enable_reply_endianness(MPIMsgPortEnableReply *reply);
+void mptsas_fix_event_notification_endianness(MPIMsgEventNotify *req);
+void mptsas_fix_event_notification_reply_endianness(MPIMsgEventNotifyReply *reply);
+
+void mptsas_reply(MPTSASState *s, MPIDefaultReply *reply);
+
+void mptsas_process_config(MPTSASState *s, MPIMsgConfig *req);
+
+#endif /* MPTSAS_H */
diff --git a/qemu/hw/scsi/scsi-bus.c b/qemu/hw/scsi/scsi-bus.c
index f0ae4625f..ad6f398c3 100644
--- a/qemu/hw/scsi/scsi-bus.c
+++ b/qemu/hw/scsi/scsi-bus.c
@@ -1,4 +1,6 @@
+#include "qemu/osdep.h"
#include "hw/hw.h"
+#include "qapi/error.h"
#include "qemu/error-report.h"
#include "hw/scsi/scsi.h"
#include "block/scsi.h"
@@ -7,6 +9,7 @@
#include "sysemu/blockdev.h"
#include "trace.h"
#include "sysemu/dma.h"
+#include "qemu/cutils.h"
static char *scsibus_get_dev_path(DeviceState *dev);
static char *scsibus_get_fw_dev_path(DeviceState *dev);
@@ -136,7 +139,8 @@ static void scsi_dma_restart_cb(void *opaque, int running, RunState state)
return;
}
if (!s->bh) {
- s->bh = qemu_bh_new(scsi_dma_restart_bh, s);
+ AioContext *ctx = blk_get_aio_context(s->conf.blk);
+ s->bh = aio_bh_new(ctx, scsi_dma_restart_bh, s);
qemu_bh_schedule(s->bh);
}
}
@@ -452,7 +456,7 @@ static bool scsi_target_emulate_inquiry(SCSITargetReq *r)
r->buf[7] = 0x10 | (r->req.bus->info->tcq ? 0x02 : 0); /* Sync, TCQ. */
memcpy(&r->buf[8], "QEMU ", 8);
memcpy(&r->buf[16], "QEMU TARGET ", 16);
- pstrcpy((char *) &r->buf[32], 4, qemu_get_version());
+ pstrcpy((char *) &r->buf[32], 4, qemu_hw_version());
}
return true;
}
@@ -557,7 +561,7 @@ SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d,
const int memset_off = offsetof(SCSIRequest, sense)
+ sizeof(req->sense);
- req = g_slice_alloc(reqops->size);
+ req = g_malloc(reqops->size);
memset((uint8_t *)req + memset_off, 0, reqops->size - memset_off);
req->refcount = 1;
req->bus = bus;
@@ -987,7 +991,6 @@ static int scsi_req_xfer(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
}
/* fall through */
case READ_10:
- case RECOVER_BUFFERED_DATA:
case READ_12:
case READ_16:
cmd->xfer *= dev->blocksize;
@@ -1621,7 +1624,7 @@ void scsi_req_unref(SCSIRequest *req)
}
object_unref(OBJECT(req->dev));
object_unref(OBJECT(qbus->parent));
- g_slice_free1(req->ops->size, req);
+ g_free(req);
}
}
@@ -1759,8 +1762,14 @@ void scsi_req_cancel_async(SCSIRequest *req, Notifier *notifier)
notifier_list_add(&req->cancel_notifiers, notifier);
}
if (req->io_canceled) {
+ /* A blk_aio_cancel_async is pending; when it finishes,
+ * scsi_req_cancel_complete will be called and will
+ * call the notifier we just added. Just wait for that.
+ */
+ assert(req->aiocb);
return;
}
+ /* Dropped in scsi_req_cancel_complete. */
scsi_req_ref(req);
scsi_req_dequeue(req);
req->io_canceled = true;
@@ -1777,6 +1786,8 @@ void scsi_req_cancel(SCSIRequest *req)
if (!req->enqueued) {
return;
}
+ assert(!req->io_canceled);
+ /* Dropped in scsi_req_cancel_complete. */
scsi_req_ref(req);
scsi_req_dequeue(req);
req->io_canceled = true;
@@ -1840,17 +1851,19 @@ void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense)
{
SCSIRequest *req;
+ aio_context_acquire(blk_get_aio_context(sdev->conf.blk));
while (!QTAILQ_EMPTY(&sdev->requests)) {
req = QTAILQ_FIRST(&sdev->requests);
- scsi_req_cancel(req);
+ scsi_req_cancel_async(req, NULL);
}
-
+ blk_drain(sdev->conf.blk);
+ aio_context_release(blk_get_aio_context(sdev->conf.blk));
scsi_device_set_ua(sdev, sense);
}
static char *scsibus_get_dev_path(DeviceState *dev)
{
- SCSIDevice *d = DO_UPCAST(SCSIDevice, qdev, dev);
+ SCSIDevice *d = SCSI_DEVICE(dev);
DeviceState *hba = dev->parent_bus->parent;
char *id;
char *path;
@@ -2023,7 +2036,7 @@ static void scsi_device_class_init(ObjectClass *klass, void *data)
static void scsi_dev_instance_init(Object *obj)
{
DeviceState *dev = DEVICE(obj);
- SCSIDevice *s = DO_UPCAST(SCSIDevice, qdev, dev);
+ SCSIDevice *s = SCSI_DEVICE(dev);
device_add_bootindex_property(obj, &s->conf.bootindex,
"bootindex", NULL,
diff --git a/qemu/hw/scsi/scsi-disk.c b/qemu/hw/scsi/scsi-disk.c
index 64f069473..c3ce54a20 100644
--- a/qemu/hw/scsi/scsi-disk.c
+++ b/qemu/hw/scsi/scsi-disk.c
@@ -28,7 +28,8 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
#define DPRINTF(fmt, ...) do {} while(0)
#endif
-#include "qemu-common.h"
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu/error-report.h"
#include "hw/scsi/scsi.h"
#include "block/scsi.h"
@@ -37,6 +38,7 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
#include "sysemu/blockdev.h"
#include "hw/block/block.h"
#include "sysemu/dma.h"
+#include "qemu/cutils.h"
#ifdef __linux
#include <scsi/sg.h>
@@ -76,8 +78,6 @@ struct SCSIDiskState
bool media_changed;
bool media_event;
bool eject_request;
- uint64_t wwn;
- uint64_t port_wwn;
uint16_t port_index;
uint64_t max_unmap_size;
uint64_t max_io_size;
@@ -90,7 +90,7 @@ struct SCSIDiskState
bool tray_locked;
};
-static int scsi_handle_rw_error(SCSIDiskReq *r, int error);
+static int scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed);
static void scsi_free_request(SCSIRequest *req)
{
@@ -169,18 +169,18 @@ static void scsi_aio_complete(void *opaque, int ret)
assert(r->req.aiocb != NULL);
r->req.aiocb = NULL;
- block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, true)) {
goto done;
}
}
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
scsi_req_complete(&r->req, GOOD);
done:
@@ -217,6 +217,8 @@ static void scsi_write_do_fua(SCSIDiskReq *r)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+ assert(r->req.aiocb == NULL);
+
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
@@ -235,22 +237,17 @@ done:
scsi_req_unref(&r->req);
}
-static void scsi_dma_complete_noio(void *opaque, int ret)
+static void scsi_dma_complete_noio(SCSIDiskReq *r, int ret)
{
- SCSIDiskReq *r = (SCSIDiskReq *)opaque;
- SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+ assert(r->req.aiocb == NULL);
- if (r->req.aiocb != NULL) {
- r->req.aiocb = NULL;
- block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
- }
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, false)) {
goto done;
}
}
@@ -271,9 +268,17 @@ done:
static void scsi_dma_complete(void *opaque, int ret)
{
SCSIDiskReq *r = (SCSIDiskReq *)opaque;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
assert(r->req.aiocb != NULL);
- scsi_dma_complete_noio(opaque, ret);
+ r->req.aiocb = NULL;
+
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ } else {
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ }
+ scsi_dma_complete_noio(r, ret);
}
static void scsi_read_complete(void * opaque, int ret)
@@ -284,18 +289,18 @@ static void scsi_read_complete(void * opaque, int ret)
assert(r->req.aiocb != NULL);
r->req.aiocb = NULL;
- block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, true)) {
goto done;
}
}
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
DPRINTF("Data ready tag=0x%x len=%zd\n", r->req.tag, r->qiov.size);
n = r->qiov.size / 512;
@@ -308,23 +313,20 @@ done:
}
/* Actually issue a read to the block device. */
-static void scsi_do_read(void *opaque, int ret)
+static void scsi_do_read(SCSIDiskReq *r, int ret)
{
- SCSIDiskReq *r = opaque;
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
uint32_t n;
- if (r->req.aiocb != NULL) {
- r->req.aiocb = NULL;
- block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
- }
+ assert (r->req.aiocb == NULL);
+
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, false)) {
goto done;
}
}
@@ -349,6 +351,22 @@ done:
scsi_req_unref(&r->req);
}
+static void scsi_do_read_cb(void *opaque, int ret)
+{
+ SCSIDiskReq *r = (SCSIDiskReq *)opaque;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+
+ assert (r->req.aiocb != NULL);
+ r->req.aiocb = NULL;
+
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ } else {
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ }
+ scsi_do_read(opaque, ret);
+}
+
/* Read more data from scsi device into buffer. */
static void scsi_read_data(SCSIRequest *req)
{
@@ -384,7 +402,7 @@ static void scsi_read_data(SCSIRequest *req)
if (first && scsi_is_cmd_fua(&r->req.cmd)) {
block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0,
BLOCK_ACCT_FLUSH);
- r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_do_read, r);
+ r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_do_read_cb, r);
} else {
scsi_do_read(r, 0);
}
@@ -397,14 +415,17 @@ static void scsi_read_data(SCSIRequest *req)
* scsi_handle_rw_error always manages its reference counts, independent
* of the return value.
*/
-static int scsi_handle_rw_error(SCSIDiskReq *r, int error)
+static int scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed)
{
- bool is_read = (r->req.cmd.xfer == SCSI_XFER_FROM_DEV);
+ bool is_read = (r->req.cmd.mode == SCSI_XFER_FROM_DEV);
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
BlockErrorAction action = blk_get_error_action(s->qdev.conf.blk,
is_read, error);
if (action == BLOCK_ERROR_ACTION_REPORT) {
+ if (acct_failed) {
+ block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ }
switch (error) {
case ENOMEDIUM:
scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
@@ -430,23 +451,19 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error)
return action != BLOCK_ERROR_ACTION_IGNORE;
}
-static void scsi_write_complete(void * opaque, int ret)
+static void scsi_write_complete_noio(SCSIDiskReq *r, int ret)
{
- SCSIDiskReq *r = (SCSIDiskReq *)opaque;
- SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
uint32_t n;
- if (r->req.aiocb != NULL) {
- r->req.aiocb = NULL;
- block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
- }
+ assert (r->req.aiocb == NULL);
+
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, false)) {
goto done;
}
}
@@ -467,6 +484,22 @@ done:
scsi_req_unref(&r->req);
}
+static void scsi_write_complete(void * opaque, int ret)
+{
+ SCSIDiskReq *r = (SCSIDiskReq *)opaque;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+
+ assert (r->req.aiocb != NULL);
+ r->req.aiocb = NULL;
+
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ } else {
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ }
+ scsi_write_complete_noio(r, ret);
+}
+
static void scsi_write_data(SCSIRequest *req)
{
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
@@ -480,18 +513,18 @@ static void scsi_write_data(SCSIRequest *req)
scsi_req_ref(&r->req);
if (r->req.cmd.mode != SCSI_XFER_TO_DEV) {
DPRINTF("Data transfer direction invalid\n");
- scsi_write_complete(r, -EINVAL);
+ scsi_write_complete_noio(r, -EINVAL);
return;
}
if (!r->req.sg && !r->qiov.size) {
/* Called for the first time. Ask the driver to send us more data. */
r->started = true;
- scsi_write_complete(r, 0);
+ scsi_write_complete_noio(r, 0);
return;
}
if (s->tray_open) {
- scsi_write_complete(r, -ENOMEDIUM);
+ scsi_write_complete_noio(r, -ENOMEDIUM);
return;
}
@@ -500,7 +533,7 @@ static void scsi_write_data(SCSIRequest *req)
if (r->req.sg) {
scsi_dma_complete_noio(r, 0);
} else {
- scsi_write_complete(r, 0);
+ scsi_write_complete_noio(r, 0);
}
return;
}
@@ -599,21 +632,21 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
memcpy(outbuf+buflen, str, id_len);
buflen += id_len;
- if (s->wwn) {
+ if (s->qdev.wwn) {
outbuf[buflen++] = 0x1; // Binary
outbuf[buflen++] = 0x3; // NAA
outbuf[buflen++] = 0; // reserved
outbuf[buflen++] = 8;
- stq_be_p(&outbuf[buflen], s->wwn);
+ stq_be_p(&outbuf[buflen], s->qdev.wwn);
buflen += 8;
}
- if (s->port_wwn) {
+ if (s->qdev.port_wwn) {
outbuf[buflen++] = 0x61; // SAS / Binary
outbuf[buflen++] = 0x93; // PIV / Target port / NAA
outbuf[buflen++] = 0; // reserved
outbuf[buflen++] = 8;
- stq_be_p(&outbuf[buflen], s->port_wwn);
+ stq_be_p(&outbuf[buflen], s->qdev.port_wwn);
buflen += 8;
}
@@ -1557,22 +1590,24 @@ typedef struct UnmapCBData {
int count;
} UnmapCBData;
-static void scsi_unmap_complete(void *opaque, int ret)
+static void scsi_unmap_complete(void *opaque, int ret);
+
+static void scsi_unmap_complete_noio(UnmapCBData *data, int ret)
{
- UnmapCBData *data = opaque;
SCSIDiskReq *r = data->r;
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
uint64_t sector_num;
uint32_t nb_sectors;
- r->req.aiocb = NULL;
+ assert(r->req.aiocb == NULL);
+
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, false)) {
goto done;
}
}
@@ -1601,6 +1636,17 @@ done:
g_free(data);
}
+static void scsi_unmap_complete(void *opaque, int ret)
+{
+ UnmapCBData *data = opaque;
+ SCSIDiskReq *r = data->r;
+
+ assert(r->req.aiocb != NULL);
+ r->req.aiocb = NULL;
+
+ scsi_unmap_complete_noio(data, ret);
+}
+
static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
@@ -1638,7 +1684,7 @@ static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf)
/* The matching unref is in scsi_unmap_complete, before data is freed. */
scsi_req_ref(&r->req);
- scsi_unmap_complete(data, 0);
+ scsi_unmap_complete_noio(data, 0);
return;
invalid_param_len:
@@ -1665,24 +1711,29 @@ static void scsi_write_same_complete(void *opaque, int ret)
assert(r->req.aiocb != NULL);
r->req.aiocb = NULL;
- block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, true)) {
goto done;
}
}
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
+
data->nb_sectors -= data->iov.iov_len / 512;
data->sector += data->iov.iov_len / 512;
data->iov.iov_len = MIN(data->nb_sectors * 512, data->iov.iov_len);
if (data->iov.iov_len) {
block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct,
data->iov.iov_len, BLOCK_ACCT_WRITE);
+ /* blk_aio_write doesn't like the qiov size being different from
+ * nb_sectors, make sure they match.
+ */
+ qemu_iovec_init_external(&data->qiov, &data->iov, 1);
r->req.aiocb = blk_aio_writev(s->qdev.conf.blk, data->sector,
&data->qiov, data->iov.iov_len / 512,
scsi_write_same_complete, data);
@@ -2280,7 +2331,7 @@ static void scsi_realize(SCSIDevice *dev, Error **errp)
}
if (!s->version) {
- s->version = g_strdup(qemu_get_version());
+ s->version = g_strdup(qemu_hw_version());
}
if (!s->vendor) {
s->vendor = g_strdup("QEMU");
@@ -2523,6 +2574,7 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp)
s->features |= (1 << SCSI_DISK_F_NO_REMOVABLE_DEVOPS);
scsi_realize(&s->qdev, errp);
+ scsi_generic_read_device_identification(&s->qdev);
}
static bool scsi_block_is_passthrough(SCSIDiskState *s, uint8_t *buf)
@@ -2616,8 +2668,8 @@ static Property scsi_hd_properties[] = {
SCSI_DISK_F_REMOVABLE, false),
DEFINE_PROP_BIT("dpofua", SCSIDiskState, features,
SCSI_DISK_F_DPOFUA, false),
- DEFINE_PROP_UINT64("wwn", SCSIDiskState, wwn, 0),
- DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, port_wwn, 0),
+ DEFINE_PROP_UINT64("wwn", SCSIDiskState, qdev.wwn, 0),
+ DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, qdev.port_wwn, 0),
DEFINE_PROP_UINT16("port_index", SCSIDiskState, port_index, 0),
DEFINE_PROP_UINT64("max_unmap_size", SCSIDiskState, max_unmap_size,
DEFAULT_MAX_UNMAP_SIZE),
@@ -2666,8 +2718,8 @@ static const TypeInfo scsi_hd_info = {
static Property scsi_cd_properties[] = {
DEFINE_SCSI_DISK_PROPERTIES(),
- DEFINE_PROP_UINT64("wwn", SCSIDiskState, wwn, 0),
- DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, port_wwn, 0),
+ DEFINE_PROP_UINT64("wwn", SCSIDiskState, qdev.wwn, 0),
+ DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, qdev.port_wwn, 0),
DEFINE_PROP_UINT16("port_index", SCSIDiskState, port_index, 0),
DEFINE_PROP_UINT64("max_io_size", SCSIDiskState, max_io_size,
DEFAULT_MAX_IO_SIZE),
@@ -2731,8 +2783,8 @@ static Property scsi_disk_properties[] = {
SCSI_DISK_F_REMOVABLE, false),
DEFINE_PROP_BIT("dpofua", SCSIDiskState, features,
SCSI_DISK_F_DPOFUA, false),
- DEFINE_PROP_UINT64("wwn", SCSIDiskState, wwn, 0),
- DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, port_wwn, 0),
+ DEFINE_PROP_UINT64("wwn", SCSIDiskState, qdev.wwn, 0),
+ DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, qdev.port_wwn, 0),
DEFINE_PROP_UINT16("port_index", SCSIDiskState, port_index, 0),
DEFINE_PROP_UINT64("max_unmap_size", SCSIDiskState, max_unmap_size,
DEFAULT_MAX_UNMAP_SIZE),
diff --git a/qemu/hw/scsi/scsi-generic.c b/qemu/hw/scsi/scsi-generic.c
index e53470f85..7459465f6 100644
--- a/qemu/hw/scsi/scsi-generic.c
+++ b/qemu/hw/scsi/scsi-generic.c
@@ -11,6 +11,8 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu-common.h"
#include "qemu/error-report.h"
#include "hw/scsi/scsi.h"
@@ -31,10 +33,6 @@ do { printf("scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
#define BADF(fmt, ...) \
do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
#include <scsi/sg.h>
#include "block/scsi.h"
@@ -88,12 +86,12 @@ static void scsi_free_request(SCSIRequest *req)
}
/* Helper function for command completion. */
-static void scsi_command_complete(void *opaque, int ret)
+static void scsi_command_complete_noio(SCSIGenericReq *r, int ret)
{
int status;
- SCSIGenericReq *r = (SCSIGenericReq *)opaque;
- r->req.aiocb = NULL;
+ assert(r->req.aiocb == NULL);
+
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
@@ -142,6 +140,15 @@ done:
scsi_req_unref(&r->req);
}
+static void scsi_command_complete(void *opaque, int ret)
+{
+ SCSIGenericReq *r = (SCSIGenericReq *)opaque;
+
+ assert(r->req.aiocb != NULL);
+ r->req.aiocb = NULL;
+ scsi_command_complete_noio(r, ret);
+}
+
static int execute_command(BlockBackend *blk,
SCSIGenericReq *r, int direction,
BlockCompletionFunc *complete)
@@ -172,33 +179,51 @@ static void scsi_read_complete(void * opaque, int ret)
SCSIDevice *s = r->req.dev;
int len;
+ assert(r->req.aiocb != NULL);
r->req.aiocb = NULL;
+
if (ret || r->req.io_canceled) {
- scsi_command_complete(r, ret);
+ scsi_command_complete_noio(r, ret);
return;
}
+
len = r->io_header.dxfer_len - r->io_header.resid;
DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len);
r->len = -1;
if (len == 0) {
- scsi_command_complete(r, 0);
- } else {
- /* Snoop READ CAPACITY output to set the blocksize. */
- if (r->req.cmd.buf[0] == READ_CAPACITY_10 &&
- (ldl_be_p(&r->buf[0]) != 0xffffffffU || s->max_lba == 0)) {
- s->blocksize = ldl_be_p(&r->buf[4]);
- s->max_lba = ldl_be_p(&r->buf[0]) & 0xffffffffULL;
- } else if (r->req.cmd.buf[0] == SERVICE_ACTION_IN_16 &&
- (r->req.cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) {
- s->blocksize = ldl_be_p(&r->buf[8]);
- s->max_lba = ldq_be_p(&r->buf[0]);
- }
- blk_set_guest_block_size(s->conf.blk, s->blocksize);
+ scsi_command_complete_noio(r, 0);
+ return;
+ }
- scsi_req_data(&r->req, len);
- scsi_req_unref(&r->req);
+ /* Snoop READ CAPACITY output to set the blocksize. */
+ if (r->req.cmd.buf[0] == READ_CAPACITY_10 &&
+ (ldl_be_p(&r->buf[0]) != 0xffffffffU || s->max_lba == 0)) {
+ s->blocksize = ldl_be_p(&r->buf[4]);
+ s->max_lba = ldl_be_p(&r->buf[0]) & 0xffffffffULL;
+ } else if (r->req.cmd.buf[0] == SERVICE_ACTION_IN_16 &&
+ (r->req.cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) {
+ s->blocksize = ldl_be_p(&r->buf[8]);
+ s->max_lba = ldq_be_p(&r->buf[0]);
+ }
+ blk_set_guest_block_size(s->conf.blk, s->blocksize);
+
+ /* Patch MODE SENSE device specific parameters if the BDS is opened
+ * readonly.
+ */
+ if ((s->type == TYPE_DISK || s->type == TYPE_TAPE) &&
+ blk_is_read_only(s->conf.blk) &&
+ (r->req.cmd.buf[0] == MODE_SENSE ||
+ r->req.cmd.buf[0] == MODE_SENSE_10) &&
+ (r->req.cmd.buf[1] & 0x8) == 0) {
+ if (r->req.cmd.buf[0] == MODE_SENSE) {
+ r->buf[2] |= 0x80;
+ } else {
+ r->buf[3] |= 0x80;
+ }
}
+ scsi_req_data(&r->req, len);
+ scsi_req_unref(&r->req);
}
/* Read more data from scsi device into buffer. */
@@ -213,14 +238,14 @@ static void scsi_read_data(SCSIRequest *req)
/* The request is used as the AIO opaque value, so add a ref. */
scsi_req_ref(&r->req);
if (r->len == -1) {
- scsi_command_complete(r, 0);
+ scsi_command_complete_noio(r, 0);
return;
}
ret = execute_command(s->conf.blk, r, SG_DXFER_FROM_DEV,
scsi_read_complete);
if (ret < 0) {
- scsi_command_complete(r, ret);
+ scsi_command_complete_noio(r, ret);
}
}
@@ -230,9 +255,12 @@ static void scsi_write_complete(void * opaque, int ret)
SCSIDevice *s = r->req.dev;
DPRINTF("scsi_write_complete() ret = %d\n", ret);
+
+ assert(r->req.aiocb != NULL);
r->req.aiocb = NULL;
+
if (ret || r->req.io_canceled) {
- scsi_command_complete(r, ret);
+ scsi_command_complete_noio(r, ret);
return;
}
@@ -242,7 +270,7 @@ static void scsi_write_complete(void * opaque, int ret)
DPRINTF("block size %d\n", s->blocksize);
}
- scsi_command_complete(r, ret);
+ scsi_command_complete_noio(r, ret);
}
/* Write data to a scsi device. Returns nonzero on failure.
@@ -264,7 +292,7 @@ static void scsi_write_data(SCSIRequest *req)
scsi_req_ref(&r->req);
ret = execute_command(s->conf.blk, r, SG_DXFER_TO_DEV, scsi_write_complete);
if (ret < 0) {
- scsi_command_complete(r, ret);
+ scsi_command_complete_noio(r, ret);
}
}
@@ -306,7 +334,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
ret = execute_command(s->conf.blk, r, SG_DXFER_NONE,
scsi_command_complete);
if (ret < 0) {
- scsi_command_complete(r, ret);
+ scsi_command_complete_noio(r, ret);
return 0;
}
return 0;
@@ -328,6 +356,96 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
}
}
+static int read_naa_id(const uint8_t *p, uint64_t *p_wwn)
+{
+ int i;
+
+ if ((p[1] & 0xF) == 3) {
+ /* NAA designator type */
+ if (p[3] != 8) {
+ return -EINVAL;
+ }
+ *p_wwn = ldq_be_p(p + 4);
+ return 0;
+ }
+
+ if ((p[1] & 0xF) == 8) {
+ /* SCSI name string designator type */
+ if (p[3] < 20 || memcmp(&p[4], "naa.", 4)) {
+ return -EINVAL;
+ }
+ if (p[3] > 20 && p[24] != ',') {
+ return -EINVAL;
+ }
+ *p_wwn = 0;
+ for (i = 8; i < 24; i++) {
+ char c = toupper(p[i]);
+ c -= (c >= '0' && c <= '9' ? '0' : 'A' - 10);
+ *p_wwn = (*p_wwn << 4) | c;
+ }
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+void scsi_generic_read_device_identification(SCSIDevice *s)
+{
+ uint8_t cmd[6];
+ uint8_t buf[250];
+ uint8_t sensebuf[8];
+ sg_io_hdr_t io_header;
+ int ret;
+ int i, len;
+
+ memset(cmd, 0, sizeof(cmd));
+ memset(buf, 0, sizeof(buf));
+ cmd[0] = INQUIRY;
+ cmd[1] = 1;
+ cmd[2] = 0x83;
+ cmd[4] = sizeof(buf);
+
+ memset(&io_header, 0, sizeof(io_header));
+ io_header.interface_id = 'S';
+ io_header.dxfer_direction = SG_DXFER_FROM_DEV;
+ io_header.dxfer_len = sizeof(buf);
+ io_header.dxferp = buf;
+ io_header.cmdp = cmd;
+ io_header.cmd_len = sizeof(cmd);
+ io_header.mx_sb_len = sizeof(sensebuf);
+ io_header.sbp = sensebuf;
+ io_header.timeout = 6000; /* XXX */
+
+ ret = blk_ioctl(s->conf.blk, SG_IO, &io_header);
+ if (ret < 0 || io_header.driver_status || io_header.host_status) {
+ return;
+ }
+
+ len = MIN((buf[2] << 8) | buf[3], sizeof(buf) - 4);
+ for (i = 0; i + 3 <= len; ) {
+ const uint8_t *p = &buf[i + 4];
+ uint64_t wwn;
+
+ if (i + (p[3] + 4) > len) {
+ break;
+ }
+
+ if ((p[1] & 0x10) == 0) {
+ /* Associated with the logical unit */
+ if (read_naa_id(p, &wwn) == 0) {
+ s->wwn = wwn;
+ }
+ } else if ((p[1] & 0x10) == 0x10) {
+ /* Associated with the target port */
+ if (read_naa_id(p, &wwn) == 0) {
+ s->port_wwn = wwn;
+ }
+ }
+
+ i += p[3] + 4;
+ }
+}
+
static int get_stream_blocksize(BlockBackend *blk)
{
uint8_t cmd[6];
@@ -431,6 +549,8 @@ static void scsi_generic_realize(SCSIDevice *s, Error **errp)
}
DPRINTF("block size %d\n", s->blocksize);
+
+ scsi_generic_read_device_identification(s);
}
const SCSIReqOps scsi_generic_req_ops = {
diff --git a/qemu/hw/scsi/spapr_vscsi.c b/qemu/hw/scsi/spapr_vscsi.c
index 891424fae..b00edf7fd 100644
--- a/qemu/hw/scsi/spapr_vscsi.c
+++ b/qemu/hw/scsi/spapr_vscsi.c
@@ -31,6 +31,9 @@
* - Add indirect descriptors support
* - Maybe do autosense (PAPR seems to mandate it, linux doesn't care)
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "hw/scsi/scsi.h"
#include "block/scsi.h"
@@ -750,7 +753,6 @@ static void vscsi_report_luns(VSCSIState *s, vscsi_req *req)
len = n+8;
resp_data = g_malloc0(len);
- memset(resp_data, 0, len);
stl_be_p(resp_data, n);
i = found_lun0 ? 8 : 16;
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
diff --git a/qemu/hw/scsi/vhost-scsi.c b/qemu/hw/scsi/vhost-scsi.c
index a69918bef..9261d51da 100644
--- a/qemu/hw/scsi/vhost-scsi.c
+++ b/qemu/hw/scsi/vhost-scsi.c
@@ -14,8 +14,9 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include <sys/ioctl.h>
-#include "config.h"
#include "qemu/error-report.h"
#include "qemu/queue.h"
#include "monitor/monitor.h"
@@ -26,6 +27,8 @@
#include "hw/virtio/virtio-bus.h"
#include "hw/virtio/virtio-access.h"
#include "hw/fw-path-provider.h"
+#include "linux/vhost.h"
+#include "qemu/cutils.h"
/* Features supported by host kernel. */
static const int kernel_feature_bits[] = {
@@ -45,7 +48,7 @@ static int vhost_scsi_set_endpoint(VHostSCSI *s)
memset(&backend, 0, sizeof(backend));
pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn);
- ret = vhost_ops->vhost_call(&s->dev, VHOST_SCSI_SET_ENDPOINT, &backend);
+ ret = vhost_ops->vhost_scsi_set_endpoint(&s->dev, &backend);
if (ret < 0) {
return -errno;
}
@@ -60,7 +63,7 @@ static void vhost_scsi_clear_endpoint(VHostSCSI *s)
memset(&backend, 0, sizeof(backend));
pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn);
- vhost_ops->vhost_call(&s->dev, VHOST_SCSI_CLEAR_ENDPOINT, &backend);
+ vhost_ops->vhost_scsi_clear_endpoint(&s->dev, &backend);
}
static int vhost_scsi_start(VHostSCSI *s)
@@ -76,8 +79,7 @@ static int vhost_scsi_start(VHostSCSI *s)
return -ENOSYS;
}
- ret = vhost_ops->vhost_call(&s->dev,
- VHOST_SCSI_GET_ABI_VERSION, &abi_version);
+ ret = vhost_ops->vhost_scsi_get_abi_version(&s->dev, &abi_version);
if (ret < 0) {
return -errno;
}
@@ -118,7 +120,7 @@ static int vhost_scsi_start(VHostSCSI *s)
* enabling/disabling irqfd.
*/
for (i = 0; i < s->dev.nvqs; i++) {
- vhost_virtqueue_mask(&s->dev, vdev, i, false);
+ vhost_virtqueue_mask(&s->dev, vdev, s->dev.vq_index + i, false);
}
return ret;
@@ -217,11 +219,9 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp)
}
if (vs->conf.vhostfd) {
- vhostfd = monitor_fd_param(cur_mon, vs->conf.vhostfd, &err);
+ vhostfd = monitor_fd_param(cur_mon, vs->conf.vhostfd, errp);
if (vhostfd == -1) {
- error_setg(errp, "vhost-scsi: unable to parse vhostfd: %s",
- error_get_pretty(err));
- error_free(err);
+ error_prepend(errp, "vhost-scsi: unable to parse vhostfd: ");
return;
}
} else {
@@ -277,6 +277,7 @@ static void vhost_scsi_unrealize(DeviceState *dev, Error **errp)
/* This will stop vhost backend. */
vhost_scsi_set_status(vdev, 0);
+ vhost_dev_cleanup(&s->dev);
g_free(s->dev.vqs);
virtio_scsi_common_unrealize(dev, errp);
@@ -291,7 +292,7 @@ static char *vhost_scsi_get_fw_dev_path(FWPathProvider *p, BusState *bus,
{
VHostSCSI *s = VHOST_SCSI(dev);
/* format: channel@channel/vhost-scsi@target,lun */
- return g_strdup_printf("channel@%x/%s@%x,%x", s->channel,
+ return g_strdup_printf("/channel@%x/%s@%x,%x", s->channel,
qdev_fw_name(dev), s->target, s->lun);
}
diff --git a/qemu/hw/scsi/virtio-scsi-dataplane.c b/qemu/hw/scsi/virtio-scsi-dataplane.c
index 5575648a9..1a49f1e4b 100644
--- a/qemu/hw/scsi/virtio-scsi-dataplane.c
+++ b/qemu/hw/scsi/virtio-scsi-dataplane.c
@@ -11,6 +11,7 @@
*
*/
+#include "qemu/osdep.h"
#include "hw/virtio/virtio-scsi.h"
#include "qemu/error-report.h"
#include "sysemu/block-backend.h"
@@ -18,7 +19,6 @@
#include <block/scsi.h>
#include <hw/virtio/virtio-bus.h>
#include "hw/virtio/virtio-access.h"
-#include "stdio.h"
/* Context: QEMU global mutex held */
void virtio_scsi_set_iothread(VirtIOSCSI *s, IOThread *iothread)
@@ -38,120 +38,57 @@ void virtio_scsi_set_iothread(VirtIOSCSI *s, IOThread *iothread)
}
}
-static VirtIOSCSIVring *virtio_scsi_vring_init(VirtIOSCSI *s,
- VirtQueue *vq,
- EventNotifierHandler *handler,
- int n)
+static void virtio_scsi_data_plane_handle_cmd(VirtIODevice *vdev,
+ VirtQueue *vq)
{
- BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s)));
- VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
- VirtIOSCSIVring *r;
- int rc;
+ VirtIOSCSI *s = (VirtIOSCSI *)vdev;
- /* Set up virtqueue notify */
- rc = k->set_host_notifier(qbus->parent, n, true);
- if (rc != 0) {
- fprintf(stderr, "virtio-scsi: Failed to set host notifier (%d)\n",
- rc);
- s->dataplane_fenced = true;
- return NULL;
- }
-
- r = g_slice_new(VirtIOSCSIVring);
- r->host_notifier = *virtio_queue_get_host_notifier(vq);
- r->guest_notifier = *virtio_queue_get_guest_notifier(vq);
- aio_set_event_notifier(s->ctx, &r->host_notifier, handler);
-
- r->parent = s;
-
- if (!vring_setup(&r->vring, VIRTIO_DEVICE(s), n)) {
- fprintf(stderr, "virtio-scsi: VRing setup failed\n");
- goto fail_vring;
- }
- return r;
-
-fail_vring:
- aio_set_event_notifier(s->ctx, &r->host_notifier, NULL);
- k->set_host_notifier(qbus->parent, n, false);
- g_slice_free(VirtIOSCSIVring, r);
- return NULL;
+ assert(s->ctx && s->dataplane_started);
+ virtio_scsi_handle_cmd_vq(s, vq);
}
-VirtIOSCSIReq *virtio_scsi_pop_req_vring(VirtIOSCSI *s,
- VirtIOSCSIVring *vring)
+static void virtio_scsi_data_plane_handle_ctrl(VirtIODevice *vdev,
+ VirtQueue *vq)
{
- VirtIOSCSIReq *req = virtio_scsi_init_req(s, NULL);
- int r;
-
- req->vring = vring;
- r = vring_pop((VirtIODevice *)s, &vring->vring, &req->elem);
- if (r < 0) {
- virtio_scsi_free_req(req);
- req = NULL;
- }
- return req;
-}
+ VirtIOSCSI *s = VIRTIO_SCSI(vdev);
-void virtio_scsi_vring_push_notify(VirtIOSCSIReq *req)
-{
- VirtIODevice *vdev = VIRTIO_DEVICE(req->vring->parent);
-
- vring_push(vdev, &req->vring->vring, &req->elem,
- req->qsgl.size + req->resp_iov.size);
-
- if (vring_should_notify(vdev, &req->vring->vring)) {
- event_notifier_set(&req->vring->guest_notifier);
- }
+ assert(s->ctx && s->dataplane_started);
+ virtio_scsi_handle_ctrl_vq(s, vq);
}
-static void virtio_scsi_iothread_handle_ctrl(EventNotifier *notifier)
+static void virtio_scsi_data_plane_handle_event(VirtIODevice *vdev,
+ VirtQueue *vq)
{
- VirtIOSCSIVring *vring = container_of(notifier,
- VirtIOSCSIVring, host_notifier);
- VirtIOSCSI *s = VIRTIO_SCSI(vring->parent);
- VirtIOSCSIReq *req;
-
- event_notifier_test_and_clear(notifier);
- while ((req = virtio_scsi_pop_req_vring(s, vring))) {
- virtio_scsi_handle_ctrl_req(s, req);
- }
+ VirtIOSCSI *s = VIRTIO_SCSI(vdev);
+
+ assert(s->ctx && s->dataplane_started);
+ virtio_scsi_handle_event_vq(s, vq);
}
-static void virtio_scsi_iothread_handle_event(EventNotifier *notifier)
+static int virtio_scsi_vring_init(VirtIOSCSI *s, VirtQueue *vq, int n,
+ void (*fn)(VirtIODevice *vdev, VirtQueue *vq))
{
- VirtIOSCSIVring *vring = container_of(notifier,
- VirtIOSCSIVring, host_notifier);
- VirtIOSCSI *s = vring->parent;
- VirtIODevice *vdev = VIRTIO_DEVICE(s);
-
- event_notifier_test_and_clear(notifier);
+ BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s)));
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
+ int rc;
- if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
- return;
+ /* Set up virtqueue notify */
+ rc = k->set_host_notifier(qbus->parent, n, true);
+ if (rc != 0) {
+ fprintf(stderr, "virtio-scsi: Failed to set host notifier (%d)\n",
+ rc);
+ s->dataplane_fenced = true;
+ return rc;
}
- if (s->events_dropped) {
- virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0);
- }
+ virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, fn);
+ return 0;
}
-static void virtio_scsi_iothread_handle_cmd(EventNotifier *notifier)
+void virtio_scsi_dataplane_notify(VirtIODevice *vdev, VirtIOSCSIReq *req)
{
- VirtIOSCSIVring *vring = container_of(notifier,
- VirtIOSCSIVring, host_notifier);
- VirtIOSCSI *s = (VirtIOSCSI *)vring->parent;
- VirtIOSCSIReq *req, *next;
- QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs);
-
- event_notifier_test_and_clear(notifier);
- while ((req = virtio_scsi_pop_req_vring(s, vring))) {
- if (virtio_scsi_handle_cmd_req_prepare(s, req)) {
- QTAILQ_INSERT_TAIL(&reqs, req, next);
- }
- }
-
- QTAILQ_FOREACH_SAFE(req, &reqs, next, next) {
- virtio_scsi_handle_cmd_req_submit(s, req);
+ if (virtio_should_notify(vdev, req->vq)) {
+ event_notifier_set(virtio_queue_get_guest_notifier(req->vq));
}
}
@@ -161,43 +98,10 @@ static void virtio_scsi_clear_aio(VirtIOSCSI *s)
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
int i;
- if (s->ctrl_vring) {
- aio_set_event_notifier(s->ctx, &s->ctrl_vring->host_notifier, NULL);
- }
- if (s->event_vring) {
- aio_set_event_notifier(s->ctx, &s->event_vring->host_notifier, NULL);
- }
- if (s->cmd_vrings) {
- for (i = 0; i < vs->conf.num_queues && s->cmd_vrings[i]; i++) {
- aio_set_event_notifier(s->ctx, &s->cmd_vrings[i]->host_notifier, NULL);
- }
- }
-}
-
-static void virtio_scsi_vring_teardown(VirtIOSCSI *s)
-{
- VirtIODevice *vdev = VIRTIO_DEVICE(s);
- VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
- int i;
-
- if (s->ctrl_vring) {
- vring_teardown(&s->ctrl_vring->vring, vdev, 0);
- g_slice_free(VirtIOSCSIVring, s->ctrl_vring);
- s->ctrl_vring = NULL;
- }
- if (s->event_vring) {
- vring_teardown(&s->event_vring->vring, vdev, 1);
- g_slice_free(VirtIOSCSIVring, s->event_vring);
- s->event_vring = NULL;
- }
- if (s->cmd_vrings) {
- for (i = 0; i < vs->conf.num_queues && s->cmd_vrings[i]; i++) {
- vring_teardown(&s->cmd_vrings[i]->vring, vdev, 2 + i);
- g_slice_free(VirtIOSCSIVring, s->cmd_vrings[i]);
- s->cmd_vrings[i] = NULL;
- }
- free(s->cmd_vrings);
- s->cmd_vrings = NULL;
+ virtio_queue_aio_set_host_notifier_handler(vs->ctrl_vq, s->ctx, NULL);
+ virtio_queue_aio_set_host_notifier_handler(vs->event_vq, s->ctx, NULL);
+ for (i = 0; i < vs->conf.num_queues; i++) {
+ virtio_queue_aio_set_host_notifier_handler(vs->cmd_vqs[i], s->ctx, NULL);
}
}
@@ -224,30 +128,24 @@ void virtio_scsi_dataplane_start(VirtIOSCSI *s)
if (rc != 0) {
fprintf(stderr, "virtio-scsi: Failed to set guest notifiers (%d), "
"ensure -enable-kvm is set\n", rc);
- s->dataplane_fenced = true;
goto fail_guest_notifiers;
}
aio_context_acquire(s->ctx);
- s->ctrl_vring = virtio_scsi_vring_init(s, vs->ctrl_vq,
- virtio_scsi_iothread_handle_ctrl,
- 0);
- if (!s->ctrl_vring) {
+ rc = virtio_scsi_vring_init(s, vs->ctrl_vq, 0,
+ virtio_scsi_data_plane_handle_ctrl);
+ if (rc) {
goto fail_vrings;
}
- s->event_vring = virtio_scsi_vring_init(s, vs->event_vq,
- virtio_scsi_iothread_handle_event,
- 1);
- if (!s->event_vring) {
+ rc = virtio_scsi_vring_init(s, vs->event_vq, 1,
+ virtio_scsi_data_plane_handle_event);
+ if (rc) {
goto fail_vrings;
}
- s->cmd_vrings = g_new(VirtIOSCSIVring *, vs->conf.num_queues);
for (i = 0; i < vs->conf.num_queues; i++) {
- s->cmd_vrings[i] =
- virtio_scsi_vring_init(s, vs->cmd_vqs[i],
- virtio_scsi_iothread_handle_cmd,
- i + 2);
- if (!s->cmd_vrings[i]) {
+ rc = virtio_scsi_vring_init(s, vs->cmd_vqs[i], i + 2,
+ virtio_scsi_data_plane_handle_cmd);
+ if (rc) {
goto fail_vrings;
}
}
@@ -260,13 +158,14 @@ void virtio_scsi_dataplane_start(VirtIOSCSI *s)
fail_vrings:
virtio_scsi_clear_aio(s);
aio_context_release(s->ctx);
- virtio_scsi_vring_teardown(s);
for (i = 0; i < vs->conf.num_queues + 2; i++) {
k->set_host_notifier(qbus->parent, i, false);
}
k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false);
fail_guest_notifiers:
+ s->dataplane_fenced = true;
s->dataplane_starting = false;
+ s->dataplane_started = true;
}
/* Context: QEMU global mutex held */
@@ -277,12 +176,14 @@ void virtio_scsi_dataplane_stop(VirtIOSCSI *s)
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
int i;
+ if (!s->dataplane_started || s->dataplane_stopping) {
+ return;
+ }
+
/* Better luck next time. */
if (s->dataplane_fenced) {
s->dataplane_fenced = false;
- return;
- }
- if (!s->dataplane_started || s->dataplane_stopping) {
+ s->dataplane_started = false;
return;
}
s->dataplane_stopping = true;
@@ -290,21 +191,12 @@ void virtio_scsi_dataplane_stop(VirtIOSCSI *s)
aio_context_acquire(s->ctx);
- aio_set_event_notifier(s->ctx, &s->ctrl_vring->host_notifier, NULL);
- aio_set_event_notifier(s->ctx, &s->event_vring->host_notifier, NULL);
- for (i = 0; i < vs->conf.num_queues; i++) {
- aio_set_event_notifier(s->ctx, &s->cmd_vrings[i]->host_notifier, NULL);
- }
+ virtio_scsi_clear_aio(s);
blk_drain_all(); /* ensure there are no in-flight requests */
aio_context_release(s->ctx);
- /* Sync vring state back to virtqueue so that non-dataplane request
- * processing can continue when we disable the host notifier below.
- */
- virtio_scsi_vring_teardown(s);
-
for (i = 0; i < vs->conf.num_queues + 2; i++) {
k->set_host_notifier(qbus->parent, i, false);
}
diff --git a/qemu/hw/scsi/virtio-scsi.c b/qemu/hw/scsi/virtio-scsi.c
index 811c3da8b..30415c6a9 100644
--- a/qemu/hw/scsi/virtio-scsi.c
+++ b/qemu/hw/scsi/virtio-scsi.c
@@ -13,6 +13,8 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "standard-headers/linux/virtio_ids.h"
#include "hw/virtio/virtio-scsi.h"
#include "qemu/error-report.h"
@@ -22,7 +24,6 @@
#include <block/scsi.h>
#include <hw/virtio/virtio-bus.h>
#include "hw/virtio/virtio-access.h"
-#include "migration/migration.h"
static inline int virtio_scsi_get_lun(uint8_t *lun)
{
@@ -40,29 +41,23 @@ static inline SCSIDevice *virtio_scsi_device_find(VirtIOSCSI *s, uint8_t *lun)
return scsi_device_find(&s->bus, 0, lun[1], virtio_scsi_get_lun(lun));
}
-VirtIOSCSIReq *virtio_scsi_init_req(VirtIOSCSI *s, VirtQueue *vq)
+void virtio_scsi_init_req(VirtIOSCSI *s, VirtQueue *vq, VirtIOSCSIReq *req)
{
- VirtIOSCSIReq *req;
- VirtIOSCSICommon *vs = (VirtIOSCSICommon *)s;
- const size_t zero_skip = offsetof(VirtIOSCSIReq, elem)
- + sizeof(VirtQueueElement);
+ const size_t zero_skip =
+ offsetof(VirtIOSCSIReq, resp_iov) + sizeof(req->resp_iov);
- req = g_slice_alloc(sizeof(*req) + vs->cdb_size);
req->vq = vq;
req->dev = s;
qemu_sglist_init(&req->qsgl, DEVICE(s), 8, &address_space_memory);
qemu_iovec_init(&req->resp_iov, 1);
memset((uint8_t *)req + zero_skip, 0, sizeof(*req) - zero_skip);
- return req;
}
void virtio_scsi_free_req(VirtIOSCSIReq *req)
{
- VirtIOSCSICommon *vs = (VirtIOSCSICommon *)req->dev;
-
qemu_iovec_destroy(&req->resp_iov);
qemu_sglist_destroy(&req->qsgl);
- g_slice_free1(sizeof(*req) + vs->cdb_size, req);
+ g_free(req);
}
static void virtio_scsi_complete_req(VirtIOSCSIReq *req)
@@ -72,11 +67,10 @@ static void virtio_scsi_complete_req(VirtIOSCSIReq *req)
VirtIODevice *vdev = VIRTIO_DEVICE(s);
qemu_iovec_from_buf(&req->resp_iov, 0, &req->resp, req->resp_size);
- if (req->vring) {
- assert(req->vq == NULL);
- virtio_scsi_vring_push_notify(req);
+ virtqueue_push(vq, &req->elem, req->qsgl.size + req->resp_iov.size);
+ if (s->dataplane_started && !s->dataplane_fenced) {
+ virtio_scsi_dataplane_notify(vdev, req);
} else {
- virtqueue_push(vq, &req->elem, req->qsgl.size + req->resp_iov.size);
virtio_notify(vdev, vq);
}
@@ -145,7 +139,7 @@ static int virtio_scsi_parse_req(VirtIOSCSIReq *req,
*
* TODO: always disable this workaround for virtio 1.0 devices.
*/
- if (!virtio_has_feature(vdev, VIRTIO_F_ANY_LAYOUT)) {
+ if (!virtio_vdev_has_feature(vdev, VIRTIO_F_ANY_LAYOUT)) {
if (req->elem.out_num) {
req_size = req->elem.out_sg[0].iov_len;
}
@@ -176,11 +170,14 @@ static int virtio_scsi_parse_req(VirtIOSCSIReq *req,
static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq)
{
- VirtIOSCSIReq *req = virtio_scsi_init_req(s, vq);
- if (!virtqueue_pop(vq, &req->elem)) {
- virtio_scsi_free_req(req);
+ VirtIOSCSICommon *vs = (VirtIOSCSICommon *)s;
+ VirtIOSCSIReq *req;
+
+ req = virtqueue_pop(vq, sizeof(VirtIOSCSIReq) + vs->cdb_size);
+ if (!req) {
return NULL;
}
+ virtio_scsi_init_req(s, vq, req);
return req;
}
@@ -192,7 +189,7 @@ static void virtio_scsi_save_request(QEMUFile *f, SCSIRequest *sreq)
assert(n < vs->conf.num_queues);
qemu_put_be32s(f, &n);
- qemu_put_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem));
+ qemu_put_virtqueue_element(f, &req->elem);
}
static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq)
@@ -205,17 +202,8 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq)
qemu_get_be32s(f, &n);
assert(n < vs->conf.num_queues);
- req = virtio_scsi_init_req(s, vs->cmd_vqs[n]);
- qemu_get_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem));
- /* TODO: add a way for SCSIBusInfo's load_request to fail,
- * and fail migration instead of asserting here.
- * When we do, we might be able to re-enable NDEBUG below.
- */
-#ifdef NDEBUG
-#error building with NDEBUG is not supported
-#endif
- assert(req->elem.in_num <= ARRAY_SIZE(req->elem.in_sg));
- assert(req->elem.out_num <= ARRAY_SIZE(req->elem.out_sg));
+ req = qemu_get_virtqueue_element(f, sizeof(VirtIOSCSIReq) + vs->cdb_size);
+ virtio_scsi_init_req(s, vs->cmd_vqs[n], req);
if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICmdReq) + vs->cdb_size,
sizeof(VirtIOSCSICmdResp) + vs->sense_size) < 0) {
@@ -245,7 +233,7 @@ static void virtio_scsi_cancel_notify(Notifier *notifier, void *data)
if (--n->tmf_req->remaining == 0) {
virtio_scsi_complete_req(n->tmf_req);
}
- g_slice_free(VirtIOSCSICancelNotifier, n);
+ g_free(n);
}
/* Return 0 if the request is ready to be completed and return to guest;
@@ -259,7 +247,7 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
int target;
int ret = 0;
- if (s->dataplane_started) {
+ if (s->dataplane_started && d) {
assert(blk_get_aio_context(d->conf.blk) == s->ctx);
}
/* Here VIRTIO_SCSI_S_OK means "FUNCTION COMPLETE". */
@@ -296,7 +284,7 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
VirtIOSCSICancelNotifier *notifier;
req->remaining = 1;
- notifier = g_slice_new(VirtIOSCSICancelNotifier);
+ notifier = g_new(VirtIOSCSICancelNotifier, 1);
notifier->tmf_req = req;
notifier->notifier.notify = virtio_scsi_cancel_notify;
scsi_req_cancel_async(r, &notifier->notifier);
@@ -345,7 +333,7 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
VirtIOSCSICancelNotifier *notifier;
req->remaining++;
- notifier = g_slice_new(VirtIOSCSICancelNotifier);
+ notifier = g_new(VirtIOSCSICancelNotifier, 1);
notifier->notifier.notify = virtio_scsi_cancel_notify;
notifier->tmf_req = req;
scsi_req_cancel_async(r, &notifier->notifier);
@@ -361,7 +349,7 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
target = req->req.tmf.lun[1];
s->resetting++;
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
- d = DO_UPCAST(SCSIDevice, qdev, kid->child);
+ d = SCSI_DEVICE(kid->child);
if (d->channel == 0 && d->id == target) {
qdev_reset_all(&d->qdev);
}
@@ -386,7 +374,7 @@ fail:
return ret;
}
-void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req)
+static void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req)
{
VirtIODevice *vdev = (VirtIODevice *)s;
uint32_t type;
@@ -424,20 +412,28 @@ void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req)
}
}
-static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
+void virtio_scsi_handle_ctrl_vq(VirtIOSCSI *s, VirtQueue *vq)
{
- VirtIOSCSI *s = (VirtIOSCSI *)vdev;
VirtIOSCSIReq *req;
- if (s->ctx && !s->dataplane_disabled) {
- virtio_scsi_dataplane_start(s);
- return;
- }
while ((req = virtio_scsi_pop_req(s, vq))) {
virtio_scsi_handle_ctrl_req(s, req);
}
}
+static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOSCSI *s = (VirtIOSCSI *)vdev;
+
+ if (s->ctx) {
+ virtio_scsi_dataplane_start(s);
+ if (!s->dataplane_fenced) {
+ return;
+ }
+ }
+ virtio_scsi_handle_ctrl_vq(s, vq);
+}
+
static void virtio_scsi_complete_cmd_req(VirtIOSCSIReq *req)
{
/* Sense data is not in req->resp and is copied separately
@@ -520,7 +516,7 @@ static void virtio_scsi_fail_cmd_req(VirtIOSCSIReq *req)
virtio_scsi_complete_cmd_req(req);
}
-bool virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req)
+static bool virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req)
{
VirtIOSCSICommon *vs = &s->parent_obj;
SCSIDevice *d;
@@ -562,7 +558,7 @@ bool virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req)
return true;
}
-void virtio_scsi_handle_cmd_req_submit(VirtIOSCSI *s, VirtIOSCSIReq *req)
+static void virtio_scsi_handle_cmd_req_submit(VirtIOSCSI *s, VirtIOSCSIReq *req)
{
SCSIRequest *sreq = req->sreq;
if (scsi_req_enqueue(sreq)) {
@@ -572,17 +568,11 @@ void virtio_scsi_handle_cmd_req_submit(VirtIOSCSI *s, VirtIOSCSIReq *req)
scsi_req_unref(sreq);
}
-static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq)
+void virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq)
{
- /* use non-QOM casts in the data path */
- VirtIOSCSI *s = (VirtIOSCSI *)vdev;
VirtIOSCSIReq *req, *next;
QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs);
- if (s->ctx && !s->dataplane_disabled) {
- virtio_scsi_dataplane_start(s);
- return;
- }
while ((req = virtio_scsi_pop_req(s, vq))) {
if (virtio_scsi_handle_cmd_req_prepare(s, req)) {
QTAILQ_INSERT_TAIL(&reqs, req, next);
@@ -594,6 +584,20 @@ static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq)
}
}
+static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq)
+{
+ /* use non-QOM casts in the data path */
+ VirtIOSCSI *s = (VirtIOSCSI *)vdev;
+
+ if (s->ctx) {
+ virtio_scsi_dataplane_start(s);
+ if (!s->dataplane_fenced) {
+ return;
+ }
+ }
+ virtio_scsi_handle_cmd_vq(s, vq);
+}
+
static void virtio_scsi_get_config(VirtIODevice *vdev,
uint8_t *config)
{
@@ -662,6 +666,11 @@ static void virtio_scsi_reset(VirtIODevice *vdev)
static void virtio_scsi_save(QEMUFile *f, void *opaque)
{
VirtIODevice *vdev = VIRTIO_DEVICE(opaque);
+ VirtIOSCSI *s = VIRTIO_SCSI(vdev);
+
+ if (s->dataplane_started) {
+ virtio_scsi_dataplane_stop(s);
+ }
virtio_save(vdev, f);
}
@@ -694,11 +703,7 @@ void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev,
aio_context_acquire(s->ctx);
}
- if (s->dataplane_started) {
- req = virtio_scsi_pop_req_vring(s, s->event_vring);
- } else {
- req = virtio_scsi_pop_req(s, vs->event_vq);
- }
+ req = virtio_scsi_pop_req(s, vs->event_vq);
if (!req) {
s->events_dropped = true;
goto out;
@@ -736,17 +741,24 @@ out:
}
}
+void virtio_scsi_handle_event_vq(VirtIOSCSI *s, VirtQueue *vq)
+{
+ if (s->events_dropped) {
+ virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0);
+ }
+}
+
static void virtio_scsi_handle_event(VirtIODevice *vdev, VirtQueue *vq)
{
VirtIOSCSI *s = VIRTIO_SCSI(vdev);
- if (s->ctx && !s->dataplane_disabled) {
+ if (s->ctx) {
virtio_scsi_dataplane_start(s);
- return;
- }
- if (s->events_dropped) {
- virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0);
+ if (!s->dataplane_fenced) {
+ return;
+ }
}
+ virtio_scsi_handle_event_vq(s, vq);
}
static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense)
@@ -754,13 +766,29 @@ static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense)
VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus);
VirtIODevice *vdev = VIRTIO_DEVICE(s);
- if (virtio_has_feature(vdev, VIRTIO_SCSI_F_CHANGE) &&
+ if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_CHANGE) &&
dev->type != TYPE_ROM) {
virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_PARAM_CHANGE,
sense.asc | (sense.ascq << 8));
}
}
+static void virtio_scsi_blk_insert_notifier(Notifier *n, void *data)
+{
+ VirtIOSCSIBlkChangeNotifier *cn = DO_UPCAST(VirtIOSCSIBlkChangeNotifier,
+ n, n);
+ assert(cn->sd->conf.blk == data);
+ blk_op_block_all(cn->sd->conf.blk, cn->s->blocker);
+}
+
+static void virtio_scsi_blk_remove_notifier(Notifier *n, void *data)
+{
+ VirtIOSCSIBlkChangeNotifier *cn = DO_UPCAST(VirtIOSCSIBlkChangeNotifier,
+ n, n);
+ assert(cn->sd->conf.blk == data);
+ blk_op_unblock_all(cn->sd->conf.blk, cn->s->blocker);
+}
+
static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp)
{
@@ -768,7 +796,9 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev,
VirtIOSCSI *s = VIRTIO_SCSI(vdev);
SCSIDevice *sd = SCSI_DEVICE(dev);
- if (s->ctx && !s->dataplane_disabled) {
+ if (s->ctx && !s->dataplane_fenced) {
+ VirtIOSCSIBlkChangeNotifier *insert_notifier, *remove_notifier;
+
if (blk_op_is_blocked(sd->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) {
return;
}
@@ -776,9 +806,23 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev,
aio_context_acquire(s->ctx);
blk_set_aio_context(sd->conf.blk, s->ctx);
aio_context_release(s->ctx);
+
+ insert_notifier = g_new0(VirtIOSCSIBlkChangeNotifier, 1);
+ insert_notifier->n.notify = virtio_scsi_blk_insert_notifier;
+ insert_notifier->s = s;
+ insert_notifier->sd = sd;
+ blk_add_insert_bs_notifier(sd->conf.blk, &insert_notifier->n);
+ QTAILQ_INSERT_TAIL(&s->insert_notifiers, insert_notifier, next);
+
+ remove_notifier = g_new0(VirtIOSCSIBlkChangeNotifier, 1);
+ remove_notifier->n.notify = virtio_scsi_blk_remove_notifier;
+ remove_notifier->s = s;
+ remove_notifier->sd = sd;
+ blk_add_remove_bs_notifier(sd->conf.blk, &remove_notifier->n);
+ QTAILQ_INSERT_TAIL(&s->remove_notifiers, remove_notifier, next);
}
- if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) {
+ if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) {
virtio_scsi_push_event(s, sd,
VIRTIO_SCSI_T_TRANSPORT_RESET,
VIRTIO_SCSI_EVT_RESET_RESCAN);
@@ -791,8 +835,9 @@ static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev,
VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev);
VirtIOSCSI *s = VIRTIO_SCSI(vdev);
SCSIDevice *sd = SCSI_DEVICE(dev);
+ VirtIOSCSIBlkChangeNotifier *insert_notifier, *remove_notifier;
- if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) {
+ if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) {
virtio_scsi_push_event(s, sd,
VIRTIO_SCSI_T_TRANSPORT_RESET,
VIRTIO_SCSI_EVT_RESET_REMOVED);
@@ -801,6 +846,25 @@ static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev,
if (s->ctx) {
blk_op_unblock_all(sd->conf.blk, s->blocker);
}
+
+ QTAILQ_FOREACH(insert_notifier, &s->insert_notifiers, next) {
+ if (insert_notifier->sd == sd) {
+ notifier_remove(&insert_notifier->n);
+ QTAILQ_REMOVE(&s->insert_notifiers, insert_notifier, next);
+ g_free(insert_notifier);
+ break;
+ }
+ }
+
+ QTAILQ_FOREACH(remove_notifier, &s->remove_notifiers, next) {
+ if (remove_notifier->sd == sd) {
+ notifier_remove(&remove_notifier->n);
+ QTAILQ_REMOVE(&s->remove_notifiers, remove_notifier, next);
+ g_free(remove_notifier);
+ break;
+ }
+ }
+
qdev_simple_device_unplug_cb(hotplug_dev, dev, errp);
}
@@ -856,31 +920,6 @@ void virtio_scsi_common_realize(DeviceState *dev, Error **errp,
}
}
-/* Disable dataplane thread during live migration since it does not
- * update the dirty memory bitmap yet.
- */
-static void virtio_scsi_migration_state_changed(Notifier *notifier, void *data)
-{
- VirtIOSCSI *s = container_of(notifier, VirtIOSCSI,
- migration_state_notifier);
- MigrationState *mig = data;
-
- if (migration_in_setup(mig)) {
- if (!s->dataplane_started) {
- return;
- }
- virtio_scsi_dataplane_stop(s);
- s->dataplane_disabled = true;
- } else if (migration_has_finished(mig) ||
- migration_has_failed(mig)) {
- if (s->dataplane_started) {
- return;
- }
- blk_drain_all(); /* complete in-flight non-dataplane requests */
- s->dataplane_disabled = false;
- }
-}
-
static void virtio_scsi_device_realize(DeviceState *dev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
@@ -911,10 +950,11 @@ static void virtio_scsi_device_realize(DeviceState *dev, Error **errp)
register_savevm(dev, "virtio-scsi", virtio_scsi_id++, 1,
virtio_scsi_save, virtio_scsi_load, s);
- s->migration_state_notifier.notify = virtio_scsi_migration_state_changed;
- add_migration_state_change_notifier(&s->migration_state_notifier);
error_setg(&s->blocker, "block device is in use by data plane");
+
+ QTAILQ_INIT(&s->insert_notifiers);
+ QTAILQ_INIT(&s->remove_notifiers);
}
static void virtio_scsi_instance_init(Object *obj)
@@ -943,8 +983,6 @@ static void virtio_scsi_device_unrealize(DeviceState *dev, Error **errp)
error_free(s->blocker);
unregister_savevm(dev, "virtio-scsi", s);
- remove_migration_state_change_notifier(&s->migration_state_notifier);
-
virtio_scsi_common_unrealize(dev, errp);
}
diff --git a/qemu/hw/scsi/vmw_pvscsi.c b/qemu/hw/scsi/vmw_pvscsi.c
index 9c71f31fe..e690b4ec0 100644
--- a/qemu/hw/scsi/vmw_pvscsi.c
+++ b/qemu/hw/scsi/vmw_pvscsi.c
@@ -25,6 +25,8 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/scsi/scsi.h"
#include <block/scsi.h>
#include "hw/pci/msi.h"
@@ -32,7 +34,6 @@
#include "trace.h"
-#define PVSCSI_MSI_OFFSET (0x50)
#define PVSCSI_USE_64BIT (true)
#define PVSCSI_PER_VECTOR_MASK (false)
@@ -49,9 +50,33 @@
(stl_le_pci_dma(&container_of(m, PVSCSIState, rings)->parent_obj, \
(m)->rs_pa + offsetof(struct PVSCSIRingsState, field), val))
+typedef struct PVSCSIClass {
+ PCIDeviceClass parent_class;
+ DeviceRealize parent_dc_realize;
+} PVSCSIClass;
+
#define TYPE_PVSCSI "pvscsi"
#define PVSCSI(obj) OBJECT_CHECK(PVSCSIState, (obj), TYPE_PVSCSI)
+#define PVSCSI_DEVICE_CLASS(klass) \
+ OBJECT_CLASS_CHECK(PVSCSIClass, (klass), TYPE_PVSCSI)
+#define PVSCSI_DEVICE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(PVSCSIClass, (obj), TYPE_PVSCSI)
+
+/* Compatability flags for migration */
+#define PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT 0
+#define PVSCSI_COMPAT_OLD_PCI_CONFIGURATION \
+ (1 << PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT)
+#define PVSCSI_COMPAT_DISABLE_PCIE_BIT 1
+#define PVSCSI_COMPAT_DISABLE_PCIE \
+ (1 << PVSCSI_COMPAT_DISABLE_PCIE_BIT)
+
+#define PVSCSI_USE_OLD_PCI_CONFIGURATION(s) \
+ ((s)->compat_flags & PVSCSI_COMPAT_OLD_PCI_CONFIGURATION)
+#define PVSCSI_MSI_OFFSET(s) \
+ (PVSCSI_USE_OLD_PCI_CONFIGURATION(s) ? 0x50 : 0x7c)
+#define PVSCSI_EXP_EP_OFFSET (0x40)
+
typedef struct PVSCSIRingInfo {
uint64_t rs_pa;
uint32_t txr_len_mask;
@@ -100,6 +125,8 @@ typedef struct {
PVSCSIRingInfo rings; /* Data transfer rings manager */
uint32_t resetting; /* Reset in progress */
+
+ uint32_t compat_flags;
} PVSCSIState;
typedef struct PVSCSIRequest {
@@ -1019,7 +1046,7 @@ pvscsi_init_msi(PVSCSIState *s)
int res;
PCIDevice *d = PCI_DEVICE(s);
- res = msi_init(d, PVSCSI_MSI_OFFSET, PVSCSI_MSIX_NUM_VECTORS,
+ res = msi_init(d, PVSCSI_MSI_OFFSET(s), PVSCSI_MSIX_NUM_VECTORS,
PVSCSI_USE_64BIT, PVSCSI_PER_VECTOR_MASK);
if (res < 0) {
trace_pvscsi_init_msi_fail(res);
@@ -1069,9 +1096,16 @@ pvscsi_init(PCIDevice *pci_dev)
trace_pvscsi_state("init");
- /* PCI subsystem ID */
- pci_dev->config[PCI_SUBSYSTEM_ID] = 0x00;
- pci_dev->config[PCI_SUBSYSTEM_ID + 1] = 0x10;
+ /* PCI subsystem ID, subsystem vendor ID, revision */
+ if (PVSCSI_USE_OLD_PCI_CONFIGURATION(s)) {
+ pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, 0x1000);
+ } else {
+ pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID,
+ PCI_VENDOR_ID_VMWARE);
+ pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID,
+ PCI_DEVICE_ID_VMWARE_PVSCSI);
+ pci_config_set_revision(pci_dev->config, 0x2);
+ }
/* PCI latency timer = 255 */
pci_dev->config[PCI_LATENCY_TIMER] = 0xff;
@@ -1085,6 +1119,10 @@ pvscsi_init(PCIDevice *pci_dev)
pvscsi_init_msi(s);
+ if (pci_is_express(pci_dev) && pci_bus_is_express(pci_dev->bus)) {
+ pcie_endpoint_cap_init(pci_dev, PVSCSI_EXP_EP_OFFSET);
+ }
+
s->completion_worker = qemu_bh_new(pvscsi_process_completion_queue, s);
if (!s->completion_worker) {
pvscsi_cleanup_msi(s);
@@ -1139,6 +1177,27 @@ pvscsi_post_load(void *opaque, int version_id)
return 0;
}
+static bool pvscsi_vmstate_need_pcie_device(void *opaque)
+{
+ PVSCSIState *s = PVSCSI(opaque);
+
+ return !(s->compat_flags & PVSCSI_COMPAT_DISABLE_PCIE);
+}
+
+static bool pvscsi_vmstate_test_pci_device(void *opaque, int version_id)
+{
+ return !pvscsi_vmstate_need_pcie_device(opaque);
+}
+
+static const VMStateDescription vmstate_pvscsi_pcie_device = {
+ .name = "pvscsi/pcie",
+ .needed = pvscsi_vmstate_need_pcie_device,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCIE_DEVICE(parent_obj, PVSCSIState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_pvscsi = {
.name = "pvscsi",
.version_id = 0,
@@ -1146,7 +1205,9 @@ static const VMStateDescription vmstate_pvscsi = {
.pre_save = pvscsi_pre_save,
.post_load = pvscsi_post_load,
.fields = (VMStateField[]) {
- VMSTATE_PCI_DEVICE(parent_obj, PVSCSIState),
+ VMSTATE_STRUCT_TEST(parent_obj, PVSCSIState,
+ pvscsi_vmstate_test_pci_device, 0,
+ vmstate_pci_device, PCIDevice),
VMSTATE_UINT8(msi_used, PVSCSIState),
VMSTATE_UINT32(resetting, PVSCSIState),
VMSTATE_UINT64(reg_interrupt_status, PVSCSIState),
@@ -1171,18 +1232,40 @@ static const VMStateDescription vmstate_pvscsi = {
VMSTATE_UINT64(rings.filled_cmp_ptr, PVSCSIState),
VMSTATE_END_OF_LIST()
+ },
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_pvscsi_pcie_device,
+ NULL
}
};
static Property pvscsi_properties[] = {
DEFINE_PROP_UINT8("use_msg", PVSCSIState, use_msg, 1),
+ DEFINE_PROP_BIT("x-old-pci-configuration", PVSCSIState, compat_flags,
+ PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT, false),
+ DEFINE_PROP_BIT("x-disable-pcie", PVSCSIState, compat_flags,
+ PVSCSI_COMPAT_DISABLE_PCIE_BIT, false),
DEFINE_PROP_END_OF_LIST(),
};
+static void pvscsi_realize(DeviceState *qdev, Error **errp)
+{
+ PVSCSIClass *pvs_c = PVSCSI_DEVICE_GET_CLASS(qdev);
+ PCIDevice *pci_dev = PCI_DEVICE(qdev);
+ PVSCSIState *s = PVSCSI(qdev);
+
+ if (!(s->compat_flags & PVSCSI_COMPAT_DISABLE_PCIE)) {
+ pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
+ }
+
+ pvs_c->parent_dc_realize(qdev, errp);
+}
+
static void pvscsi_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ PVSCSIClass *pvs_k = PVSCSI_DEVICE_CLASS(klass);
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
k->init = pvscsi_init;
@@ -1191,6 +1274,8 @@ static void pvscsi_class_init(ObjectClass *klass, void *data)
k->device_id = PCI_DEVICE_ID_VMWARE_PVSCSI;
k->class_id = PCI_CLASS_STORAGE_SCSI;
k->subsystem_id = 0x1000;
+ pvs_k->parent_dc_realize = dc->realize;
+ dc->realize = pvscsi_realize;
dc->reset = pvscsi_reset;
dc->vmsd = &vmstate_pvscsi;
dc->props = pvscsi_properties;
@@ -1202,6 +1287,7 @@ static void pvscsi_class_init(ObjectClass *klass, void *data)
static const TypeInfo pvscsi_info = {
.name = TYPE_PVSCSI,
.parent = TYPE_PCI_DEVICE,
+ .class_size = sizeof(PVSCSIClass),
.instance_size = sizeof(PVSCSIState),
.class_init = pvscsi_class_init,
.interfaces = (InterfaceInfo[]) {
diff --git a/qemu/hw/sd/Makefile.objs b/qemu/hw/sd/Makefile.objs
index f1aed83d9..31c83308f 100644
--- a/qemu/hw/sd/Makefile.objs
+++ b/qemu/hw/sd/Makefile.objs
@@ -1,6 +1,6 @@
common-obj-$(CONFIG_PL181) += pl181.o
common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
-common-obj-$(CONFIG_SD) += sd.o
+common-obj-$(CONFIG_SD) += sd.o core.o
common-obj-$(CONFIG_SDHCI) += sdhci.o
obj-$(CONFIG_MILKYMIST) += milkymist-memcard.o
diff --git a/qemu/hw/sd/core.c b/qemu/hw/sd/core.c
new file mode 100644
index 000000000..14c2bdf27
--- /dev/null
+++ b/qemu/hw/sd/core.c
@@ -0,0 +1,146 @@
+/*
+ * SD card bus interface code.
+ *
+ * Copyright (c) 2015 Linaro Limited
+ *
+ * Author:
+ * Peter Maydell <peter.maydell@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/qdev-core.h"
+#include "sysemu/block-backend.h"
+#include "hw/sd/sd.h"
+
+static SDState *get_card(SDBus *sdbus)
+{
+ /* We only ever have one child on the bus so just return it */
+ BusChild *kid = QTAILQ_FIRST(&sdbus->qbus.children);
+
+ if (!kid) {
+ return NULL;
+ }
+ return SD_CARD(kid->child);
+}
+
+int sdbus_do_command(SDBus *sdbus, SDRequest *req, uint8_t *response)
+{
+ SDState *card = get_card(sdbus);
+
+ if (card) {
+ SDCardClass *sc = SD_CARD_GET_CLASS(card);
+
+ return sc->do_command(card, req, response);
+ }
+
+ return 0;
+}
+
+void sdbus_write_data(SDBus *sdbus, uint8_t value)
+{
+ SDState *card = get_card(sdbus);
+
+ if (card) {
+ SDCardClass *sc = SD_CARD_GET_CLASS(card);
+
+ sc->write_data(card, value);
+ }
+}
+
+uint8_t sdbus_read_data(SDBus *sdbus)
+{
+ SDState *card = get_card(sdbus);
+
+ if (card) {
+ SDCardClass *sc = SD_CARD_GET_CLASS(card);
+
+ return sc->read_data(card);
+ }
+
+ return 0;
+}
+
+bool sdbus_data_ready(SDBus *sdbus)
+{
+ SDState *card = get_card(sdbus);
+
+ if (card) {
+ SDCardClass *sc = SD_CARD_GET_CLASS(card);
+
+ return sc->data_ready(card);
+ }
+
+ return false;
+}
+
+bool sdbus_get_inserted(SDBus *sdbus)
+{
+ SDState *card = get_card(sdbus);
+
+ if (card) {
+ SDCardClass *sc = SD_CARD_GET_CLASS(card);
+
+ return sc->get_inserted(card);
+ }
+
+ return false;
+}
+
+bool sdbus_get_readonly(SDBus *sdbus)
+{
+ SDState *card = get_card(sdbus);
+
+ if (card) {
+ SDCardClass *sc = SD_CARD_GET_CLASS(card);
+
+ return sc->get_readonly(card);
+ }
+
+ return false;
+}
+
+void sdbus_set_inserted(SDBus *sdbus, bool inserted)
+{
+ SDBusClass *sbc = SD_BUS_GET_CLASS(sdbus);
+ BusState *qbus = BUS(sdbus);
+
+ if (sbc->set_inserted) {
+ sbc->set_inserted(qbus->parent, inserted);
+ }
+}
+
+void sdbus_set_readonly(SDBus *sdbus, bool readonly)
+{
+ SDBusClass *sbc = SD_BUS_GET_CLASS(sdbus);
+ BusState *qbus = BUS(sdbus);
+
+ if (sbc->set_readonly) {
+ sbc->set_readonly(qbus->parent, readonly);
+ }
+}
+
+static const TypeInfo sd_bus_info = {
+ .name = TYPE_SD_BUS,
+ .parent = TYPE_BUS,
+ .instance_size = sizeof(SDBus),
+ .class_size = sizeof(SDBusClass),
+};
+
+static void sd_bus_register_types(void)
+{
+ type_register_static(&sd_bus_info);
+}
+
+type_init(sd_bus_register_types)
diff --git a/qemu/hw/sd/milkymist-memcard.c b/qemu/hw/sd/milkymist-memcard.c
index 2209ef1d5..c04ff02fa 100644
--- a/qemu/hw/sd/milkymist-memcard.c
+++ b/qemu/hw/sd/milkymist-memcard.c
@@ -21,6 +21,7 @@
* http://www.milkymist.org/socdoc/memcard.pdf
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "sysemu/sysemu.h"
@@ -28,7 +29,7 @@
#include "qemu/error-report.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
-#include "hw/sd.h"
+#include "hw/sd/sd.h"
enum {
ENABLE_CMD_TX = (1<<0),
diff --git a/qemu/hw/sd/omap_mmc.c b/qemu/hw/sd/omap_mmc.c
index d072deca1..e934cd365 100644
--- a/qemu/hw/sd/omap_mmc.c
+++ b/qemu/hw/sd/omap_mmc.c
@@ -16,9 +16,10 @@
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/arm/omap.h"
-#include "hw/sd.h"
+#include "hw/sd/sd.h"
struct omap_mmc_s {
qemu_irq irq;
@@ -578,8 +579,7 @@ struct omap_mmc_s *omap_mmc_init(hwaddr base,
BlockBackend *blk,
qemu_irq irq, qemu_irq dma[], omap_clk clk)
{
- struct omap_mmc_s *s = (struct omap_mmc_s *)
- g_malloc0(sizeof(struct omap_mmc_s));
+ struct omap_mmc_s *s = g_new0(struct omap_mmc_s, 1);
s->irq = irq;
s->dma = dma;
@@ -605,8 +605,7 @@ struct omap_mmc_s *omap2_mmc_init(struct omap_target_agent_s *ta,
BlockBackend *blk, qemu_irq irq, qemu_irq dma[],
omap_clk fclk, omap_clk iclk)
{
- struct omap_mmc_s *s = (struct omap_mmc_s *)
- g_malloc0(sizeof(struct omap_mmc_s));
+ struct omap_mmc_s *s = g_new0(struct omap_mmc_s, 1);
s->irq = irq;
s->dma = dma;
diff --git a/qemu/hw/sd/pl181.c b/qemu/hw/sd/pl181.c
index 11fcd479d..e87abb205 100644
--- a/qemu/hw/sd/pl181.c
+++ b/qemu/hw/sd/pl181.c
@@ -7,10 +7,11 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
#include "hw/sysbus.h"
-#include "hw/sd.h"
+#include "hw/sd/sd.h"
//#define DEBUG_PL181 1
@@ -46,7 +47,7 @@ typedef struct PL181State {
int32_t fifo_pos;
int32_t fifo_len;
/* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives
- while it is reading the FIFO. We hack around this be defering
+ while it is reading the FIFO. We hack around this by deferring
subsequent transfers until after the driver polls the status word.
http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1
*/
diff --git a/qemu/hw/sd/pxa2xx_mmci.c b/qemu/hw/sd/pxa2xx_mmci.c
index d1fe6d58e..3deccf02c 100644
--- a/qemu/hw/sd/pxa2xx_mmci.c
+++ b/qemu/hw/sd/pxa2xx_mmci.c
@@ -10,18 +10,34 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
+#include "hw/sysbus.h"
#include "hw/arm/pxa.h"
-#include "hw/sd.h"
+#include "hw/sd/sd.h"
#include "hw/qdev.h"
+#include "hw/qdev-properties.h"
+#include "qemu/error-report.h"
+
+#define TYPE_PXA2XX_MMCI "pxa2xx-mmci"
+#define PXA2XX_MMCI(obj) OBJECT_CHECK(PXA2xxMMCIState, (obj), TYPE_PXA2XX_MMCI)
+
+#define TYPE_PXA2XX_MMCI_BUS "pxa2xx-mmci-bus"
+#define PXA2XX_MMCI_BUS(obj) OBJECT_CHECK(SDBus, (obj), TYPE_PXA2XX_MMCI_BUS)
struct PXA2xxMMCIState {
+ SysBusDevice parent_obj;
+
MemoryRegion iomem;
qemu_irq irq;
qemu_irq rx_dma;
qemu_irq tx_dma;
+ qemu_irq inserted;
+ qemu_irq readonly;
- SDState *card;
+ BlockBackend *blk;
+ SDBus sdbus;
uint32_t status;
uint32_t clkrt;
@@ -29,25 +45,70 @@ struct PXA2xxMMCIState {
uint32_t cmdat;
uint32_t resp_tout;
uint32_t read_tout;
- int blklen;
- int numblk;
+ int32_t blklen;
+ int32_t numblk;
uint32_t intmask;
uint32_t intreq;
- int cmd;
+ int32_t cmd;
uint32_t arg;
- int active;
- int bytesleft;
+ int32_t active;
+ int32_t bytesleft;
uint8_t tx_fifo[64];
- int tx_start;
- int tx_len;
+ uint32_t tx_start;
+ uint32_t tx_len;
uint8_t rx_fifo[32];
- int rx_start;
- int rx_len;
+ uint32_t rx_start;
+ uint32_t rx_len;
uint16_t resp_fifo[9];
- int resp_len;
+ uint32_t resp_len;
- int cmdreq;
+ int32_t cmdreq;
+};
+
+static bool pxa2xx_mmci_vmstate_validate(void *opaque, int version_id)
+{
+ PXA2xxMMCIState *s = opaque;
+
+ return s->tx_start < ARRAY_SIZE(s->tx_fifo)
+ && s->rx_start < ARRAY_SIZE(s->rx_fifo)
+ && s->tx_len <= ARRAY_SIZE(s->tx_fifo)
+ && s->rx_len <= ARRAY_SIZE(s->rx_fifo)
+ && s->resp_len <= ARRAY_SIZE(s->resp_fifo);
+}
+
+
+static const VMStateDescription vmstate_pxa2xx_mmci = {
+ .name = "pxa2xx-mmci",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(status, PXA2xxMMCIState),
+ VMSTATE_UINT32(clkrt, PXA2xxMMCIState),
+ VMSTATE_UINT32(spi, PXA2xxMMCIState),
+ VMSTATE_UINT32(cmdat, PXA2xxMMCIState),
+ VMSTATE_UINT32(resp_tout, PXA2xxMMCIState),
+ VMSTATE_UINT32(read_tout, PXA2xxMMCIState),
+ VMSTATE_INT32(blklen, PXA2xxMMCIState),
+ VMSTATE_INT32(numblk, PXA2xxMMCIState),
+ VMSTATE_UINT32(intmask, PXA2xxMMCIState),
+ VMSTATE_UINT32(intreq, PXA2xxMMCIState),
+ VMSTATE_INT32(cmd, PXA2xxMMCIState),
+ VMSTATE_UINT32(arg, PXA2xxMMCIState),
+ VMSTATE_INT32(cmdreq, PXA2xxMMCIState),
+ VMSTATE_INT32(active, PXA2xxMMCIState),
+ VMSTATE_INT32(bytesleft, PXA2xxMMCIState),
+ VMSTATE_UINT32(tx_start, PXA2xxMMCIState),
+ VMSTATE_UINT32(tx_len, PXA2xxMMCIState),
+ VMSTATE_UINT32(rx_start, PXA2xxMMCIState),
+ VMSTATE_UINT32(rx_len, PXA2xxMMCIState),
+ VMSTATE_UINT32(resp_len, PXA2xxMMCIState),
+ VMSTATE_VALIDATE("fifo size incorrect", pxa2xx_mmci_vmstate_validate),
+ VMSTATE_UINT8_ARRAY(tx_fifo, PXA2xxMMCIState, 64),
+ VMSTATE_UINT8_ARRAY(rx_fifo, PXA2xxMMCIState, 32),
+ VMSTATE_UINT16_ARRAY(resp_fifo, PXA2xxMMCIState, 9),
+ VMSTATE_END_OF_LIST()
+ }
};
#define MMC_STRPCL 0x00 /* MMC Clock Start/Stop register */
@@ -121,7 +182,7 @@ static void pxa2xx_mmci_fifo_update(PXA2xxMMCIState *s)
if (s->cmdat & CMDAT_WR_RD) {
while (s->bytesleft && s->tx_len) {
- sd_write_data(s->card, s->tx_fifo[s->tx_start ++]);
+ sdbus_write_data(&s->sdbus, s->tx_fifo[s->tx_start++]);
s->tx_start &= 0x1f;
s->tx_len --;
s->bytesleft --;
@@ -131,7 +192,7 @@ static void pxa2xx_mmci_fifo_update(PXA2xxMMCIState *s)
} else
while (s->bytesleft && s->rx_len < 32) {
s->rx_fifo[(s->rx_start + (s->rx_len ++)) & 0x1f] =
- sd_read_data(s->card);
+ sdbus_read_data(&s->sdbus);
s->bytesleft --;
s->intreq |= INT_RXFIFO_REQ;
}
@@ -165,7 +226,7 @@ static void pxa2xx_mmci_wakequeues(PXA2xxMMCIState *s)
request.arg = s->arg;
request.crc = 0; /* FIXME */
- rsplen = sd_do_command(s->card, &request, response);
+ rsplen = sdbus_do_command(&s->sdbus, &request, response);
s->intreq |= INT_END_CMD;
memset(s->resp_fifo, 0, sizeof(s->resp_fifo));
@@ -391,114 +452,147 @@ static const MemoryRegionOps pxa2xx_mmci_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static void pxa2xx_mmci_save(QEMUFile *f, void *opaque)
+PXA2xxMMCIState *pxa2xx_mmci_init(MemoryRegion *sysmem,
+ hwaddr base,
+ BlockBackend *blk, qemu_irq irq,
+ qemu_irq rx_dma, qemu_irq tx_dma)
{
- PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
- int i;
-
- qemu_put_be32s(f, &s->status);
- qemu_put_be32s(f, &s->clkrt);
- qemu_put_be32s(f, &s->spi);
- qemu_put_be32s(f, &s->cmdat);
- qemu_put_be32s(f, &s->resp_tout);
- qemu_put_be32s(f, &s->read_tout);
- qemu_put_be32(f, s->blklen);
- qemu_put_be32(f, s->numblk);
- qemu_put_be32s(f, &s->intmask);
- qemu_put_be32s(f, &s->intreq);
- qemu_put_be32(f, s->cmd);
- qemu_put_be32s(f, &s->arg);
- qemu_put_be32(f, s->cmdreq);
- qemu_put_be32(f, s->active);
- qemu_put_be32(f, s->bytesleft);
-
- qemu_put_byte(f, s->tx_len);
- for (i = 0; i < s->tx_len; i ++)
- qemu_put_byte(f, s->tx_fifo[(s->tx_start + i) & 63]);
-
- qemu_put_byte(f, s->rx_len);
- for (i = 0; i < s->rx_len; i ++)
- qemu_put_byte(f, s->rx_fifo[(s->rx_start + i) & 31]);
-
- qemu_put_byte(f, s->resp_len);
- for (i = s->resp_len; i < 9; i ++)
- qemu_put_be16s(f, &s->resp_fifo[i]);
+ DeviceState *dev, *carddev;
+ SysBusDevice *sbd;
+ PXA2xxMMCIState *s;
+ Error *err = NULL;
+
+ dev = qdev_create(NULL, TYPE_PXA2XX_MMCI);
+ s = PXA2XX_MMCI(dev);
+ sbd = SYS_BUS_DEVICE(dev);
+ sysbus_mmio_map(sbd, 0, base);
+ sysbus_connect_irq(sbd, 0, irq);
+ qdev_connect_gpio_out_named(dev, "rx-dma", 0, rx_dma);
+ qdev_connect_gpio_out_named(dev, "tx-dma", 0, tx_dma);
+
+ /* Create and plug in the sd card */
+ carddev = qdev_create(qdev_get_child_bus(dev, "sd-bus"), TYPE_SD_CARD);
+ qdev_prop_set_drive(carddev, "drive", blk, &err);
+ if (err) {
+ error_report("failed to init SD card: %s", error_get_pretty(err));
+ return NULL;
+ }
+ object_property_set_bool(OBJECT(carddev), true, "realized", &err);
+ if (err) {
+ error_report("failed to init SD card: %s", error_get_pretty(err));
+ return NULL;
+ }
+
+ return s;
}
-static int pxa2xx_mmci_load(QEMUFile *f, void *opaque, int version_id)
+static void pxa2xx_mmci_set_inserted(DeviceState *dev, bool inserted)
{
- PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
- int i;
-
- qemu_get_be32s(f, &s->status);
- qemu_get_be32s(f, &s->clkrt);
- qemu_get_be32s(f, &s->spi);
- qemu_get_be32s(f, &s->cmdat);
- qemu_get_be32s(f, &s->resp_tout);
- qemu_get_be32s(f, &s->read_tout);
- s->blklen = qemu_get_be32(f);
- s->numblk = qemu_get_be32(f);
- qemu_get_be32s(f, &s->intmask);
- qemu_get_be32s(f, &s->intreq);
- s->cmd = qemu_get_be32(f);
- qemu_get_be32s(f, &s->arg);
- s->cmdreq = qemu_get_be32(f);
- s->active = qemu_get_be32(f);
- s->bytesleft = qemu_get_be32(f);
-
- s->tx_len = qemu_get_byte(f);
- s->tx_start = 0;
- if (s->tx_len >= sizeof(s->tx_fifo) || s->tx_len < 0)
- return -EINVAL;
- for (i = 0; i < s->tx_len; i ++)
- s->tx_fifo[i] = qemu_get_byte(f);
+ PXA2xxMMCIState *s = PXA2XX_MMCI(dev);
- s->rx_len = qemu_get_byte(f);
- s->rx_start = 0;
- if (s->rx_len >= sizeof(s->rx_fifo) || s->rx_len < 0)
- return -EINVAL;
- for (i = 0; i < s->rx_len; i ++)
- s->rx_fifo[i] = qemu_get_byte(f);
+ qemu_set_irq(s->inserted, inserted);
+}
- s->resp_len = qemu_get_byte(f);
- if (s->resp_len > 9 || s->resp_len < 0)
- return -EINVAL;
- for (i = s->resp_len; i < 9; i ++)
- qemu_get_be16s(f, &s->resp_fifo[i]);
+static void pxa2xx_mmci_set_readonly(DeviceState *dev, bool readonly)
+{
+ PXA2xxMMCIState *s = PXA2XX_MMCI(dev);
- return 0;
+ qemu_set_irq(s->readonly, readonly);
}
-PXA2xxMMCIState *pxa2xx_mmci_init(MemoryRegion *sysmem,
- hwaddr base,
- BlockBackend *blk, qemu_irq irq,
- qemu_irq rx_dma, qemu_irq tx_dma)
+void pxa2xx_mmci_handlers(PXA2xxMMCIState *s, qemu_irq readonly,
+ qemu_irq coverswitch)
{
- PXA2xxMMCIState *s;
+ DeviceState *dev = DEVICE(s);
+
+ s->readonly = readonly;
+ s->inserted = coverswitch;
+
+ pxa2xx_mmci_set_inserted(dev, sdbus_get_inserted(&s->sdbus));
+ pxa2xx_mmci_set_readonly(dev, sdbus_get_readonly(&s->sdbus));
+}
+
+static void pxa2xx_mmci_reset(DeviceState *d)
+{
+ PXA2xxMMCIState *s = PXA2XX_MMCI(d);
+
+ s->status = 0;
+ s->clkrt = 0;
+ s->spi = 0;
+ s->cmdat = 0;
+ s->resp_tout = 0;
+ s->read_tout = 0;
+ s->blklen = 0;
+ s->numblk = 0;
+ s->intmask = 0;
+ s->intreq = 0;
+ s->cmd = 0;
+ s->arg = 0;
+ s->active = 0;
+ s->bytesleft = 0;
+ s->tx_start = 0;
+ s->tx_len = 0;
+ s->rx_start = 0;
+ s->rx_len = 0;
+ s->resp_len = 0;
+ s->cmdreq = 0;
+ memset(s->tx_fifo, 0, sizeof(s->tx_fifo));
+ memset(s->rx_fifo, 0, sizeof(s->rx_fifo));
+ memset(s->resp_fifo, 0, sizeof(s->resp_fifo));
+}
- s = (PXA2xxMMCIState *) g_malloc0(sizeof(PXA2xxMMCIState));
- s->irq = irq;
- s->rx_dma = rx_dma;
- s->tx_dma = tx_dma;
+static void pxa2xx_mmci_instance_init(Object *obj)
+{
+ PXA2xxMMCIState *s = PXA2XX_MMCI(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ DeviceState *dev = DEVICE(obj);
- memory_region_init_io(&s->iomem, NULL, &pxa2xx_mmci_ops, s,
+ memory_region_init_io(&s->iomem, obj, &pxa2xx_mmci_ops, s,
"pxa2xx-mmci", 0x00100000);
- memory_region_add_subregion(sysmem, base, &s->iomem);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->irq);
+ qdev_init_gpio_out_named(dev, &s->rx_dma, "rx-dma", 1);
+ qdev_init_gpio_out_named(dev, &s->tx_dma, "tx-dma", 1);
- /* Instantiate the actual storage */
- s->card = sd_init(blk, false);
- if (s->card == NULL) {
- exit(1);
- }
+ qbus_create_inplace(&s->sdbus, sizeof(s->sdbus),
+ TYPE_PXA2XX_MMCI_BUS, DEVICE(obj), "sd-bus");
+}
- register_savevm(NULL, "pxa2xx_mmci", 0, 0,
- pxa2xx_mmci_save, pxa2xx_mmci_load, s);
+static void pxa2xx_mmci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
- return s;
+ dc->vmsd = &vmstate_pxa2xx_mmci;
+ dc->reset = pxa2xx_mmci_reset;
}
-void pxa2xx_mmci_handlers(PXA2xxMMCIState *s, qemu_irq readonly,
- qemu_irq coverswitch)
+static void pxa2xx_mmci_bus_class_init(ObjectClass *klass, void *data)
{
- sd_set_cb(s->card, readonly, coverswitch);
+ SDBusClass *sbc = SD_BUS_CLASS(klass);
+
+ sbc->set_inserted = pxa2xx_mmci_set_inserted;
+ sbc->set_readonly = pxa2xx_mmci_set_readonly;
}
+
+static const TypeInfo pxa2xx_mmci_info = {
+ .name = TYPE_PXA2XX_MMCI,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(PXA2xxMMCIState),
+ .instance_init = pxa2xx_mmci_instance_init,
+ .class_init = pxa2xx_mmci_class_init,
+};
+
+static const TypeInfo pxa2xx_mmci_bus_info = {
+ .name = TYPE_PXA2XX_MMCI_BUS,
+ .parent = TYPE_SD_BUS,
+ .instance_size = sizeof(SDBus),
+ .class_init = pxa2xx_mmci_bus_class_init,
+};
+
+static void pxa2xx_mmci_register_types(void)
+{
+ type_register_static(&pxa2xx_mmci_info);
+ type_register_static(&pxa2xx_mmci_bus_info);
+}
+
+type_init(pxa2xx_mmci_register_types)
diff --git a/qemu/hw/sd/sd.c b/qemu/hw/sd/sd.c
index a1ff465a6..b66e5d2db 100644
--- a/qemu/hw/sd/sd.c
+++ b/qemu/hw/sd/sd.c
@@ -29,10 +29,16 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "qemu/osdep.h"
+#include "hw/qdev.h"
#include "hw/hw.h"
#include "sysemu/block-backend.h"
-#include "hw/sd.h"
+#include "hw/sd/sd.h"
+#include "qapi/error.h"
#include "qemu/bitmap.h"
+#include "hw/qdev-properties.h"
+#include "qemu/error-report.h"
+#include "qemu/timer.h"
//#define DEBUG_SD 1
@@ -43,7 +49,9 @@ do { fprintf(stderr, "SD: " fmt , ## __VA_ARGS__); } while (0)
#define DPRINTF(fmt, ...) do {} while(0)
#endif
-#define ACMD41_ENQUIRY_MASK 0x00ffffff
+#define ACMD41_ENQUIRY_MASK 0x00ffffff
+#define OCR_POWER_UP 0x80000000
+#define OCR_POWER_DELAY_NS 500000 /* 0.5ms */
typedef enum {
sd_r0 = 0, /* no response */
@@ -77,9 +85,12 @@ enum SDCardStates {
};
struct SDState {
+ DeviceState parent_obj;
+
uint32_t mode; /* current card mode, one of SDCardModes */
int32_t state; /* current card state, one of SDCardStates */
uint32_t ocr;
+ QEMUTimer *ocr_power_timer;
uint8_t scr[8];
uint8_t cid[16];
uint8_t csd[16];
@@ -92,6 +103,7 @@ struct SDState {
int32_t wpgrps_size;
uint64_t size;
uint32_t blk_len;
+ uint32_t multi_blk_cnt;
uint32_t erase_start;
uint32_t erase_end;
uint8_t pwd[16];
@@ -193,8 +205,17 @@ static uint16_t sd_crc16(void *message, size_t width)
static void sd_set_ocr(SDState *sd)
{
- /* All voltages OK, card power-up OK, Standard Capacity SD Memory Card */
- sd->ocr = 0x80ffff00;
+ /* All voltages OK, Standard Capacity SD Memory Card, not yet powered up */
+ sd->ocr = 0x00ffff00;
+}
+
+static void sd_ocr_powerup(void *opaque)
+{
+ SDState *sd = opaque;
+
+ /* Set powered up bit in OCR */
+ assert(!(sd->ocr & OCR_POWER_UP));
+ sd->ocr |= OCR_POWER_UP;
}
static void sd_set_scr(SDState *sd)
@@ -389,8 +410,9 @@ static inline uint64_t sd_addr_to_wpnum(uint64_t addr)
return addr >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT);
}
-static void sd_reset(SDState *sd)
+static void sd_reset(DeviceState *dev)
{
+ SDState *sd = SD_CARD(dev);
uint64_t size;
uint64_t sect;
@@ -412,8 +434,7 @@ static void sd_reset(SDState *sd)
sd_set_cardstatus(sd);
sd_set_sdstatus(sd);
- if (sd->wp_groups)
- g_free(sd->wp_groups);
+ g_free(sd->wp_groups);
sd->wp_switch = sd->blk ? blk_is_read_only(sd->blk) : false;
sd->wpgrps_size = sect;
sd->wp_groups = bitmap_new(sd->wpgrps_size);
@@ -424,16 +445,44 @@ static void sd_reset(SDState *sd)
sd->blk_len = 0x200;
sd->pwd_len = 0;
sd->expecting_acmd = false;
+ sd->multi_blk_cnt = 0;
+}
+
+static bool sd_get_inserted(SDState *sd)
+{
+ return sd->blk && blk_is_inserted(sd->blk);
+}
+
+static bool sd_get_readonly(SDState *sd)
+{
+ return sd->wp_switch;
}
static void sd_cardchange(void *opaque, bool load)
{
SDState *sd = opaque;
+ DeviceState *dev = DEVICE(sd);
+ SDBus *sdbus = SD_BUS(qdev_get_parent_bus(dev));
+ bool inserted = sd_get_inserted(sd);
+ bool readonly = sd_get_readonly(sd);
- qemu_set_irq(sd->inserted_cb, blk_is_inserted(sd->blk));
- if (blk_is_inserted(sd->blk)) {
- sd_reset(sd);
- qemu_set_irq(sd->readonly_cb, sd->wp_switch);
+ if (inserted) {
+ sd_reset(dev);
+ }
+
+ /* The IRQ notification is for legacy non-QOM SD controller devices;
+ * QOMified controllers use the SDBus APIs.
+ */
+ if (sdbus) {
+ sdbus_set_inserted(sdbus, inserted);
+ if (inserted) {
+ sdbus_set_readonly(sdbus, readonly);
+ }
+ } else {
+ qemu_set_irq(sd->inserted_cb, inserted);
+ if (inserted) {
+ qemu_set_irq(sd->readonly_cb, readonly);
+ }
}
}
@@ -441,10 +490,44 @@ static const BlockDevOps sd_block_ops = {
.change_media_cb = sd_cardchange,
};
+static bool sd_ocr_vmstate_needed(void *opaque)
+{
+ SDState *sd = opaque;
+
+ /* Include the OCR state (and timer) if it is not yet powered up */
+ return !(sd->ocr & OCR_POWER_UP);
+}
+
+static const VMStateDescription sd_ocr_vmstate = {
+ .name = "sd-card/ocr-state",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = sd_ocr_vmstate_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(ocr, SDState),
+ VMSTATE_TIMER_PTR(ocr_power_timer, SDState),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static int sd_vmstate_pre_load(void *opaque)
+{
+ SDState *sd = opaque;
+
+ /* If the OCR state is not included (prior versions, or not
+ * needed), then the OCR must be set as powered up. If the OCR state
+ * is included, this will be replaced by the state restore.
+ */
+ sd_ocr_powerup(sd);
+
+ return 0;
+}
+
static const VMStateDescription sd_vmstate = {
.name = "sd-card",
.version_id = 1,
.minimum_version_id = 1,
+ .pre_load = sd_vmstate_pre_load,
.fields = (VMStateField[]) {
VMSTATE_UINT32(mode, SDState),
VMSTATE_INT32(state, SDState),
@@ -456,6 +539,7 @@ static const VMStateDescription sd_vmstate = {
VMSTATE_UINT32(vhs, SDState),
VMSTATE_BITMAP(wp_groups, SDState, 0, wpgrps_size),
VMSTATE_UINT32(blk_len, SDState),
+ VMSTATE_UINT32(multi_blk_cnt, SDState),
VMSTATE_UINT32(erase_start, SDState),
VMSTATE_UINT32(erase_end, SDState),
VMSTATE_UINT8_ARRAY(pwd, SDState, 16),
@@ -470,34 +554,35 @@ static const VMStateDescription sd_vmstate = {
VMSTATE_BUFFER_POINTER_UNSAFE(buf, SDState, 1, 512),
VMSTATE_BOOL(enable, SDState),
VMSTATE_END_OF_LIST()
- }
+ },
+ .subsections = (const VMStateDescription*[]) {
+ &sd_ocr_vmstate,
+ NULL
+ },
};
-/* We do not model the chip select pin, so allow the board to select
- whether card should be in SSI or MMC/SD mode. It is also up to the
- board to ensure that ssi transfers only occur when the chip select
- is asserted. */
+/* Legacy initialization function for use by non-qdevified callers */
SDState *sd_init(BlockBackend *blk, bool is_spi)
{
- SDState *sd;
-
- if (blk && blk_is_read_only(blk)) {
- fprintf(stderr, "sd_init: Cannot use read-only drive\n");
+ Object *obj;
+ DeviceState *dev;
+ Error *err = NULL;
+
+ obj = object_new(TYPE_SD_CARD);
+ dev = DEVICE(obj);
+ qdev_prop_set_drive(dev, "drive", blk, &err);
+ if (err) {
+ error_report("sd_init failed: %s", error_get_pretty(err));
return NULL;
}
-
- sd = (SDState *) g_malloc0(sizeof(SDState));
- sd->buf = blk_blockalign(blk, 512);
- sd->spi = is_spi;
- sd->enable = true;
- sd->blk = blk;
- sd_reset(sd);
- if (sd->blk) {
- blk_attach_dev_nofail(sd->blk, sd);
- blk_set_dev_ops(sd->blk, &sd_block_ops, sd);
+ qdev_prop_set_bit(dev, "spi", is_spi);
+ object_property_set_bool(obj, true, "realized", &err);
+ if (err) {
+ error_report("sd_init failed: %s", error_get_pretty(err));
+ return NULL;
}
- vmstate_register(NULL, -1, &sd_vmstate, sd);
- return sd;
+
+ return SD_CARD(dev);
}
void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert)
@@ -666,8 +751,16 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
/* Not interpreting this as an app command */
sd->card_status &= ~APP_CMD;
- if (sd_cmd_type[req.cmd] == sd_ac || sd_cmd_type[req.cmd] == sd_adtc)
+ if (sd_cmd_type[req.cmd & 0x3F] == sd_ac
+ || sd_cmd_type[req.cmd & 0x3F] == sd_adtc) {
rca = req.arg >> 16;
+ }
+
+ /* CMD23 (set block count) must be immediately followed by CMD18 or CMD25
+ * if not, its effects are cancelled */
+ if (sd->multi_blk_cnt != 0 && !(req.cmd == 18 || req.cmd == 25)) {
+ sd->multi_blk_cnt = 0;
+ }
DPRINTF("CMD%d 0x%08x state %d\n", req.cmd, req.arg, sd->state);
switch (req.cmd) {
@@ -679,7 +772,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
default:
sd->state = sd_idle_state;
- sd_reset(sd);
+ sd_reset(DEVICE(sd));
return sd->spi ? sd_r1 : sd_r0;
}
break;
@@ -964,6 +1057,17 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
}
break;
+ case 23: /* CMD23: SET_BLOCK_COUNT */
+ switch (sd->state) {
+ case sd_transfer_state:
+ sd->multi_blk_cnt = req.arg;
+ return sd_r1;
+
+ default:
+ break;
+ }
+ break;
+
/* Block write commands (Class 4) */
case 24: /* CMD24: WRITE_SINGLE_BLOCK */
if (sd->spi)
@@ -1196,16 +1300,17 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
default:
bad_cmd:
- fprintf(stderr, "SD: Unknown CMD%i\n", req.cmd);
+ qemu_log_mask(LOG_GUEST_ERROR, "SD: Unknown CMD%i\n", req.cmd);
return sd_illegal;
unimplemented_cmd:
/* Commands that are recognised but not yet implemented in SPI mode. */
- fprintf(stderr, "SD: CMD%i not implemented in SPI mode\n", req.cmd);
+ qemu_log_mask(LOG_UNIMP, "SD: CMD%i not implemented in SPI mode\n",
+ req.cmd);
return sd_illegal;
}
- fprintf(stderr, "SD: CMD%i in a wrong state\n", req.cmd);
+ qemu_log_mask(LOG_GUEST_ERROR, "SD: CMD%i in a wrong state\n", req.cmd);
return sd_illegal;
}
@@ -1273,9 +1378,28 @@ static sd_rsp_type_t sd_app_command(SDState *sd,
}
switch (sd->state) {
case sd_idle_state:
+ /* If it's the first ACMD41 since reset, we need to decide
+ * whether to power up. If this is not an enquiry ACMD41,
+ * we immediately report power on and proceed below to the
+ * ready state, but if it is, we set a timer to model a
+ * delay for power up. This works around a bug in EDK2
+ * UEFI, which sends an initial enquiry ACMD41, but
+ * assumes that the card is in ready state as soon as it
+ * sees the power up bit set. */
+ if (!(sd->ocr & OCR_POWER_UP)) {
+ if ((req.arg & ACMD41_ENQUIRY_MASK) != 0) {
+ timer_del(sd->ocr_power_timer);
+ sd_ocr_powerup(sd);
+ } else if (!timer_pending(sd->ocr_power_timer)) {
+ timer_mod_ns(sd->ocr_power_timer,
+ (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)
+ + OCR_POWER_DELAY_NS));
+ }
+ }
+
/* We accept any voltage. 10000 V is nothing.
*
- * We don't model init delay so just advance straight to ready state
+ * Once we're powered up, we advance straight to ready state
* unless it's an enquiry ACMD41 (bits 23:0 == 0).
*/
if (req.arg & ACMD41_ENQUIRY_MASK) {
@@ -1318,7 +1442,7 @@ static sd_rsp_type_t sd_app_command(SDState *sd,
return sd_normal_command(sd, req);
}
- fprintf(stderr, "SD: ACMD%i in a wrong state\n", req.cmd);
+ qemu_log_mask(LOG_GUEST_ERROR, "SD: ACMD%i in a wrong state\n", req.cmd);
return sd_illegal;
}
@@ -1338,7 +1462,8 @@ static int cmd_valid_while_locked(SDState *sd, SDRequest *req)
if (req->cmd == 16 || req->cmd == 55) {
return 1;
}
- return sd_cmd_class[req->cmd] == 0 || sd_cmd_class[req->cmd] == 7;
+ return sd_cmd_class[req->cmd & 0x3F] == 0
+ || sd_cmd_class[req->cmd & 0x3F] == 7;
}
int sd_do_command(SDState *sd, SDRequest *req,
@@ -1361,7 +1486,7 @@ int sd_do_command(SDState *sd, SDRequest *req,
if (!cmd_valid_while_locked(sd, req)) {
sd->card_status |= ILLEGAL_COMMAND;
sd->expecting_acmd = false;
- fprintf(stderr, "SD: Card is locked\n");
+ qemu_log_mask(LOG_GUEST_ERROR, "SD: Card is locked\n");
rtype = sd_illegal;
goto send_response;
}
@@ -1519,7 +1644,8 @@ void sd_write_data(SDState *sd, uint8_t value)
return;
if (sd->state != sd_receivingdata_state) {
- fprintf(stderr, "sd_write_data: not in Receiving-Data state\n");
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "sd_write_data: not in Receiving-Data state\n");
return;
}
@@ -1563,6 +1689,14 @@ void sd_write_data(SDState *sd, uint8_t value)
sd->csd[14] |= 0x40;
/* Bzzzzzzztt .... Operation complete. */
+ if (sd->multi_blk_cnt != 0) {
+ if (--sd->multi_blk_cnt == 0) {
+ /* Stop! */
+ sd->state = sd_transfer_state;
+ break;
+ }
+ }
+
sd->state = sd_receivingdata_state;
}
break;
@@ -1630,7 +1764,7 @@ void sd_write_data(SDState *sd, uint8_t value)
break;
default:
- fprintf(stderr, "sd_write_data: unknown command\n");
+ qemu_log_mask(LOG_GUEST_ERROR, "sd_write_data: unknown command\n");
break;
}
}
@@ -1645,7 +1779,8 @@ uint8_t sd_read_data(SDState *sd)
return 0x00;
if (sd->state != sd_sendingdata_state) {
- fprintf(stderr, "sd_read_data: not in Sending-Data state\n");
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "sd_read_data: not in Sending-Data state\n");
return 0x00;
}
@@ -1709,6 +1844,15 @@ uint8_t sd_read_data(SDState *sd)
if (sd->data_offset >= io_len) {
sd->data_start += io_len;
sd->data_offset = 0;
+
+ if (sd->multi_blk_cnt != 0) {
+ if (--sd->multi_blk_cnt == 0) {
+ /* Stop! */
+ sd->state = sd_transfer_state;
+ break;
+ }
+ }
+
if (sd->data_start + io_len > sd->size) {
sd->card_status |= ADDRESS_ERROR;
break;
@@ -1747,7 +1891,7 @@ uint8_t sd_read_data(SDState *sd)
break;
default:
- fprintf(stderr, "sd_read_data: unknown command\n");
+ qemu_log_mask(LOG_GUEST_ERROR, "sd_read_data: unknown command\n");
return 0x00;
}
@@ -1763,3 +1907,73 @@ void sd_enable(SDState *sd, bool enable)
{
sd->enable = enable;
}
+
+static void sd_instance_init(Object *obj)
+{
+ SDState *sd = SD_CARD(obj);
+
+ sd->enable = true;
+ sd->ocr_power_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sd_ocr_powerup, sd);
+}
+
+static void sd_realize(DeviceState *dev, Error **errp)
+{
+ SDState *sd = SD_CARD(dev);
+
+ if (sd->blk && blk_is_read_only(sd->blk)) {
+ error_setg(errp, "Cannot use read-only drive as SD card");
+ return;
+ }
+
+ sd->buf = blk_blockalign(sd->blk, 512);
+
+ if (sd->blk) {
+ blk_set_dev_ops(sd->blk, &sd_block_ops, sd);
+ }
+}
+
+static Property sd_properties[] = {
+ DEFINE_PROP_DRIVE("drive", SDState, blk),
+ /* We do not model the chip select pin, so allow the board to select
+ * whether card should be in SSI or MMC/SD mode. It is also up to the
+ * board to ensure that ssi transfers only occur when the chip select
+ * is asserted. */
+ DEFINE_PROP_BOOL("spi", SDState, spi, false),
+ DEFINE_PROP_END_OF_LIST()
+};
+
+static void sd_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SDCardClass *sc = SD_CARD_CLASS(klass);
+
+ dc->realize = sd_realize;
+ dc->props = sd_properties;
+ dc->vmsd = &sd_vmstate;
+ dc->reset = sd_reset;
+ dc->bus_type = TYPE_SD_BUS;
+
+ sc->do_command = sd_do_command;
+ sc->write_data = sd_write_data;
+ sc->read_data = sd_read_data;
+ sc->data_ready = sd_data_ready;
+ sc->enable = sd_enable;
+ sc->get_inserted = sd_get_inserted;
+ sc->get_readonly = sd_get_readonly;
+}
+
+static const TypeInfo sd_info = {
+ .name = TYPE_SD_CARD,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(SDState),
+ .class_size = sizeof(SDCardClass),
+ .class_init = sd_class_init,
+ .instance_init = sd_instance_init,
+};
+
+static void sd_register_types(void)
+{
+ type_register_static(&sd_info);
+}
+
+type_init(sd_register_types)
diff --git a/qemu/hw/sd/sdhci.h b/qemu/hw/sd/sdhci-internal.h
index 3352d23d6..161177cf3 100644
--- a/qemu/hw/sd/sdhci.h
+++ b/qemu/hw/sd/sdhci-internal.h
@@ -21,14 +21,10 @@
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#ifndef SDHCI_INTERNAL_H
+#define SDHCI_INTERNAL_H
-#ifndef SDHCI_H
-#define SDHCI_H
-
-#include "qemu-common.h"
-#include "hw/pci/pci.h"
-#include "hw/sysbus.h"
-#include "hw/sd.h"
+#include "hw/sd/sdhci.h"
/* R/W SDMA System Address register 0x0 */
#define SDHC_SYSAD 0x00
@@ -220,7 +216,7 @@
#define SD_HOST_SPECv2_VERS 0x2401
#define SDHC_REGISTERS_MAP_SIZE 0x100
-#define SDHC_INSERTION_DELAY (get_ticks_per_sec())
+#define SDHC_INSERTION_DELAY (NANOSECONDS_PER_SECOND)
#define SDHC_TRANSFER_DELAY 100
#define SDHC_ADMA_DESCS_PER_DELAY 5
#define SDHC_CMD_RESPONSE (3 << 0)
@@ -231,65 +227,6 @@ enum {
sdhc_gap_write = 2 /* SDHC stopped at block gap during write operation */
};
-/* SD/MMC host controller state */
-typedef struct SDHCIState {
- union {
- PCIDevice pcidev;
- SysBusDevice busdev;
- };
- SDState *card;
- MemoryRegion iomem;
-
- QEMUTimer *insert_timer; /* timer for 'changing' sd card. */
- QEMUTimer *transfer_timer;
- qemu_irq eject_cb;
- qemu_irq ro_cb;
- qemu_irq irq;
-
- uint32_t sdmasysad; /* SDMA System Address register */
- uint16_t blksize; /* Host DMA Buff Boundary and Transfer BlkSize Reg */
- uint16_t blkcnt; /* Blocks count for current transfer */
- uint32_t argument; /* Command Argument Register */
- uint16_t trnmod; /* Transfer Mode Setting Register */
- uint16_t cmdreg; /* Command Register */
- uint32_t rspreg[4]; /* Response Registers 0-3 */
- uint32_t prnsts; /* Present State Register */
- uint8_t hostctl; /* Host Control Register */
- uint8_t pwrcon; /* Power control Register */
- uint8_t blkgap; /* Block Gap Control Register */
- uint8_t wakcon; /* WakeUp Control Register */
- uint16_t clkcon; /* Clock control Register */
- uint8_t timeoutcon; /* Timeout Control Register */
- uint8_t admaerr; /* ADMA Error Status Register */
- uint16_t norintsts; /* Normal Interrupt Status Register */
- uint16_t errintsts; /* Error Interrupt Status Register */
- uint16_t norintstsen; /* Normal Interrupt Status Enable Register */
- uint16_t errintstsen; /* Error Interrupt Status Enable Register */
- uint16_t norintsigen; /* Normal Interrupt Signal Enable Register */
- uint16_t errintsigen; /* Error Interrupt Signal Enable Register */
- uint16_t acmd12errsts; /* Auto CMD12 error status register */
- uint64_t admasysaddr; /* ADMA System Address Register */
-
- uint32_t capareg; /* Capabilities Register */
- uint32_t maxcurr; /* Maximum Current Capabilities Register */
- uint8_t *fifo_buffer; /* SD host i/o FIFO buffer */
- uint32_t buf_maxsz;
- uint16_t data_count; /* current element in FIFO buffer */
- uint8_t stopped_state;/* Current SDHC state */
- /* Buffer Data Port Register - virtual access point to R and W buffers */
- /* Software Reset Register - always reads as 0 */
- /* Force Event Auto CMD12 Error Interrupt Reg - write only */
- /* Force Event Error Interrupt Register- write only */
- /* RO Host Controller Version Register always reads as 0x2401 */
-} SDHCIState;
-
extern const VMStateDescription sdhci_vmstate;
-#define TYPE_PCI_SDHCI "sdhci-pci"
-#define PCI_SDHCI(obj) OBJECT_CHECK(SDHCIState, (obj), TYPE_PCI_SDHCI)
-
-#define TYPE_SYSBUS_SDHCI "generic-sdhci"
-#define SYSBUS_SDHCI(obj) \
- OBJECT_CHECK(SDHCIState, (obj), TYPE_SYSBUS_SDHCI)
-
-#endif /* SDHCI_H */
+#endif
diff --git a/qemu/hw/sd/sdhci.c b/qemu/hw/sd/sdhci.c
index e63367ba5..d28b5871f 100644
--- a/qemu/hw/sd/sdhci.c
+++ b/qemu/hw/sd/sdhci.c
@@ -22,38 +22,41 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
#include "sysemu/dma.h"
#include "qemu/timer.h"
#include "qemu/bitops.h"
-
-#include "sdhci.h"
+#include "sdhci-internal.h"
/* host controller debug messages */
#ifndef SDHC_DEBUG
#define SDHC_DEBUG 0
#endif
-#if SDHC_DEBUG == 0
- #define DPRINT_L1(fmt, args...) do { } while (0)
- #define DPRINT_L2(fmt, args...) do { } while (0)
- #define ERRPRINT(fmt, args...) do { } while (0)
-#elif SDHC_DEBUG == 1
- #define DPRINT_L1(fmt, args...) \
- do {fprintf(stderr, "QEMU SDHC: "fmt, ## args); } while (0)
- #define DPRINT_L2(fmt, args...) do { } while (0)
- #define ERRPRINT(fmt, args...) \
- do {fprintf(stderr, "QEMU SDHC ERROR: "fmt, ## args); } while (0)
-#else
- #define DPRINT_L1(fmt, args...) \
- do {fprintf(stderr, "QEMU SDHC: "fmt, ## args); } while (0)
- #define DPRINT_L2(fmt, args...) \
- do {fprintf(stderr, "QEMU SDHC: "fmt, ## args); } while (0)
- #define ERRPRINT(fmt, args...) \
- do {fprintf(stderr, "QEMU SDHC ERROR: "fmt, ## args); } while (0)
-#endif
+#define DPRINT_L1(fmt, args...) \
+ do { \
+ if (SDHC_DEBUG) { \
+ fprintf(stderr, "QEMU SDHC: " fmt, ## args); \
+ } \
+ } while (0)
+#define DPRINT_L2(fmt, args...) \
+ do { \
+ if (SDHC_DEBUG > 1) { \
+ fprintf(stderr, "QEMU SDHC: " fmt, ## args); \
+ } \
+ } while (0)
+#define ERRPRINT(fmt, args...) \
+ do { \
+ if (SDHC_DEBUG) { \
+ fprintf(stderr, "QEMU SDHC ERROR: " fmt, ## args); \
+ } \
+ } while (0)
+
+#define TYPE_SDHCI_BUS "sdhci-bus"
+#define SDHCI_BUS(obj) OBJECT_CHECK(SDBus, (obj), TYPE_SDHCI_BUS)
/* Default SD/MMC host controller features information, which will be
* presented in CAPABILITIES register of generic SD host controller at reset.
@@ -145,9 +148,9 @@ static void sdhci_raise_insertion_irq(void *opaque)
}
}
-static void sdhci_insert_eject_cb(void *opaque, int irq, int level)
+static void sdhci_set_inserted(DeviceState *dev, bool level)
{
- SDHCIState *s = (SDHCIState *)opaque;
+ SDHCIState *s = (SDHCIState *)dev;
DPRINT_L1("Card state changed: %s!\n", level ? "insert" : "eject");
if ((s->norintsts & SDHC_NIS_REMOVE) && level) {
@@ -172,9 +175,9 @@ static void sdhci_insert_eject_cb(void *opaque, int irq, int level)
}
}
-static void sdhci_card_readonly_cb(void *opaque, int irq, int level)
+static void sdhci_set_readonly(DeviceState *dev, bool level)
{
- SDHCIState *s = (SDHCIState *)opaque;
+ SDHCIState *s = (SDHCIState *)dev;
if (level) {
s->prnsts &= ~SDHC_WRITE_PROTECT;
@@ -186,6 +189,8 @@ static void sdhci_card_readonly_cb(void *opaque, int irq, int level)
static void sdhci_reset(SDHCIState *s)
{
+ DeviceState *dev = DEVICE(s);
+
timer_del(s->insert_timer);
timer_del(s->transfer_timer);
/* Set all registers to 0. Capabilities registers are not cleared
@@ -193,9 +198,28 @@ static void sdhci_reset(SDHCIState *s)
* initialization */
memset(&s->sdmasysad, 0, (uintptr_t)&s->capareg - (uintptr_t)&s->sdmasysad);
- sd_set_cb(s->card, s->ro_cb, s->eject_cb);
+ /* Reset other state based on current card insertion/readonly status */
+ sdhci_set_inserted(dev, sdbus_get_inserted(&s->sdbus));
+ sdhci_set_readonly(dev, sdbus_get_readonly(&s->sdbus));
+
s->data_count = 0;
s->stopped_state = sdhc_not_stopped;
+ s->pending_insert_state = false;
+}
+
+static void sdhci_poweron_reset(DeviceState *dev)
+{
+ /* QOM (ie power-on) reset. This is identical to reset
+ * commanded via device register apart from handling of the
+ * 'pending insert on powerup' quirk.
+ */
+ SDHCIState *s = (SDHCIState *)dev;
+
+ sdhci_reset(s);
+
+ if (s->pending_insert_quirk) {
+ s->pending_insert_state = true;
+ }
}
static void sdhci_data_transfer(void *opaque);
@@ -211,7 +235,7 @@ static void sdhci_send_command(SDHCIState *s)
request.cmd = s->cmdreg >> 8;
request.arg = s->argument;
DPRINT_L1("sending CMD%u ARG[0x%08x]\n", request.cmd, request.arg);
- rlen = sd_do_command(s->card, &request, response);
+ rlen = sdbus_do_command(&s->sdbus, &request, response);
if (s->cmdreg & SDHC_CMD_RESPONSE) {
if (rlen == 4) {
@@ -243,9 +267,6 @@ static void sdhci_send_command(SDHCIState *s)
(s->cmdreg & SDHC_CMD_RESPONSE) == SDHC_CMD_RSP_WITH_BUSY) {
s->norintsts |= SDHC_NIS_TRSCMP;
}
- } else if (rlen != 0 && (s->errintstsen & SDHC_EISEN_CMDIDX)) {
- s->errintsts |= SDHC_EIS_CMDIDX;
- s->norintsts |= SDHC_NIS_ERR;
}
if (s->norintstsen & SDHC_NISEN_CMDCMP) {
@@ -270,7 +291,7 @@ static void sdhci_end_transfer(SDHCIState *s)
request.cmd = 0x0C;
request.arg = 0;
DPRINT_L1("Automatically issue CMD%d %08x\n", request.cmd, request.arg);
- sd_do_command(s->card, &request, response);
+ sdbus_do_command(&s->sdbus, &request, response);
/* Auto CMD12 response goes to the upper Response register */
s->rspreg[3] = (response[0] << 24) | (response[1] << 16) |
(response[2] << 8) | response[3];
@@ -302,7 +323,7 @@ static void sdhci_read_block_from_card(SDHCIState *s)
}
for (index = 0; index < (s->blksize & 0x0fff); index++) {
- s->fifo_buffer[index] = sd_read_data(s->card);
+ s->fifo_buffer[index] = sdbus_read_data(&s->sdbus);
}
/* New data now available for READ through Buffer Port Register */
@@ -395,7 +416,7 @@ static void sdhci_write_block_to_card(SDHCIState *s)
}
for (index = 0; index < (s->blksize & 0x0fff); index++) {
- sd_write_data(s->card, s->fifo_buffer[index]);
+ sdbus_write_data(&s->sdbus, s->fifo_buffer[index]);
}
/* Next data can be written through BUFFER DATORT register */
@@ -477,7 +498,7 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
while (s->blkcnt) {
if (s->data_count == 0) {
for (n = 0; n < block_size; n++) {
- s->fifo_buffer[n] = sd_read_data(s->card);
+ s->fifo_buffer[n] = sdbus_read_data(&s->sdbus);
}
}
begin = s->data_count;
@@ -518,7 +539,7 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
s->sdmasysad += s->data_count - begin;
if (s->data_count == block_size) {
for (n = 0; n < block_size; n++) {
- sd_write_data(s->card, s->fifo_buffer[n]);
+ sdbus_write_data(&s->sdbus, s->fifo_buffer[n]);
}
s->data_count = 0;
if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) {
@@ -550,7 +571,7 @@ static void sdhci_sdma_transfer_single_block(SDHCIState *s)
if (s->trnmod & SDHC_TRNS_READ) {
for (n = 0; n < datacnt; n++) {
- s->fifo_buffer[n] = sd_read_data(s->card);
+ s->fifo_buffer[n] = sdbus_read_data(&s->sdbus);
}
dma_memory_write(&address_space_memory, s->sdmasysad, s->fifo_buffer,
datacnt);
@@ -558,7 +579,7 @@ static void sdhci_sdma_transfer_single_block(SDHCIState *s)
dma_memory_read(&address_space_memory, s->sdmasysad, s->fifo_buffer,
datacnt);
for (n = 0; n < datacnt; n++) {
- sd_write_data(s->card, s->fifo_buffer[n]);
+ sdbus_write_data(&s->sdbus, s->fifo_buffer[n]);
}
}
@@ -662,7 +683,7 @@ static void sdhci_do_adma(SDHCIState *s)
while (length) {
if (s->data_count == 0) {
for (n = 0; n < block_size; n++) {
- s->fifo_buffer[n] = sd_read_data(s->card);
+ s->fifo_buffer[n] = sdbus_read_data(&s->sdbus);
}
}
begin = s->data_count;
@@ -703,7 +724,7 @@ static void sdhci_do_adma(SDHCIState *s)
dscr.addr += s->data_count - begin;
if (s->data_count == block_size) {
for (n = 0; n < block_size; n++) {
- sd_write_data(s->card, s->fifo_buffer[n]);
+ sdbus_write_data(&s->sdbus, s->fifo_buffer[n]);
}
s->data_count = 0;
if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) {
@@ -719,7 +740,8 @@ static void sdhci_do_adma(SDHCIState *s)
break;
case SDHC_ADMA_ATTR_ACT_LINK: /* link to next descriptor table */
s->admasysaddr = dscr.addr;
- DPRINT_L1("ADMA link: admasysaddr=0x%lx\n", s->admasysaddr);
+ DPRINT_L1("ADMA link: admasysaddr=0x%" PRIx64 "\n",
+ s->admasysaddr);
break;
default:
s->admasysaddr += dscr.incr;
@@ -727,7 +749,8 @@ static void sdhci_do_adma(SDHCIState *s)
}
if (dscr.attr & SDHC_ADMA_ATTR_INT) {
- DPRINT_L1("ADMA interrupt: admasysaddr=0x%lx\n", s->admasysaddr);
+ DPRINT_L1("ADMA interrupt: admasysaddr=0x%" PRIx64 "\n",
+ s->admasysaddr);
if (s->norintstsen & SDHC_NISEN_DMA) {
s->norintsts |= SDHC_NIS_DMA;
}
@@ -815,7 +838,7 @@ static void sdhci_data_transfer(void *opaque)
break;
}
} else {
- if ((s->trnmod & SDHC_TRNS_READ) && sd_data_ready(s->card)) {
+ if ((s->trnmod & SDHC_TRNS_READ) && sdbus_data_ready(&s->sdbus)) {
s->prnsts |= SDHC_DOING_READ | SDHC_DATA_INHIBIT |
SDHC_DAT_LINE_ACTIVE;
sdhci_read_block_from_card(s);
@@ -829,7 +852,7 @@ static void sdhci_data_transfer(void *opaque)
static bool sdhci_can_issue_command(SDHCIState *s)
{
- if (!SDHC_CLOCK_IS_ON(s->clkcon) || !(s->pwrcon & SDHC_POWER_ON) ||
+ if (!SDHC_CLOCK_IS_ON(s->clkcon) ||
(((s->prnsts & SDHC_DATA_INHIBIT) || s->stopped_state) &&
((s->cmdreg & SDHC_CMD_DATA_PRESENT) ||
((s->cmdreg & SDHC_CMD_RESPONSE) == SDHC_CMD_RSP_WITH_BUSY &&
@@ -1006,6 +1029,16 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
MASKED_WRITE(s->blksize, mask, value);
MASKED_WRITE(s->blkcnt, mask >> 16, value >> 16);
}
+
+ /* Limit block size to the maximum buffer size */
+ if (extract32(s->blksize, 0, 12) > s->buf_maxsz) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Size 0x%x is larger than " \
+ "the maximum buffer 0x%x", __func__, s->blksize,
+ s->buf_maxsz);
+
+ s->blksize = deposit32(s->blksize, 0, 12, s->buf_maxsz);
+ }
+
break;
case SDHC_ARGUMENT:
MASKED_WRITE(s->argument, mask, value);
@@ -1078,6 +1111,13 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
} else {
s->norintsts &= ~SDHC_NIS_ERR;
}
+ /* Quirk for Raspberry Pi: pending card insert interrupt
+ * appears when first enabled after power on */
+ if ((s->norintstsen & SDHC_NISEN_INSERT) && s->pending_insert_state) {
+ assert(s->pending_insert_quirk);
+ s->norintsts |= SDHC_NIS_INSERT;
+ s->pending_insert_state = false;
+ }
sdhci_update_irq(s);
break;
case SDHC_NORINTSIGEN:
@@ -1144,17 +1184,8 @@ static inline unsigned int sdhci_get_fifolen(SDHCIState *s)
static void sdhci_initfn(SDHCIState *s)
{
- DriveInfo *di;
-
- /* FIXME use a qdev drive property instead of drive_get_next() */
- di = drive_get_next(IF_SD);
- s->card = sd_init(di ? blk_by_legacy_dinfo(di) : NULL, false);
- if (s->card == NULL) {
- exit(1);
- }
- s->eject_cb = qemu_allocate_irq(sdhci_insert_eject_cb, s, 0);
- s->ro_cb = qemu_allocate_irq(sdhci_card_readonly_cb, s, 0);
- sd_set_cb(s->card, s->ro_cb, s->eject_cb);
+ qbus_create_inplace(&s->sdbus, sizeof(s->sdbus),
+ TYPE_SDHCI_BUS, DEVICE(s), "sd-bus");
s->insert_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sdhci_raise_insertion_irq, s);
s->transfer_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sdhci_data_transfer, s);
@@ -1169,12 +1200,28 @@ static void sdhci_uninitfn(SDHCIState *s)
qemu_free_irq(s->eject_cb);
qemu_free_irq(s->ro_cb);
- if (s->fifo_buffer) {
- g_free(s->fifo_buffer);
- s->fifo_buffer = NULL;
- }
+ g_free(s->fifo_buffer);
+ s->fifo_buffer = NULL;
}
+static bool sdhci_pending_insert_vmstate_needed(void *opaque)
+{
+ SDHCIState *s = opaque;
+
+ return s->pending_insert_state;
+}
+
+static const VMStateDescription sdhci_pending_insert_vmstate = {
+ .name = "sdhci/pending-insert",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = sdhci_pending_insert_vmstate_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_BOOL(pending_insert_state, SDHCIState),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
const VMStateDescription sdhci_vmstate = {
.name = "sdhci",
.version_id = 1,
@@ -1209,12 +1256,16 @@ const VMStateDescription sdhci_vmstate = {
VMSTATE_TIMER_PTR(insert_timer, SDHCIState),
VMSTATE_TIMER_PTR(transfer_timer, SDHCIState),
VMSTATE_END_OF_LIST()
- }
+ },
+ .subsections = (const VMStateDescription*[]) {
+ &sdhci_pending_insert_vmstate,
+ NULL
+ },
};
/* Capabilities registers provide information on supported features of this
* specific host controller implementation */
-static Property sdhci_properties[] = {
+static Property sdhci_pci_properties[] = {
DEFINE_PROP_UINT32("capareg", SDHCIState, capareg,
SDHC_CAPAB_REG_DEFAULT),
DEFINE_PROP_UINT32("maxcurr", SDHCIState, maxcurr, 0),
@@ -1253,9 +1304,8 @@ static void sdhci_pci_class_init(ObjectClass *klass, void *data)
k->class_id = PCI_CLASS_SYSTEM_SDHCI;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
dc->vmsd = &sdhci_vmstate;
- dc->props = sdhci_properties;
- /* Reason: realize() method uses drive_get_next() */
- dc->cannot_instantiate_with_device_add_yet = true;
+ dc->props = sdhci_pci_properties;
+ dc->reset = sdhci_poweron_reset;
}
static const TypeInfo sdhci_pci_info = {
@@ -1265,9 +1315,19 @@ static const TypeInfo sdhci_pci_info = {
.class_init = sdhci_pci_class_init,
};
+static Property sdhci_sysbus_properties[] = {
+ DEFINE_PROP_UINT32("capareg", SDHCIState, capareg,
+ SDHC_CAPAB_REG_DEFAULT),
+ DEFINE_PROP_UINT32("maxcurr", SDHCIState, maxcurr, 0),
+ DEFINE_PROP_BOOL("pending-insert-quirk", SDHCIState, pending_insert_quirk,
+ false),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static void sdhci_sysbus_init(Object *obj)
{
SDHCIState *s = SYSBUS_SDHCI(obj);
+
sdhci_initfn(s);
}
@@ -1295,10 +1355,9 @@ static void sdhci_sysbus_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
dc->vmsd = &sdhci_vmstate;
- dc->props = sdhci_properties;
+ dc->props = sdhci_sysbus_properties;
dc->realize = sdhci_sysbus_realize;
- /* Reason: instance_init() method uses drive_get_next() */
- dc->cannot_instantiate_with_device_add_yet = true;
+ dc->reset = sdhci_poweron_reset;
}
static const TypeInfo sdhci_sysbus_info = {
@@ -1310,10 +1369,26 @@ static const TypeInfo sdhci_sysbus_info = {
.class_init = sdhci_sysbus_class_init,
};
+static void sdhci_bus_class_init(ObjectClass *klass, void *data)
+{
+ SDBusClass *sbc = SD_BUS_CLASS(klass);
+
+ sbc->set_inserted = sdhci_set_inserted;
+ sbc->set_readonly = sdhci_set_readonly;
+}
+
+static const TypeInfo sdhci_bus_info = {
+ .name = TYPE_SDHCI_BUS,
+ .parent = TYPE_SD_BUS,
+ .instance_size = sizeof(SDBus),
+ .class_init = sdhci_bus_class_init,
+};
+
static void sdhci_register_types(void)
{
type_register_static(&sdhci_pci_info);
type_register_static(&sdhci_sysbus_info);
+ type_register_static(&sdhci_bus_info);
}
type_init(sdhci_register_types)
diff --git a/qemu/hw/sd/ssi-sd.c b/qemu/hw/sd/ssi-sd.c
index e4b2d4f83..075e4ed5d 100644
--- a/qemu/hw/sd/ssi-sd.c
+++ b/qemu/hw/sd/ssi-sd.c
@@ -10,10 +10,11 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
-#include "hw/ssi.h"
-#include "hw/sd.h"
+#include "hw/ssi/ssi.h"
+#include "hw/sd/sd.h"
//#define DEBUG_SSI_SD 1
diff --git a/qemu/hw/sh4/r2d.c b/qemu/hw/sh4/r2d.c
index 5e22ed79b..db373c70c 100644
--- a/qemu/hw/sh4/r2d.c
+++ b/qemu/hw/sh4/r2d.c
@@ -23,6 +23,10 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/sysbus.h"
#include "hw/hw.h"
#include "hw/sh4/sh.h"
@@ -255,7 +259,7 @@ static void r2d_init(MachineState *machine)
qemu_register_reset(main_cpu_reset, reset_info);
/* Allocate memory space */
- memory_region_init_ram(sdram, NULL, "r2d.sdram", SDRAM_SIZE, &error_abort);
+ memory_region_init_ram(sdram, NULL, "r2d.sdram", SDRAM_SIZE, &error_fatal);
vmstate_register_ram_global(sdram);
memory_region_add_subregion(address_space_mem, SDRAM_BASE, sdram);
/* Register peripherals */
@@ -338,9 +342,9 @@ static void r2d_init(MachineState *machine)
}
/* initialization which should be done by firmware */
- boot_params.loader_type = 1;
- boot_params.initrd_start = INITRD_LOAD_OFFSET;
- boot_params.initrd_size = initrd_size;
+ boot_params.loader_type = tswap32(1);
+ boot_params.initrd_start = tswap32(INITRD_LOAD_OFFSET);
+ boot_params.initrd_size = tswap32(initrd_size);
}
if (kernel_cmdline) {
@@ -354,15 +358,10 @@ static void r2d_init(MachineState *machine)
SDRAM_BASE + BOOT_PARAMS_OFFSET);
}
-static QEMUMachine r2d_machine = {
- .name = "r2d",
- .desc = "r2d-plus board",
- .init = r2d_init,
-};
-
-static void r2d_machine_init(void)
+static void r2d_machine_init(MachineClass *mc)
{
- qemu_register_machine(&r2d_machine);
+ mc->desc = "r2d-plus board";
+ mc->init = r2d_init;
}
-machine_init(r2d_machine_init);
+DEFINE_MACHINE("r2d", r2d_machine_init)
diff --git a/qemu/hw/sh4/sh7750.c b/qemu/hw/sh4/sh7750.c
index 5dda5de34..a1ea760f6 100644
--- a/qemu/hw/sh4/sh7750.c
+++ b/qemu/hw/sh4/sh7750.c
@@ -22,7 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-#include <stdio.h>
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sh4/sh.h"
#include "sysemu/sysemu.h"
diff --git a/qemu/hw/sh4/sh7750_regnames.c b/qemu/hw/sh4/sh7750_regnames.c
index 52ac1cc78..34b4f99b8 100644
--- a/qemu/hw/sh4/sh7750_regnames.c
+++ b/qemu/hw/sh4/sh7750_regnames.c
@@ -1,3 +1,4 @@
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sh4/sh.h"
#include "sh7750_regs.h"
diff --git a/qemu/hw/sh4/sh_pci.c b/qemu/hw/sh4/sh_pci.c
index a2f6d9e0b..e820a3230 100644
--- a/qemu/hw/sh4/sh_pci.c
+++ b/qemu/hw/sh4/sh_pci.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/sh4/sh.h"
#include "hw/pci/pci.h"
@@ -151,12 +152,11 @@ static int sh_pci_device_init(SysBusDevice *dev)
return 0;
}
-static int sh_pci_host_init(PCIDevice *d)
+static void sh_pci_host_realize(PCIDevice *d, Error **errp)
{
pci_set_word(d->config + PCI_COMMAND, PCI_COMMAND_WAIT);
pci_set_word(d->config + PCI_STATUS, PCI_STATUS_CAP_LIST |
PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM);
- return 0;
}
static void sh_pci_host_class_init(ObjectClass *klass, void *data)
@@ -164,7 +164,7 @@ static void sh_pci_host_class_init(ObjectClass *klass, void *data)
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
- k->init = sh_pci_host_init;
+ k->realize = sh_pci_host_realize;
k->vendor_id = PCI_VENDOR_ID_HITACHI;
k->device_id = PCI_DEVICE_ID_HITACHI_SH7751R;
/*
diff --git a/qemu/hw/sh4/shix.c b/qemu/hw/sh4/shix.c
index f93f98e56..ccc9e7589 100644
--- a/qemu/hw/sh4/shix.c
+++ b/qemu/hw/sh4/shix.c
@@ -27,6 +27,10 @@
More information in target-sh4/README.sh4
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "hw/sh4/sh.h"
#include "sysemu/sysemu.h"
@@ -59,16 +63,16 @@ static void shix_init(MachineState *machine)
}
/* Allocate memory space */
- memory_region_init_ram(rom, NULL, "shix.rom", 0x4000, &error_abort);
+ memory_region_init_ram(rom, NULL, "shix.rom", 0x4000, &error_fatal);
vmstate_register_ram_global(rom);
memory_region_set_readonly(rom, true);
memory_region_add_subregion(sysmem, 0x00000000, rom);
memory_region_init_ram(&sdram[0], NULL, "shix.sdram1", 0x01000000,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&sdram[0]);
memory_region_add_subregion(sysmem, 0x08000000, &sdram[0]);
memory_region_init_ram(&sdram[1], NULL, "shix.sdram2", 0x01000000,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&sdram[1]);
memory_region_add_subregion(sysmem, 0x0c000000, &sdram[1]);
@@ -87,16 +91,11 @@ static void shix_init(MachineState *machine)
tc58128_init(s, "shix_linux_nand.bin", NULL);
}
-static QEMUMachine shix_machine = {
- .name = "shix",
- .desc = "shix card",
- .init = shix_init,
- .is_default = 1,
-};
-
-static void shix_machine_init(void)
+static void shix_machine_init(MachineClass *mc)
{
- qemu_register_machine(&shix_machine);
+ mc->desc = "shix card";
+ mc->init = shix_init;
+ mc->is_default = 1;
}
-machine_init(shix_machine_init);
+DEFINE_MACHINE("shix", shix_machine_init)
diff --git a/qemu/hw/smbios/Makefile.objs b/qemu/hw/smbios/Makefile.objs
new file mode 100644
index 000000000..f69a92f96
--- /dev/null
+++ b/qemu/hw/smbios/Makefile.objs
@@ -0,0 +1 @@
+common-obj-$(CONFIG_SMBIOS) += smbios.o
diff --git a/qemu/hw/i386/smbios.c b/qemu/hw/smbios/smbios.c
index 1341e0234..cb8a11110 100644
--- a/qemu/hw/i386/smbios.c
+++ b/qemu/hw/smbios/smbios.c
@@ -15,14 +15,15 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu/config-file.h"
#include "qemu/error-report.h"
#include "sysemu/sysemu.h"
#include "sysemu/cpus.h"
-#include "hw/i386/pc.h"
-#include "hw/i386/smbios.h"
+#include "hw/smbios/smbios.h"
#include "hw/loader.h"
-
+#include "exec/cpu-common.h"
/* legacy structures and constants for <= 2.0 machines */
struct smbios_header {
@@ -56,7 +57,9 @@ static uint8_t *smbios_tables;
static size_t smbios_tables_len;
static unsigned smbios_table_max;
static unsigned smbios_table_cnt;
-static struct smbios_entry_point ep;
+static SmbiosEntryPointType smbios_ep_type = SMBIOS_ENTRY_POINT_21;
+
+static SmbiosEntryPoint ep;
static int smbios_type4_count = 0;
static bool smbios_immutable;
@@ -318,7 +321,7 @@ static void smbios_register_config(void)
qemu_add_opts(&qemu_smbios_opts);
}
-machine_init(smbios_register_config);
+opts_init(smbios_register_config);
static void smbios_validate_table(void)
{
@@ -772,11 +775,12 @@ void smbios_set_cpuid(uint32_t version, uint32_t features)
void smbios_set_defaults(const char *manufacturer, const char *product,
const char *version, bool legacy_mode,
- bool uuid_encoded)
+ bool uuid_encoded, SmbiosEntryPointType ep_type)
{
smbios_have_defaults = true;
smbios_legacy = legacy_mode;
smbios_uuid_encoded = uuid_encoded;
+ smbios_ep_type = ep_type;
/* drop unwanted version of command-line file blob(s) */
if (smbios_legacy) {
@@ -809,32 +813,61 @@ void smbios_set_defaults(const char *manufacturer, const char *product,
static void smbios_entry_point_setup(void)
{
- memcpy(ep.anchor_string, "_SM_", 4);
- memcpy(ep.intermediate_anchor_string, "_DMI_", 5);
- ep.length = sizeof(struct smbios_entry_point);
- ep.entry_point_revision = 0; /* formatted_area reserved, per spec v2.1+ */
- memset(ep.formatted_area, 0, 5);
-
- /* compliant with smbios spec v2.8 */
- ep.smbios_major_version = 2;
- ep.smbios_minor_version = 8;
- ep.smbios_bcd_revision = 0x28;
-
- /* set during table construction, but BIOS may override: */
- ep.structure_table_length = cpu_to_le16(smbios_tables_len);
- ep.max_structure_size = cpu_to_le16(smbios_table_max);
- ep.number_of_structures = cpu_to_le16(smbios_table_cnt);
-
- /* BIOS must recalculate: */
- ep.checksum = 0;
- ep.intermediate_checksum = 0;
- ep.structure_table_address = cpu_to_le32(0);
+ switch (smbios_ep_type) {
+ case SMBIOS_ENTRY_POINT_21:
+ memcpy(ep.ep21.anchor_string, "_SM_", 4);
+ memcpy(ep.ep21.intermediate_anchor_string, "_DMI_", 5);
+ ep.ep21.length = sizeof(struct smbios_21_entry_point);
+ ep.ep21.entry_point_revision = 0; /* formatted_area reserved */
+ memset(ep.ep21.formatted_area, 0, 5);
+
+ /* compliant with smbios spec v2.8 */
+ ep.ep21.smbios_major_version = 2;
+ ep.ep21.smbios_minor_version = 8;
+ ep.ep21.smbios_bcd_revision = 0x28;
+
+ /* set during table construction, but BIOS may override: */
+ ep.ep21.structure_table_length = cpu_to_le16(smbios_tables_len);
+ ep.ep21.max_structure_size = cpu_to_le16(smbios_table_max);
+ ep.ep21.number_of_structures = cpu_to_le16(smbios_table_cnt);
+
+ /* BIOS must recalculate */
+ ep.ep21.checksum = 0;
+ ep.ep21.intermediate_checksum = 0;
+ ep.ep21.structure_table_address = cpu_to_le32(0);
+
+ break;
+ case SMBIOS_ENTRY_POINT_30:
+ memcpy(ep.ep30.anchor_string, "_SM3_", 5);
+ ep.ep30.length = sizeof(struct smbios_30_entry_point);
+ ep.ep30.entry_point_revision = 1;
+ ep.ep30.reserved = 0;
+
+ /* compliant with smbios spec 3.0 */
+ ep.ep30.smbios_major_version = 3;
+ ep.ep30.smbios_minor_version = 0;
+ ep.ep30.smbios_doc_rev = 0;
+
+ /* set during table construct, but BIOS might override */
+ ep.ep30.structure_table_max_size = cpu_to_le32(smbios_tables_len);
+
+ /* BIOS must recalculate */
+ ep.ep30.checksum = 0;
+ ep.ep30.structure_table_address = cpu_to_le64(0);
+
+ break;
+ default:
+ abort();
+ break;
+ }
}
-void smbios_get_tables(uint8_t **tables, size_t *tables_len,
+void smbios_get_tables(const struct smbios_phys_mem_area *mem_array,
+ const unsigned int mem_array_size,
+ uint8_t **tables, size_t *tables_len,
uint8_t **anchor, size_t *anchor_len)
{
- unsigned i, dimm_cnt, instance;
+ unsigned i, dimm_cnt;
if (smbios_legacy) {
*tables = *anchor = NULL;
@@ -867,11 +900,9 @@ void smbios_get_tables(uint8_t **tables, size_t *tables_len,
smbios_build_type_17_table(i, GET_DIMM_SZ);
}
- for (i = 0, instance = 0; i < e820_get_num_entries(); i++) {
- uint64_t address, length;
- if (e820_get_entry(i, E820_RAM, &address, &length)) {
- smbios_build_type_19_table(instance++, address, length);
- }
+ for (i = 0; i < mem_array_size; i++) {
+ smbios_build_type_19_table(i, mem_array[i].address,
+ mem_array[i].length);
}
smbios_build_type_32_table();
@@ -886,7 +917,15 @@ void smbios_get_tables(uint8_t **tables, size_t *tables_len,
*tables = smbios_tables;
*tables_len = smbios_tables_len;
*anchor = (uint8_t *)&ep;
- *anchor_len = sizeof(struct smbios_entry_point);
+
+ /* calculate length based on anchor string */
+ if (!strncmp((char *)&ep, "_SM_", 4)) {
+ *anchor_len = sizeof(struct smbios_21_entry_point);
+ } else if (!strncmp((char *)&ep, "_SM3_", 5)) {
+ *anchor_len = sizeof(struct smbios_30_entry_point);
+ } else {
+ abort();
+ }
}
static void save_opt(const char **dest, QemuOpts *opts, const char *name)
@@ -900,7 +939,6 @@ static void save_opt(const char **dest, QemuOpts *opts, const char *name)
void smbios_entry_add(QemuOpts *opts)
{
- Error *local_err = NULL;
const char *val;
assert(!smbios_immutable);
@@ -911,11 +949,7 @@ void smbios_entry_add(QemuOpts *opts)
int size;
struct smbios_table *table; /* legacy mode only */
- qemu_opts_validate(opts, qemu_smbios_file_opts, &local_err);
- if (local_err) {
- error_report_err(local_err);
- exit(1);
- }
+ qemu_opts_validate(opts, qemu_smbios_file_opts, &error_fatal);
size = get_image_size(val);
if (size == -1 || size < sizeof(struct smbios_structure_header)) {
@@ -997,11 +1031,7 @@ void smbios_entry_add(QemuOpts *opts)
switch (type) {
case 0:
- qemu_opts_validate(opts, qemu_smbios_type0_opts, &local_err);
- if (local_err) {
- error_report_err(local_err);
- exit(1);
- }
+ qemu_opts_validate(opts, qemu_smbios_type0_opts, &error_fatal);
save_opt(&type0.vendor, opts, "vendor");
save_opt(&type0.version, opts, "version");
save_opt(&type0.date, opts, "date");
@@ -1017,11 +1047,7 @@ void smbios_entry_add(QemuOpts *opts)
}
return;
case 1:
- qemu_opts_validate(opts, qemu_smbios_type1_opts, &local_err);
- if (local_err) {
- error_report_err(local_err);
- exit(1);
- }
+ qemu_opts_validate(opts, qemu_smbios_type1_opts, &error_fatal);
save_opt(&type1.manufacturer, opts, "manufacturer");
save_opt(&type1.product, opts, "product");
save_opt(&type1.version, opts, "version");
@@ -1039,11 +1065,7 @@ void smbios_entry_add(QemuOpts *opts)
}
return;
case 2:
- qemu_opts_validate(opts, qemu_smbios_type2_opts, &local_err);
- if (local_err) {
- error_report_err(local_err);
- exit(1);
- }
+ qemu_opts_validate(opts, qemu_smbios_type2_opts, &error_fatal);
save_opt(&type2.manufacturer, opts, "manufacturer");
save_opt(&type2.product, opts, "product");
save_opt(&type2.version, opts, "version");
@@ -1052,11 +1074,7 @@ void smbios_entry_add(QemuOpts *opts)
save_opt(&type2.location, opts, "location");
return;
case 3:
- qemu_opts_validate(opts, qemu_smbios_type3_opts, &local_err);
- if (local_err) {
- error_report_err(local_err);
- exit(1);
- }
+ qemu_opts_validate(opts, qemu_smbios_type3_opts, &error_fatal);
save_opt(&type3.manufacturer, opts, "manufacturer");
save_opt(&type3.version, opts, "version");
save_opt(&type3.serial, opts, "serial");
@@ -1064,11 +1082,7 @@ void smbios_entry_add(QemuOpts *opts)
save_opt(&type3.sku, opts, "sku");
return;
case 4:
- qemu_opts_validate(opts, qemu_smbios_type4_opts, &local_err);
- if (local_err) {
- error_report_err(local_err);
- exit(1);
- }
+ qemu_opts_validate(opts, qemu_smbios_type4_opts, &error_fatal);
save_opt(&type4.sock_pfx, opts, "sock_pfx");
save_opt(&type4.manufacturer, opts, "manufacturer");
save_opt(&type4.version, opts, "version");
@@ -1077,11 +1091,7 @@ void smbios_entry_add(QemuOpts *opts)
save_opt(&type4.part, opts, "part");
return;
case 17:
- qemu_opts_validate(opts, qemu_smbios_type17_opts, &local_err);
- if (local_err) {
- error_report_err(local_err);
- exit(1);
- }
+ qemu_opts_validate(opts, qemu_smbios_type17_opts, &error_fatal);
save_opt(&type17.loc_pfx, opts, "loc_pfx");
save_opt(&type17.bank, opts, "bank");
save_opt(&type17.manufacturer, opts, "manufacturer");
diff --git a/qemu/hw/sparc/leon3.c b/qemu/hw/sparc/leon3.c
index 7f5dcd6d8..dbae41f3a 100644
--- a/qemu/hw/sparc/leon3.c
+++ b/qemu/hw/sparc/leon3.c
@@ -21,6 +21,10 @@
* 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-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "qemu/timer.h"
#include "hw/ptimer.h"
@@ -156,7 +160,7 @@ static void leon3_generic_hw_init(MachineState *machine)
/* Allocate BIOS */
prom_size = 8 * 1024 * 1024; /* 8Mb */
- memory_region_init_ram(prom, NULL, "Leon3.bios", prom_size, &error_abort);
+ memory_region_init_ram(prom, NULL, "Leon3.bios", prom_size, &error_fatal);
vmstate_register_ram_global(prom);
memory_region_set_readonly(prom, true);
memory_region_add_subregion(address_space_mem, 0x00000000, prom);
@@ -193,7 +197,7 @@ static void leon3_generic_hw_init(MachineState *machine)
uint64_t entry;
kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL,
- 1 /* big endian */, ELF_MACHINE, 0);
+ 1 /* big endian */, EM_SPARC, 0, 0);
if (kernel_size < 0) {
fprintf(stderr, "qemu: could not load kernel '%s'\n",
kernel_filename);
@@ -216,15 +220,10 @@ static void leon3_generic_hw_init(MachineState *machine)
}
}
-static QEMUMachine leon3_generic_machine = {
- .name = "leon3_generic",
- .desc = "Leon-3 generic",
- .init = leon3_generic_hw_init,
-};
-
-static void leon3_machine_init(void)
+static void leon3_generic_machine_init(MachineClass *mc)
{
- qemu_register_machine(&leon3_generic_machine);
+ mc->desc = "Leon-3 generic";
+ mc->init = leon3_generic_hw_init;
}
-machine_init(leon3_machine_init);
+DEFINE_MACHINE("leon3_generic", leon3_generic_machine_init)
diff --git a/qemu/hw/sparc/sun4m.c b/qemu/hw/sparc/sun4m.c
index 68ac4d8bb..7bfc00abc 100644
--- a/qemu/hw/sparc/sun4m.c
+++ b/qemu/hw/sparc/sun4m.c
@@ -21,6 +21,10 @@
* 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-common.h"
+#include "cpu.h"
#include "hw/sysbus.h"
#include "qemu/error-report.h"
#include "qemu/timer.h"
@@ -42,6 +46,7 @@
#include "elf.h"
#include "sysemu/block-backend.h"
#include "trace.h"
+#include "qemu/cutils.h"
/*
* Sun4m architecture was used in the following machines:
@@ -95,29 +100,7 @@ struct sun4m_hwdef {
uint8_t nvram_machine_id;
};
-int DMA_get_channel_mode (int nchan)
-{
- return 0;
-}
-int DMA_read_memory (int nchan, void *buf, int pos, int size)
-{
- return 0;
-}
-int DMA_write_memory (int nchan, void *buf, int pos, int size)
-{
- return 0;
-}
-void DMA_hold_DREQ (int nchan) {}
-void DMA_release_DREQ (int nchan) {}
-void DMA_schedule(int nchan) {}
-
-void DMA_init(int high_page_enable, qemu_irq *cpu_request_exit)
-{
-}
-
-void DMA_register_channel (int nchan,
- DMA_transfer_handler transfer_handler,
- void *opaque)
+void DMA_init(ISABus *bus, int high_page_enable)
{
}
@@ -300,7 +283,7 @@ static unsigned long sun4m_load_kernel(const char *kernel_filename,
bswap_needed = 0;
#endif
kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL,
- NULL, NULL, NULL, 1, ELF_MACHINE, 0);
+ NULL, NULL, NULL, 1, EM_SPARC, 0, 0);
if (kernel_size < 0)
kernel_size = load_aout(kernel_filename, KERNEL_LOAD_ADDR,
RAM_size - KERNEL_LOAD_ADDR, bswap_needed,
@@ -641,7 +624,7 @@ static int idreg_init1(SysBusDevice *dev)
IDRegState *s = MACIO_ID_REGISTER(dev);
memory_region_init_ram(&s->mem, OBJECT(s),
- "sun4m.idreg", sizeof(idreg_data), &error_abort);
+ "sun4m.idreg", sizeof(idreg_data), &error_fatal);
vmstate_register_ram_global(&s->mem);
memory_region_set_readonly(&s->mem, true);
sysbus_init_mmio(dev, &s->mem);
@@ -688,7 +671,7 @@ static int afx_init1(SysBusDevice *dev)
{
AFXState *s = TCX_AFX(dev);
- memory_region_init_ram(&s->mem, OBJECT(s), "sun4m.afx", 4, &error_abort);
+ memory_region_init_ram(&s->mem, OBJECT(s), "sun4m.afx", 4, &error_fatal);
vmstate_register_ram_global(&s->mem);
sysbus_init_mmio(dev, &s->mem);
return 0;
@@ -744,7 +727,7 @@ static void prom_init(hwaddr addr, const char *bios_name)
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
if (filename) {
ret = load_elf(filename, translate_prom_address, &addr, NULL,
- NULL, NULL, 1, ELF_MACHINE, 0);
+ NULL, NULL, 1, EM_SPARC, 0, 0);
if (ret < 0 || ret > PROM_SIZE_MAX) {
ret = load_image_targphys(filename, addr, PROM_SIZE_MAX);
}
@@ -763,7 +746,7 @@ static int prom_init1(SysBusDevice *dev)
PROMState *s = OPENPROM(dev);
memory_region_init_ram(&s->prom, OBJECT(s), "sun4m.prom", PROM_SIZE_MAX,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&s->prom);
memory_region_set_readonly(&s->prom, true);
sysbus_init_mmio(dev, &s->prom);
@@ -1420,80 +1403,152 @@ static void sbook_init(MachineState *machine)
sun4m_hw_init(&sun4m_hwdefs[8], machine);
}
-static QEMUMachine ss5_machine = {
- .name = "SS-5",
- .desc = "Sun4m platform, SPARCstation 5",
- .init = ss5_init,
- .block_default_type = IF_SCSI,
- .is_default = 1,
- .default_boot_order = "c",
+static void ss5_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sun4m platform, SPARCstation 5";
+ mc->init = ss5_init;
+ mc->block_default_type = IF_SCSI;
+ mc->is_default = 1;
+ mc->default_boot_order = "c";
+}
+
+static const TypeInfo ss5_type = {
+ .name = MACHINE_TYPE_NAME("SS-5"),
+ .parent = TYPE_MACHINE,
+ .class_init = ss5_class_init,
};
-static QEMUMachine ss10_machine = {
- .name = "SS-10",
- .desc = "Sun4m platform, SPARCstation 10",
- .init = ss10_init,
- .block_default_type = IF_SCSI,
- .max_cpus = 4,
- .default_boot_order = "c",
+static void ss10_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sun4m platform, SPARCstation 10";
+ mc->init = ss10_init;
+ mc->block_default_type = IF_SCSI;
+ mc->max_cpus = 4;
+ mc->default_boot_order = "c";
+}
+
+static const TypeInfo ss10_type = {
+ .name = MACHINE_TYPE_NAME("SS-10"),
+ .parent = TYPE_MACHINE,
+ .class_init = ss10_class_init,
};
-static QEMUMachine ss600mp_machine = {
- .name = "SS-600MP",
- .desc = "Sun4m platform, SPARCserver 600MP",
- .init = ss600mp_init,
- .block_default_type = IF_SCSI,
- .max_cpus = 4,
- .default_boot_order = "c",
+static void ss600mp_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sun4m platform, SPARCserver 600MP";
+ mc->init = ss600mp_init;
+ mc->block_default_type = IF_SCSI;
+ mc->max_cpus = 4;
+ mc->default_boot_order = "c";
+}
+
+static const TypeInfo ss600mp_type = {
+ .name = MACHINE_TYPE_NAME("SS-600MP"),
+ .parent = TYPE_MACHINE,
+ .class_init = ss600mp_class_init,
};
-static QEMUMachine ss20_machine = {
- .name = "SS-20",
- .desc = "Sun4m platform, SPARCstation 20",
- .init = ss20_init,
- .block_default_type = IF_SCSI,
- .max_cpus = 4,
- .default_boot_order = "c",
+static void ss20_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sun4m platform, SPARCstation 20";
+ mc->init = ss20_init;
+ mc->block_default_type = IF_SCSI;
+ mc->max_cpus = 4;
+ mc->default_boot_order = "c";
+}
+
+static const TypeInfo ss20_type = {
+ .name = MACHINE_TYPE_NAME("SS-20"),
+ .parent = TYPE_MACHINE,
+ .class_init = ss20_class_init,
};
-static QEMUMachine voyager_machine = {
- .name = "Voyager",
- .desc = "Sun4m platform, SPARCstation Voyager",
- .init = vger_init,
- .block_default_type = IF_SCSI,
- .default_boot_order = "c",
+static void voyager_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sun4m platform, SPARCstation Voyager";
+ mc->init = vger_init;
+ mc->block_default_type = IF_SCSI;
+ mc->default_boot_order = "c";
+}
+
+static const TypeInfo voyager_type = {
+ .name = MACHINE_TYPE_NAME("Voyager"),
+ .parent = TYPE_MACHINE,
+ .class_init = voyager_class_init,
};
-static QEMUMachine ss_lx_machine = {
- .name = "LX",
- .desc = "Sun4m platform, SPARCstation LX",
- .init = ss_lx_init,
- .block_default_type = IF_SCSI,
- .default_boot_order = "c",
+static void ss_lx_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sun4m platform, SPARCstation LX";
+ mc->init = ss_lx_init;
+ mc->block_default_type = IF_SCSI;
+ mc->default_boot_order = "c";
+}
+
+static const TypeInfo ss_lx_type = {
+ .name = MACHINE_TYPE_NAME("LX"),
+ .parent = TYPE_MACHINE,
+ .class_init = ss_lx_class_init,
};
-static QEMUMachine ss4_machine = {
- .name = "SS-4",
- .desc = "Sun4m platform, SPARCstation 4",
- .init = ss4_init,
- .block_default_type = IF_SCSI,
- .default_boot_order = "c",
+static void ss4_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sun4m platform, SPARCstation 4";
+ mc->init = ss4_init;
+ mc->block_default_type = IF_SCSI;
+ mc->default_boot_order = "c";
+}
+
+static const TypeInfo ss4_type = {
+ .name = MACHINE_TYPE_NAME("SS-4"),
+ .parent = TYPE_MACHINE,
+ .class_init = ss4_class_init,
};
-static QEMUMachine scls_machine = {
- .name = "SPARCClassic",
- .desc = "Sun4m platform, SPARCClassic",
- .init = scls_init,
- .block_default_type = IF_SCSI,
- .default_boot_order = "c",
+static void scls_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sun4m platform, SPARCClassic";
+ mc->init = scls_init;
+ mc->block_default_type = IF_SCSI;
+ mc->default_boot_order = "c";
+}
+
+static const TypeInfo scls_type = {
+ .name = MACHINE_TYPE_NAME("SPARCClassic"),
+ .parent = TYPE_MACHINE,
+ .class_init = scls_class_init,
};
-static QEMUMachine sbook_machine = {
- .name = "SPARCbook",
- .desc = "Sun4m platform, SPARCbook",
- .init = sbook_init,
- .block_default_type = IF_SCSI,
- .default_boot_order = "c",
+static void sbook_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sun4m platform, SPARCbook";
+ mc->init = sbook_init;
+ mc->block_default_type = IF_SCSI;
+ mc->default_boot_order = "c";
+}
+
+static const TypeInfo sbook_type = {
+ .name = MACHINE_TYPE_NAME("SPARCbook"),
+ .parent = TYPE_MACHINE,
+ .class_init = sbook_class_init,
};
static void sun4m_register_types(void)
@@ -1502,20 +1557,16 @@ static void sun4m_register_types(void)
type_register_static(&afx_info);
type_register_static(&prom_info);
type_register_static(&ram_info);
-}
-static void sun4m_machine_init(void)
-{
- qemu_register_machine(&ss5_machine);
- qemu_register_machine(&ss10_machine);
- qemu_register_machine(&ss600mp_machine);
- qemu_register_machine(&ss20_machine);
- qemu_register_machine(&voyager_machine);
- qemu_register_machine(&ss_lx_machine);
- qemu_register_machine(&ss4_machine);
- qemu_register_machine(&scls_machine);
- qemu_register_machine(&sbook_machine);
+ type_register_static(&ss5_type);
+ type_register_static(&ss10_type);
+ type_register_static(&ss600mp_type);
+ type_register_static(&ss20_type);
+ type_register_static(&voyager_type);
+ type_register_static(&ss_lx_type);
+ type_register_static(&ss4_type);
+ type_register_static(&scls_type);
+ type_register_static(&sbook_type);
}
type_init(sun4m_register_types)
-machine_init(sun4m_machine_init);
diff --git a/qemu/hw/sparc64/sun4u.c b/qemu/hw/sparc64/sun4u.c
index 30cfa0e0a..3165e18eb 100644
--- a/qemu/hw/sparc64/sun4u.c
+++ b/qemu/hw/sparc64/sun4u.c
@@ -21,6 +21,10 @@
* 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-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "hw/pci-host/apb.h"
@@ -40,6 +44,7 @@
#include "elf.h"
#include "sysemu/block-backend.h"
#include "exec/address-spaces.h"
+#include "qemu/cutils.h"
//#define DEBUG_IRQ
//#define DEBUG_EBUS
@@ -98,29 +103,7 @@ typedef struct EbusState {
MemoryRegion bar1;
} EbusState;
-int DMA_get_channel_mode (int nchan)
-{
- return 0;
-}
-int DMA_read_memory (int nchan, void *buf, int pos, int size)
-{
- return 0;
-}
-int DMA_write_memory (int nchan, void *buf, int pos, int size)
-{
- return 0;
-}
-void DMA_hold_DREQ (int nchan) {}
-void DMA_release_DREQ (int nchan) {}
-void DMA_schedule(int nchan) {}
-
-void DMA_init(int high_page_enable, qemu_irq *cpu_request_exit)
-{
-}
-
-void DMA_register_channel (int nchan,
- DMA_transfer_handler transfer_handler,
- void *opaque)
+void DMA_init(ISABus *bus, int high_page_enable)
{
}
@@ -208,7 +191,7 @@ static uint64_t sun4u_load_kernel(const char *kernel_filename,
bswap_needed = 0;
#endif
kernel_size = load_elf(kernel_filename, NULL, NULL, kernel_entry,
- kernel_addr, &kernel_top, 1, ELF_MACHINE, 0);
+ kernel_addr, &kernel_top, 1, EM_SPARCV9, 0, 0);
if (kernel_size < 0) {
*kernel_addr = KERNEL_LOAD_ADDR;
*kernel_entry = KERNEL_LOAD_ADDR;
@@ -358,37 +341,19 @@ typedef struct ResetData {
uint64_t prom_addr;
} ResetData;
-void cpu_put_timer(QEMUFile *f, CPUTimer *s)
-{
- qemu_put_be32s(f, &s->frequency);
- qemu_put_be32s(f, &s->disabled);
- qemu_put_be64s(f, &s->disabled_mask);
- qemu_put_sbe64s(f, &s->clock_offset);
-
- timer_put(f, s->qtimer);
-}
-
-void cpu_get_timer(QEMUFile *f, CPUTimer *s)
-{
- qemu_get_be32s(f, &s->frequency);
- qemu_get_be32s(f, &s->disabled);
- qemu_get_be64s(f, &s->disabled_mask);
- qemu_get_sbe64s(f, &s->clock_offset);
-
- timer_get(f, s->qtimer);
-}
-
static CPUTimer *cpu_timer_create(const char *name, SPARCCPU *cpu,
QEMUBHFunc *cb, uint32_t frequency,
- uint64_t disabled_mask)
+ uint64_t disabled_mask, uint64_t npt_mask)
{
CPUTimer *timer = g_malloc0(sizeof (CPUTimer));
timer->name = name;
timer->frequency = frequency;
timer->disabled_mask = disabled_mask;
+ timer->npt_mask = npt_mask;
timer->disabled = 1;
+ timer->npt = 1;
timer->clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
timer->qtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cb, cpu);
@@ -484,27 +449,27 @@ static void hstick_irq(void *opaque)
static int64_t cpu_to_timer_ticks(int64_t cpu_ticks, uint32_t frequency)
{
- return muldiv64(cpu_ticks, get_ticks_per_sec(), frequency);
+ return muldiv64(cpu_ticks, NANOSECONDS_PER_SECOND, frequency);
}
static uint64_t timer_to_cpu_ticks(int64_t timer_ticks, uint32_t frequency)
{
- return muldiv64(timer_ticks, frequency, get_ticks_per_sec());
+ return muldiv64(timer_ticks, frequency, NANOSECONDS_PER_SECOND);
}
void cpu_tick_set_count(CPUTimer *timer, uint64_t count)
{
- uint64_t real_count = count & ~timer->disabled_mask;
- uint64_t disabled_bit = count & timer->disabled_mask;
+ uint64_t real_count = count & ~timer->npt_mask;
+ uint64_t npt_bit = count & timer->npt_mask;
int64_t vm_clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) -
cpu_to_timer_ticks(real_count, timer->frequency);
- TIMER_DPRINTF("%s set_count count=0x%016lx (%s) p=%p\n",
+ TIMER_DPRINTF("%s set_count count=0x%016lx (npt %s) p=%p\n",
timer->name, real_count,
- timer->disabled?"disabled":"enabled", timer);
+ timer->npt ? "disabled" : "enabled", timer);
- timer->disabled = disabled_bit ? 1 : 0;
+ timer->npt = npt_bit ? 1 : 0;
timer->clock_offset = vm_clock_offset;
}
@@ -514,12 +479,13 @@ uint64_t cpu_tick_get_count(CPUTimer *timer)
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - timer->clock_offset,
timer->frequency);
- TIMER_DPRINTF("%s get_count count=0x%016lx (%s) p=%p\n",
+ TIMER_DPRINTF("%s get_count count=0x%016lx (npt %s) p=%p\n",
timer->name, real_count,
- timer->disabled?"disabled":"enabled", timer);
+ timer->npt ? "disabled" : "enabled", timer);
- if (timer->disabled)
- real_count |= timer->disabled_mask;
+ if (timer->npt) {
+ real_count |= timer->npt_mask;
+ }
return real_count;
}
@@ -593,13 +559,14 @@ pci_ebus_init(PCIBus *bus, int devfn, qemu_irq *irqs)
return isa_bus;
}
-static int
-pci_ebus_init1(PCIDevice *pci_dev)
+static void pci_ebus_realize(PCIDevice *pci_dev, Error **errp)
{
EbusState *s = DO_UPCAST(EbusState, pci_dev, pci_dev);
- isa_bus_new(DEVICE(pci_dev), get_system_memory(),
- pci_address_space_io(pci_dev));
+ if (!isa_bus_new(DEVICE(pci_dev), get_system_memory(),
+ pci_address_space_io(pci_dev), errp)) {
+ return;
+ }
pci_dev->config[0x04] = 0x06; // command = bus master, pci mem
pci_dev->config[0x05] = 0x00;
@@ -614,14 +581,13 @@ pci_ebus_init1(PCIDevice *pci_dev)
memory_region_init_alias(&s->bar1, OBJECT(s), "bar1", get_system_io(),
0, 0x4000);
pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->bar1);
- return 0;
}
static void ebus_class_init(ObjectClass *klass, void *data)
{
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
- k->init = pci_ebus_init1;
+ k->realize = pci_ebus_realize;
k->vendor_id = PCI_VENDOR_ID_SUN;
k->device_id = PCI_DEVICE_ID_SUN_EBUS;
k->revision = 0x01;
@@ -671,7 +637,7 @@ static void prom_init(hwaddr addr, const char *bios_name)
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
if (filename) {
ret = load_elf(filename, translate_prom_address, &addr,
- NULL, NULL, NULL, 1, ELF_MACHINE, 0);
+ NULL, NULL, NULL, 1, EM_SPARCV9, 0, 0);
if (ret < 0 || ret > PROM_SIZE_MAX) {
ret = load_image_targphys(filename, addr, PROM_SIZE_MAX);
}
@@ -690,7 +656,7 @@ static int prom_init1(SysBusDevice *dev)
PROMState *s = OPENPROM(dev);
memory_region_init_ram(&s->prom, OBJECT(s), "sun4u.prom", PROM_SIZE_MAX,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&s->prom);
memory_region_set_readonly(&s->prom, true);
sysbus_init_mmio(dev, &s->prom);
@@ -734,7 +700,7 @@ static int ram_init1(SysBusDevice *dev)
RamDevice *d = SUN4U_RAM(dev);
memory_region_init_ram(&d->ram, OBJECT(d), "sun4u.ram", d->size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&d->ram);
sysbus_init_mmio(dev, &d->ram);
return 0;
@@ -799,13 +765,16 @@ static SPARCCPU *cpu_devinit(const char *cpu_model, const struct hwdef *hwdef)
env = &cpu->env;
env->tick = cpu_timer_create("tick", cpu, tick_irq,
- tick_frequency, TICK_NPT_MASK);
+ tick_frequency, TICK_INT_DIS,
+ TICK_NPT_MASK);
env->stick = cpu_timer_create("stick", cpu, stick_irq,
- stick_frequency, TICK_INT_DIS);
+ stick_frequency, TICK_INT_DIS,
+ TICK_NPT_MASK);
env->hstick = cpu_timer_create("hstick", cpu, hstick_irq,
- hstick_frequency, TICK_INT_DIS);
+ hstick_frequency, TICK_INT_DIS,
+ TICK_NPT_MASK);
reset_info = g_malloc0(sizeof(ResetData));
reset_info->cpu = cpu;
@@ -829,6 +798,7 @@ static void sun4uv_init(MemoryRegion *address_space_mem,
qemu_irq *ivec_irqs, *pbm_irqs;
DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
DriveInfo *fd[MAX_FD];
+ DeviceState *dev;
FWCfgState *fw_cfg;
/* init CPUs */
@@ -865,10 +835,22 @@ static void sun4uv_init(MemoryRegion *address_space_mem,
pci_cmd646_ide_init(pci_bus, hd, 1);
isa_create_simple(isa_bus, "i8042");
+
+ /* Floppy */
for(i = 0; i < MAX_FD; i++) {
fd[i] = drive_get(IF_FLOPPY, 0, i);
}
- fdctrl_init_isa(isa_bus, fd);
+ dev = DEVICE(isa_create(isa_bus, TYPE_ISA_FDC));
+ if (fd[0]) {
+ qdev_prop_set_drive(dev, "driveA", blk_by_legacy_dinfo(fd[0]),
+ &error_abort);
+ }
+ if (fd[1]) {
+ qdev_prop_set_drive(dev, "driveB", blk_by_legacy_dinfo(fd[1]),
+ &error_abort);
+ }
+ qdev_prop_set_uint32(dev, "dma", -1);
+ qdev_init_nofail(dev);
/* Map NVRAM into I/O (ebus) space */
nvram = m48t59_init(NULL, 0, 0, NVRAM_SIZE, 1968, 59);
@@ -965,29 +947,53 @@ static void niagara_init(MachineState *machine)
sun4uv_init(get_system_memory(), machine, &hwdefs[2]);
}
-static QEMUMachine sun4u_machine = {
- .name = "sun4u",
- .desc = "Sun4u platform",
- .init = sun4u_init,
- .max_cpus = 1, // XXX for now
- .is_default = 1,
- .default_boot_order = "c",
+static void sun4u_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sun4u platform";
+ mc->init = sun4u_init;
+ mc->max_cpus = 1; /* XXX for now */
+ mc->is_default = 1;
+ mc->default_boot_order = "c";
+}
+
+static const TypeInfo sun4u_type = {
+ .name = MACHINE_TYPE_NAME("sun4u"),
+ .parent = TYPE_MACHINE,
+ .class_init = sun4u_class_init,
};
-static QEMUMachine sun4v_machine = {
- .name = "sun4v",
- .desc = "Sun4v platform",
- .init = sun4v_init,
- .max_cpus = 1, // XXX for now
- .default_boot_order = "c",
+static void sun4v_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sun4v platform";
+ mc->init = sun4v_init;
+ mc->max_cpus = 1; /* XXX for now */
+ mc->default_boot_order = "c";
+}
+
+static const TypeInfo sun4v_type = {
+ .name = MACHINE_TYPE_NAME("sun4v"),
+ .parent = TYPE_MACHINE,
+ .class_init = sun4v_class_init,
};
-static QEMUMachine niagara_machine = {
- .name = "Niagara",
- .desc = "Sun4v platform, Niagara",
- .init = niagara_init,
- .max_cpus = 1, // XXX for now
- .default_boot_order = "c",
+static void niagara_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sun4v platform, Niagara";
+ mc->init = niagara_init;
+ mc->max_cpus = 1; /* XXX for now */
+ mc->default_boot_order = "c";
+}
+
+static const TypeInfo niagara_type = {
+ .name = MACHINE_TYPE_NAME("Niagara"),
+ .parent = TYPE_MACHINE,
+ .class_init = niagara_class_init,
};
static void sun4u_register_types(void)
@@ -995,14 +1001,10 @@ static void sun4u_register_types(void)
type_register_static(&ebus_info);
type_register_static(&prom_info);
type_register_static(&ram_info);
-}
-static void sun4u_machine_init(void)
-{
- qemu_register_machine(&sun4u_machine);
- qemu_register_machine(&sun4v_machine);
- qemu_register_machine(&niagara_machine);
+ type_register_static(&sun4u_type);
+ type_register_static(&sun4v_type);
+ type_register_static(&niagara_type);
}
type_init(sun4u_register_types)
-machine_init(sun4u_machine_init);
diff --git a/qemu/hw/ssi/omap_spi.c b/qemu/hw/ssi/omap_spi.c
index 119e325a6..22034656b 100644
--- a/qemu/hw/ssi/omap_spi.c
+++ b/qemu/hw/ssi/omap_spi.c
@@ -19,6 +19,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/arm/omap.h"
@@ -342,8 +343,7 @@ static const MemoryRegionOps omap_mcspi_ops = {
struct omap_mcspi_s *omap_mcspi_init(struct omap_target_agent_s *ta, int chnum,
qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk)
{
- struct omap_mcspi_s *s = (struct omap_mcspi_s *)
- g_malloc0(sizeof(struct omap_mcspi_s));
+ struct omap_mcspi_s *s = g_new0(struct omap_mcspi_s, 1);
struct omap_mcspi_ch_s *ch = s->ch;
s->irq = irq;
diff --git a/qemu/hw/ssi/pl022.c b/qemu/hw/ssi/pl022.c
index 61d568f36..564a0d36e 100644
--- a/qemu/hw/ssi/pl022.c
+++ b/qemu/hw/ssi/pl022.c
@@ -7,8 +7,9 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
-#include "hw/ssi.h"
+#include "hw/ssi/ssi.h"
//#define DEBUG_PL022 1
diff --git a/qemu/hw/ssi/ssi.c b/qemu/hw/ssi/ssi.c
index 2aab79ba7..9791c0d94 100644
--- a/qemu/hw/ssi/ssi.c
+++ b/qemu/hw/ssi/ssi.c
@@ -12,7 +12,8 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
-#include "hw/ssi.h"
+#include "qemu/osdep.h"
+#include "hw/ssi/ssi.h"
struct SSIBus {
BusState parent_obj;
diff --git a/qemu/hw/ssi/xilinx_spi.c b/qemu/hw/ssi/xilinx_spi.c
index 620573cac..33482f04d 100644
--- a/qemu/hw/ssi/xilinx_spi.c
+++ b/qemu/hw/ssi/xilinx_spi.c
@@ -24,12 +24,13 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "sysemu/sysemu.h"
#include "qemu/log.h"
#include "qemu/fifo8.h"
-#include "hw/ssi.h"
+#include "hw/ssi/ssi.h"
#ifdef XILINX_SPI_ERR_DEBUG
#define DB_PRINT(...) do { \
diff --git a/qemu/hw/ssi/xilinx_spips.c b/qemu/hw/ssi/xilinx_spips.c
index 0910f5479..e2b77dc3d 100644
--- a/qemu/hw/ssi/xilinx_spips.c
+++ b/qemu/hw/ssi/xilinx_spips.c
@@ -22,13 +22,15 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "sysemu/sysemu.h"
#include "hw/ptimer.h"
#include "qemu/log.h"
#include "qemu/fifo8.h"
-#include "hw/ssi.h"
+#include "hw/ssi/ssi.h"
#include "qemu/bitops.h"
+#include "hw/ssi/xilinx_spips.h"
#ifndef XILINX_SPIPS_ERR_DEBUG
#define XILINX_SPIPS_ERR_DEBUG 0
@@ -103,8 +105,6 @@
#define R_MOD_ID (0xFC / 4)
-#define R_MAX (R_MOD_ID+1)
-
/* size of TXRX FIFOs */
#define RXFF_A 32
#define TXFF_A 32
@@ -135,30 +135,6 @@ typedef enum {
} FlashCMD;
typedef struct {
- SysBusDevice parent_obj;
-
- MemoryRegion iomem;
- MemoryRegion mmlqspi;
-
- qemu_irq irq;
- int irqline;
-
- uint8_t num_cs;
- uint8_t num_busses;
-
- uint8_t snoop_state;
- qemu_irq *cs_lines;
- SSIBus **spi;
-
- Fifo8 rx_fifo;
- Fifo8 tx_fifo;
-
- uint8_t num_txrx_bytes;
-
- uint32_t regs[R_MAX];
-} XilinxSPIPS;
-
-typedef struct {
XilinxSPIPS parent_obj;
uint8_t lqspi_buf[LQSPI_CACHE_SIZE];
@@ -174,19 +150,6 @@ typedef struct XilinxSPIPSClass {
uint32_t tx_fifo_size;
} XilinxSPIPSClass;
-#define TYPE_XILINX_SPIPS "xlnx.ps7-spi"
-#define TYPE_XILINX_QSPIPS "xlnx.ps7-qspi"
-
-#define XILINX_SPIPS(obj) \
- OBJECT_CHECK(XilinxSPIPS, (obj), TYPE_XILINX_SPIPS)
-#define XILINX_SPIPS_CLASS(klass) \
- OBJECT_CLASS_CHECK(XilinxSPIPSClass, (klass), TYPE_XILINX_SPIPS)
-#define XILINX_SPIPS_GET_CLASS(obj) \
- OBJECT_GET_CLASS(XilinxSPIPSClass, (obj), TYPE_XILINX_SPIPS)
-
-#define XILINX_QSPIPS(obj) \
- OBJECT_CHECK(XilinxQSPIPS, (obj), TYPE_XILINX_QSPIPS)
-
static inline int num_effective_busses(XilinxSPIPS *s)
{
return (s->regs[R_LQSPI_CFG] & LQSPI_CFG_SEP_BUS &&
@@ -257,7 +220,7 @@ static void xilinx_spips_reset(DeviceState *d)
XilinxSPIPS *s = XILINX_SPIPS(d);
int i;
- for (i = 0; i < R_MAX; i++) {
+ for (i = 0; i < XLNX_SPIPS_R_MAX; i++) {
s->regs[i] = 0;
}
@@ -664,7 +627,7 @@ static void xilinx_spips_realize(DeviceState *dev, Error **errp)
}
memory_region_init_io(&s->iomem, OBJECT(s), xsc->reg_ops, s,
- "spi", R_MAX*4);
+ "spi", XLNX_SPIPS_R_MAX * 4);
sysbus_init_mmio(sbd, &s->iomem);
s->irqline = -1;
@@ -708,7 +671,7 @@ static const VMStateDescription vmstate_xilinx_spips = {
.fields = (VMStateField[]) {
VMSTATE_FIFO8(tx_fifo, XilinxSPIPS),
VMSTATE_FIFO8(rx_fifo, XilinxSPIPS),
- VMSTATE_UINT32_ARRAY(regs, XilinxSPIPS, R_MAX),
+ VMSTATE_UINT32_ARRAY(regs, XilinxSPIPS, XLNX_SPIPS_R_MAX),
VMSTATE_UINT8(snoop_state, XilinxSPIPS),
VMSTATE_END_OF_LIST()
}
diff --git a/qemu/hw/timer/Makefile.objs b/qemu/hw/timer/Makefile.objs
index 133bd0d45..003c14fa2 100644
--- a/qemu/hw/timer/Makefile.objs
+++ b/qemu/hw/timer/Makefile.objs
@@ -25,7 +25,6 @@ obj-$(CONFIG_OMAP) += omap_gptimer.o
obj-$(CONFIG_OMAP) += omap_synctimer.o
obj-$(CONFIG_PXA2XX) += pxa2xx_timer.o
obj-$(CONFIG_SH4) += sh_timer.o
-obj-$(CONFIG_TUSB6010) += tusb6010.o
obj-$(CONFIG_DIGIC) += digic-timer.o
obj-$(CONFIG_MC146818RTC) += mc146818rtc.o
@@ -33,3 +32,4 @@ obj-$(CONFIG_MC146818RTC) += mc146818rtc.o
obj-$(CONFIG_ALLWINNER_A10_PIT) += allwinner-a10-pit.o
common-obj-$(CONFIG_STM32F2XX_TIMER) += stm32f2xx_timer.o
+common-obj-$(CONFIG_ASPEED_SOC) += aspeed_timer.o
diff --git a/qemu/hw/timer/a9gtimer.c b/qemu/hw/timer/a9gtimer.c
index dd4aae8b3..afe577c76 100644
--- a/qemu/hw/timer/a9gtimer.c
+++ b/qemu/hw/timer/a9gtimer.c
@@ -20,10 +20,13 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/timer/a9gtimer.h"
+#include "qapi/error.h"
#include "qemu/timer.h"
#include "qemu/bitops.h"
#include "qemu/log.h"
+#include "qom/cpu.h"
#ifndef A9_GTIMER_ERR_DEBUG
#define A9_GTIMER_ERR_DEBUG 0
diff --git a/qemu/hw/timer/allwinner-a10-pit.c b/qemu/hw/timer/allwinner-a10-pit.c
index 34124fe3d..51cdc98f3 100644
--- a/qemu/hw/timer/allwinner-a10-pit.c
+++ b/qemu/hw/timer/allwinner-a10-pit.c
@@ -15,6 +15,7 @@
* for more details.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "sysemu/sysemu.h"
#include "hw/timer/allwinner-a10-pit.h"
diff --git a/qemu/hw/timer/arm_mptimer.c b/qemu/hw/timer/arm_mptimer.c
index 3e59c2a28..d66bbf01b 100644
--- a/qemu/hw/timer/arm_mptimer.c
+++ b/qemu/hw/timer/arm_mptimer.c
@@ -19,7 +19,9 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/timer/arm_mptimer.h"
+#include "qapi/error.h"
#include "qemu/timer.h"
#include "qom/cpu.h"
@@ -220,8 +222,9 @@ static void arm_mptimer_realize(DeviceState *dev, Error **errp)
int i;
if (s->num_cpu < 1 || s->num_cpu > ARM_MPTIMER_MAX_CPUS) {
- hw_error("%s: num-cpu must be between 1 and %d\n",
- __func__, ARM_MPTIMER_MAX_CPUS);
+ error_setg(errp, "num-cpu must be between 1 and %d",
+ ARM_MPTIMER_MAX_CPUS);
+ return;
}
/* We implement one timer block per CPU, and expose multiple MMIO regions:
* * region 0 is "timer for this core"
diff --git a/qemu/hw/timer/arm_timer.c b/qemu/hw/timer/arm_timer.c
index d53f39ad6..f1ede5f53 100644
--- a/qemu/hw/timer/arm_timer.c
+++ b/qemu/hw/timer/arm_timer.c
@@ -7,6 +7,7 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "qemu/timer.h"
#include "qemu-common.h"
@@ -276,21 +277,25 @@ static const VMStateDescription vmstate_sp804 = {
}
};
-static int sp804_init(SysBusDevice *sbd)
+static void sp804_init(Object *obj)
{
- DeviceState *dev = DEVICE(sbd);
- SP804State *s = SP804(dev);
+ SP804State *s = SP804(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
sysbus_init_irq(sbd, &s->irq);
+ memory_region_init_io(&s->iomem, obj, &sp804_ops, s,
+ "sp804", 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static void sp804_realize(DeviceState *dev, Error **errp)
+{
+ SP804State *s = SP804(dev);
+
s->timer[0] = arm_timer_init(s->freq0);
s->timer[1] = arm_timer_init(s->freq1);
s->timer[0]->irq = qemu_allocate_irq(sp804_set_irq, s, 0);
s->timer[1]->irq = qemu_allocate_irq(sp804_set_irq, s, 1);
- memory_region_init_io(&s->iomem, OBJECT(s), &sp804_ops, s,
- "sp804", 0x1000);
- sysbus_init_mmio(sbd, &s->iomem);
- vmstate_register(dev, -1, &vmstate_sp804, s);
- return 0;
}
/* Integrator/CP timer module. */
@@ -343,9 +348,10 @@ static const MemoryRegionOps icp_pit_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static int icp_pit_init(SysBusDevice *dev)
+static void icp_pit_init(Object *obj)
{
- icp_pit_state *s = INTEGRATOR_PIT(dev);
+ icp_pit_state *s = INTEGRATOR_PIT(obj);
+ SysBusDevice *dev = SYS_BUS_DEVICE(obj);
/* Timer 0 runs at the system clock speed (40MHz). */
s->timer[0] = arm_timer_init(40000000);
@@ -357,26 +363,18 @@ static int icp_pit_init(SysBusDevice *dev)
sysbus_init_irq(dev, &s->timer[1]->irq);
sysbus_init_irq(dev, &s->timer[2]->irq);
- memory_region_init_io(&s->iomem, OBJECT(s), &icp_pit_ops, s,
+ memory_region_init_io(&s->iomem, obj, &icp_pit_ops, s,
"icp_pit", 0x1000);
sysbus_init_mmio(dev, &s->iomem);
/* This device has no state to save/restore. The component timers will
save themselves. */
- return 0;
-}
-
-static void icp_pit_class_init(ObjectClass *klass, void *data)
-{
- SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
-
- sdc->init = icp_pit_init;
}
static const TypeInfo icp_pit_info = {
.name = TYPE_INTEGRATOR_PIT,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(icp_pit_state),
- .class_init = icp_pit_class_init,
+ .instance_init = icp_pit_init,
};
static Property sp804_properties[] = {
@@ -387,17 +385,18 @@ static Property sp804_properties[] = {
static void sp804_class_init(ObjectClass *klass, void *data)
{
- SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
DeviceClass *k = DEVICE_CLASS(klass);
- sdc->init = sp804_init;
+ k->realize = sp804_realize;
k->props = sp804_properties;
+ k->vmsd = &vmstate_sp804;
}
static const TypeInfo sp804_info = {
.name = TYPE_SP804,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(SP804State),
+ .instance_init = sp804_init,
.class_init = sp804_class_init,
};
diff --git a/qemu/hw/timer/aspeed_timer.c b/qemu/hw/timer/aspeed_timer.c
new file mode 100644
index 000000000..51e8303cd
--- /dev/null
+++ b/qemu/hw/timer/aspeed_timer.c
@@ -0,0 +1,449 @@
+/*
+ * ASPEED AST2400 Timer
+ *
+ * Andrew Jeffery <andrew@aj.id.au>
+ *
+ * Copyright (C) 2016 IBM Corp.
+ *
+ * This code is licensed under the GPL version 2 or later. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/ptimer.h"
+#include "hw/sysbus.h"
+#include "hw/timer/aspeed_timer.h"
+#include "qemu-common.h"
+#include "qemu/bitops.h"
+#include "qemu/main-loop.h"
+#include "qemu/timer.h"
+#include "trace.h"
+
+#define TIMER_NR_REGS 4
+
+#define TIMER_CTRL_BITS 4
+#define TIMER_CTRL_MASK ((1 << TIMER_CTRL_BITS) - 1)
+
+#define TIMER_CLOCK_USE_EXT true
+#define TIMER_CLOCK_EXT_HZ 1000000
+#define TIMER_CLOCK_USE_APB false
+#define TIMER_CLOCK_APB_HZ 24000000
+
+#define TIMER_REG_STATUS 0
+#define TIMER_REG_RELOAD 1
+#define TIMER_REG_MATCH_FIRST 2
+#define TIMER_REG_MATCH_SECOND 3
+
+#define TIMER_FIRST_CAP_PULSE 4
+
+enum timer_ctrl_op {
+ op_enable = 0,
+ op_external_clock,
+ op_overflow_interrupt,
+ op_pulse_enable
+};
+
+/**
+ * Avoid mutual references between AspeedTimerCtrlState and AspeedTimer
+ * structs, as it's a waste of memory. The ptimer BH callback needs to know
+ * whether a specific AspeedTimer is enabled, but this information is held in
+ * AspeedTimerCtrlState. So, provide a helper to hoist ourselves from an
+ * arbitrary AspeedTimer to AspeedTimerCtrlState.
+ */
+static inline AspeedTimerCtrlState *timer_to_ctrl(AspeedTimer *t)
+{
+ const AspeedTimer (*timers)[] = (void *)t - (t->id * sizeof(*t));
+ return container_of(timers, AspeedTimerCtrlState, timers);
+}
+
+static inline bool timer_ctrl_status(AspeedTimer *t, enum timer_ctrl_op op)
+{
+ return !!(timer_to_ctrl(t)->ctrl & BIT(t->id * TIMER_CTRL_BITS + op));
+}
+
+static inline bool timer_enabled(AspeedTimer *t)
+{
+ return timer_ctrl_status(t, op_enable);
+}
+
+static inline bool timer_overflow_interrupt(AspeedTimer *t)
+{
+ return timer_ctrl_status(t, op_overflow_interrupt);
+}
+
+static inline bool timer_can_pulse(AspeedTimer *t)
+{
+ return t->id >= TIMER_FIRST_CAP_PULSE;
+}
+
+static void aspeed_timer_expire(void *opaque)
+{
+ AspeedTimer *t = opaque;
+
+ /* Only support interrupts on match values of zero for the moment - this is
+ * sufficient to boot an aspeed_defconfig Linux kernel.
+ *
+ * TODO: matching on arbitrary values (see e.g. hw/timer/a9gtimer.c)
+ */
+ bool match = !(t->match[0] && t->match[1]);
+ bool interrupt = timer_overflow_interrupt(t) || match;
+ if (timer_enabled(t) && interrupt) {
+ t->level = !t->level;
+ qemu_set_irq(t->irq, t->level);
+ }
+}
+
+static uint64_t aspeed_timer_get_value(AspeedTimer *t, int reg)
+{
+ uint64_t value;
+
+ switch (reg) {
+ case TIMER_REG_STATUS:
+ value = ptimer_get_count(t->timer);
+ break;
+ case TIMER_REG_RELOAD:
+ value = t->reload;
+ break;
+ case TIMER_REG_MATCH_FIRST:
+ case TIMER_REG_MATCH_SECOND:
+ value = t->match[reg - 2];
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: Programming error: unexpected reg: %d\n",
+ __func__, reg);
+ value = 0;
+ break;
+ }
+ return value;
+}
+
+static uint64_t aspeed_timer_read(void *opaque, hwaddr offset, unsigned size)
+{
+ AspeedTimerCtrlState *s = opaque;
+ const int reg = (offset & 0xf) / 4;
+ uint64_t value;
+
+ switch (offset) {
+ case 0x30: /* Control Register */
+ value = s->ctrl;
+ break;
+ case 0x34: /* Control Register 2 */
+ value = s->ctrl2;
+ break;
+ case 0x00 ... 0x2c: /* Timers 1 - 4 */
+ value = aspeed_timer_get_value(&s->timers[(offset >> 4)], reg);
+ break;
+ case 0x40 ... 0x8c: /* Timers 5 - 8 */
+ value = aspeed_timer_get_value(&s->timers[(offset >> 4) - 1], reg);
+ break;
+ /* Illegal */
+ case 0x38:
+ case 0x3C:
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ value = 0;
+ break;
+ }
+ trace_aspeed_timer_read(offset, size, value);
+ return value;
+}
+
+static void aspeed_timer_set_value(AspeedTimerCtrlState *s, int timer, int reg,
+ uint32_t value)
+{
+ AspeedTimer *t;
+
+ trace_aspeed_timer_set_value(timer, reg, value);
+ t = &s->timers[timer];
+ switch (reg) {
+ case TIMER_REG_STATUS:
+ if (timer_enabled(t)) {
+ ptimer_set_count(t->timer, value);
+ }
+ break;
+ case TIMER_REG_RELOAD:
+ t->reload = value;
+ ptimer_set_limit(t->timer, value, 1);
+ break;
+ case TIMER_REG_MATCH_FIRST:
+ case TIMER_REG_MATCH_SECOND:
+ if (value) {
+ /* Non-zero match values are unsupported. As such an interrupt will
+ * always be triggered when the timer reaches zero even if the
+ * overflow interrupt control bit is clear.
+ */
+ qemu_log_mask(LOG_UNIMP, "%s: Match value unsupported by device: "
+ "0x%" PRIx32 "\n", __func__, value);
+ } else {
+ t->match[reg - 2] = value;
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: Programming error: unexpected reg: %d\n",
+ __func__, reg);
+ break;
+ }
+}
+
+/* Control register operations are broken out into helpers that can be
+ * explictly called on aspeed_timer_reset(), but also from
+ * aspeed_timer_ctrl_op().
+ */
+
+static void aspeed_timer_ctrl_enable(AspeedTimer *t, bool enable)
+{
+ trace_aspeed_timer_ctrl_enable(t->id, enable);
+ if (enable) {
+ ptimer_run(t->timer, 0);
+ } else {
+ ptimer_stop(t->timer);
+ ptimer_set_limit(t->timer, t->reload, 1);
+ }
+}
+
+static void aspeed_timer_ctrl_external_clock(AspeedTimer *t, bool enable)
+{
+ trace_aspeed_timer_ctrl_external_clock(t->id, enable);
+ if (enable) {
+ ptimer_set_freq(t->timer, TIMER_CLOCK_EXT_HZ);
+ } else {
+ ptimer_set_freq(t->timer, TIMER_CLOCK_APB_HZ);
+ }
+}
+
+static void aspeed_timer_ctrl_overflow_interrupt(AspeedTimer *t, bool enable)
+{
+ trace_aspeed_timer_ctrl_overflow_interrupt(t->id, enable);
+}
+
+static void aspeed_timer_ctrl_pulse_enable(AspeedTimer *t, bool enable)
+{
+ if (timer_can_pulse(t)) {
+ trace_aspeed_timer_ctrl_pulse_enable(t->id, enable);
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Timer does not support pulse mode\n", __func__);
+ }
+}
+
+/**
+ * Given the actions are fixed in number and completely described in helper
+ * functions, dispatch with a lookup table rather than manage control flow with
+ * a switch statement.
+ */
+static void (*const ctrl_ops[])(AspeedTimer *, bool) = {
+ [op_enable] = aspeed_timer_ctrl_enable,
+ [op_external_clock] = aspeed_timer_ctrl_external_clock,
+ [op_overflow_interrupt] = aspeed_timer_ctrl_overflow_interrupt,
+ [op_pulse_enable] = aspeed_timer_ctrl_pulse_enable,
+};
+
+/**
+ * Conditionally affect changes chosen by a timer's control bit.
+ *
+ * The aspeed_timer_ctrl_op() interface is convenient for the
+ * aspeed_timer_set_ctrl() function as the "no change" early exit can be
+ * calculated for all operations, which cleans up the caller code. However the
+ * interface isn't convenient for the reset function where we want to enter a
+ * specific state without artificially constructing old and new values that
+ * will fall through the change guard (and motivates extracting the actions
+ * out to helper functions).
+ *
+ * @t: The timer to manipulate
+ * @op: The type of operation to be performed
+ * @old: The old state of the timer's control bits
+ * @new: The incoming state for the timer's control bits
+ */
+static void aspeed_timer_ctrl_op(AspeedTimer *t, enum timer_ctrl_op op,
+ uint8_t old, uint8_t new)
+{
+ const uint8_t mask = BIT(op);
+ const bool enable = !!(new & mask);
+ const bool changed = ((old ^ new) & mask);
+ if (!changed) {
+ return;
+ }
+ ctrl_ops[op](t, enable);
+}
+
+static void aspeed_timer_set_ctrl(AspeedTimerCtrlState *s, uint32_t reg)
+{
+ int i;
+ int shift;
+ uint8_t t_old, t_new;
+ AspeedTimer *t;
+ const uint8_t enable_mask = BIT(op_enable);
+
+ /* Handle a dependency between the 'enable' and remaining three
+ * configuration bits - i.e. if more than one bit in the control set has
+ * changed, including the 'enable' bit, then we want either disable the
+ * timer and perform configuration, or perform configuration and then
+ * enable the timer
+ */
+ for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) {
+ t = &s->timers[i];
+ shift = (i * TIMER_CTRL_BITS);
+ t_old = (s->ctrl >> shift) & TIMER_CTRL_MASK;
+ t_new = (reg >> shift) & TIMER_CTRL_MASK;
+
+ /* If we are disabling, do so first */
+ if ((t_old & enable_mask) && !(t_new & enable_mask)) {
+ aspeed_timer_ctrl_enable(t, false);
+ }
+ aspeed_timer_ctrl_op(t, op_external_clock, t_old, t_new);
+ aspeed_timer_ctrl_op(t, op_overflow_interrupt, t_old, t_new);
+ aspeed_timer_ctrl_op(t, op_pulse_enable, t_old, t_new);
+ /* If we are enabling, do so last */
+ if (!(t_old & enable_mask) && (t_new & enable_mask)) {
+ aspeed_timer_ctrl_enable(t, true);
+ }
+ }
+ s->ctrl = reg;
+}
+
+static void aspeed_timer_set_ctrl2(AspeedTimerCtrlState *s, uint32_t value)
+{
+ trace_aspeed_timer_set_ctrl2(value);
+}
+
+static void aspeed_timer_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ const uint32_t tv = (uint32_t)(value & 0xFFFFFFFF);
+ const int reg = (offset & 0xf) / 4;
+ AspeedTimerCtrlState *s = opaque;
+
+ switch (offset) {
+ /* Control Registers */
+ case 0x30:
+ aspeed_timer_set_ctrl(s, tv);
+ break;
+ case 0x34:
+ aspeed_timer_set_ctrl2(s, tv);
+ break;
+ /* Timer Registers */
+ case 0x00 ... 0x2c:
+ aspeed_timer_set_value(s, (offset >> TIMER_NR_REGS), reg, tv);
+ break;
+ case 0x40 ... 0x8c:
+ aspeed_timer_set_value(s, (offset >> TIMER_NR_REGS) - 1, reg, tv);
+ break;
+ /* Illegal */
+ case 0x38:
+ case 0x3C:
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ break;
+ }
+}
+
+static const MemoryRegionOps aspeed_timer_ops = {
+ .read = aspeed_timer_read,
+ .write = aspeed_timer_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .valid.unaligned = false,
+};
+
+static void aspeed_init_one_timer(AspeedTimerCtrlState *s, uint8_t id)
+{
+ QEMUBH *bh;
+ AspeedTimer *t = &s->timers[id];
+
+ t->id = id;
+ bh = qemu_bh_new(aspeed_timer_expire, t);
+ t->timer = ptimer_init(bh);
+}
+
+static void aspeed_timer_realize(DeviceState *dev, Error **errp)
+{
+ int i;
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ AspeedTimerCtrlState *s = ASPEED_TIMER(dev);
+
+ for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) {
+ aspeed_init_one_timer(s, i);
+ sysbus_init_irq(sbd, &s->timers[i].irq);
+ }
+ memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_timer_ops, s,
+ TYPE_ASPEED_TIMER, 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static void aspeed_timer_reset(DeviceState *dev)
+{
+ int i;
+ AspeedTimerCtrlState *s = ASPEED_TIMER(dev);
+
+ for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) {
+ AspeedTimer *t = &s->timers[i];
+ /* Explictly call helpers to avoid any conditional behaviour through
+ * aspeed_timer_set_ctrl().
+ */
+ aspeed_timer_ctrl_enable(t, false);
+ aspeed_timer_ctrl_external_clock(t, TIMER_CLOCK_USE_APB);
+ aspeed_timer_ctrl_overflow_interrupt(t, false);
+ aspeed_timer_ctrl_pulse_enable(t, false);
+ t->level = 0;
+ t->reload = 0;
+ t->match[0] = 0;
+ t->match[1] = 0;
+ }
+ s->ctrl = 0;
+ s->ctrl2 = 0;
+}
+
+static const VMStateDescription vmstate_aspeed_timer = {
+ .name = "aspeed.timer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(id, AspeedTimer),
+ VMSTATE_INT32(level, AspeedTimer),
+ VMSTATE_PTIMER(timer, AspeedTimer),
+ VMSTATE_UINT32(reload, AspeedTimer),
+ VMSTATE_UINT32_ARRAY(match, AspeedTimer, 2),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_aspeed_timer_state = {
+ .name = "aspeed.timerctrl",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(ctrl, AspeedTimerCtrlState),
+ VMSTATE_UINT32(ctrl2, AspeedTimerCtrlState),
+ VMSTATE_STRUCT_ARRAY(timers, AspeedTimerCtrlState,
+ ASPEED_TIMER_NR_TIMERS, 1, vmstate_aspeed_timer,
+ AspeedTimer),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void timer_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = aspeed_timer_realize;
+ dc->reset = aspeed_timer_reset;
+ dc->desc = "ASPEED Timer";
+ dc->vmsd = &vmstate_aspeed_timer_state;
+}
+
+static const TypeInfo aspeed_timer_info = {
+ .name = TYPE_ASPEED_TIMER,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AspeedTimerCtrlState),
+ .class_init = timer_class_init,
+};
+
+static void aspeed_timer_register_types(void)
+{
+ type_register_static(&aspeed_timer_info);
+}
+
+type_init(aspeed_timer_register_types)
diff --git a/qemu/hw/timer/cadence_ttc.c b/qemu/hw/timer/cadence_ttc.c
index 35bc88033..03f5b9c20 100644
--- a/qemu/hw/timer/cadence_ttc.c
+++ b/qemu/hw/timer/cadence_ttc.c
@@ -16,6 +16,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "qemu/timer.h"
diff --git a/qemu/hw/timer/digic-timer.c b/qemu/hw/timer/digic-timer.c
index 7e28e7e5f..5b97e1e1a 100644
--- a/qemu/hw/timer/digic-timer.c
+++ b/qemu/hw/timer/digic-timer.c
@@ -26,6 +26,7 @@
*
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/ptimer.h"
#include "qemu/main-loop.h"
diff --git a/qemu/hw/timer/ds1338.c b/qemu/hw/timer/ds1338.c
index ec6dbeeab..0112949e2 100644
--- a/qemu/hw/timer/ds1338.c
+++ b/qemu/hw/timer/ds1338.c
@@ -10,7 +10,10 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
#include "hw/i2c/i2c.h"
+#include "qemu/bcd.h"
/* Size of NVRAM including both the user-accessible area and the
* secondary register area.
diff --git a/qemu/hw/timer/etraxfs_timer.c b/qemu/hw/timer/etraxfs_timer.c
index aee4990eb..36d8f462c 100644
--- a/qemu/hw/timer/etraxfs_timer.c
+++ b/qemu/hw/timer/etraxfs_timer.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "sysemu/sysemu.h"
#include "qemu/timer.h"
diff --git a/qemu/hw/timer/exynos4210_mct.c b/qemu/hw/timer/exynos4210_mct.c
index 015bbaf1b..ae69345f0 100644
--- a/qemu/hw/timer/exynos4210_mct.c
+++ b/qemu/hw/timer/exynos4210_mct.c
@@ -52,6 +52,7 @@
* there is no way to avoid frequently events).
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "qemu/timer.h"
#include "qemu/main-loop.h"
@@ -1421,10 +1422,11 @@ static const MemoryRegionOps exynos4210_mct_ops = {
};
/* MCT init */
-static int exynos4210_mct_init(SysBusDevice *dev)
+static void exynos4210_mct_init(Object *obj)
{
int i;
- Exynos4210MCTState *s = EXYNOS4210_MCT(dev);
+ Exynos4210MCTState *s = EXYNOS4210_MCT(obj);
+ SysBusDevice *dev = SYS_BUS_DEVICE(obj);
QEMUBH *bh[2];
/* Global timer */
@@ -1449,19 +1451,15 @@ static int exynos4210_mct_init(SysBusDevice *dev)
sysbus_init_irq(dev, &s->l_timer[i].irq);
}
- memory_region_init_io(&s->iomem, OBJECT(s), &exynos4210_mct_ops, s,
+ memory_region_init_io(&s->iomem, obj, &exynos4210_mct_ops, s,
"exynos4210-mct", MCT_SFR_SIZE);
sysbus_init_mmio(dev, &s->iomem);
-
- return 0;
}
static void exynos4210_mct_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = exynos4210_mct_init;
dc->reset = exynos4210_mct_reset;
dc->vmsd = &vmstate_exynos4210_mct_state;
}
@@ -1470,6 +1468,7 @@ static const TypeInfo exynos4210_mct_info = {
.name = TYPE_EXYNOS4210_MCT,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(Exynos4210MCTState),
+ .instance_init = exynos4210_mct_init,
.class_init = exynos4210_mct_class_init,
};
diff --git a/qemu/hw/timer/exynos4210_pwm.c b/qemu/hw/timer/exynos4210_pwm.c
index 1c1a2b8ff..0e9e2e9bf 100644
--- a/qemu/hw/timer/exynos4210_pwm.c
+++ b/qemu/hw/timer/exynos4210_pwm.c
@@ -20,6 +20,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "qemu/timer.h"
#include "qemu-common.h"
@@ -379,9 +380,10 @@ static const MemoryRegionOps exynos4210_pwm_ops = {
/*
* PWM timer initialization
*/
-static int exynos4210_pwm_init(SysBusDevice *dev)
+static void exynos4210_pwm_init(Object *obj)
{
- Exynos4210PWMState *s = EXYNOS4210_PWM(dev);
+ Exynos4210PWMState *s = EXYNOS4210_PWM(obj);
+ SysBusDevice *dev = SYS_BUS_DEVICE(obj);
int i;
QEMUBH *bh;
@@ -393,19 +395,15 @@ static int exynos4210_pwm_init(SysBusDevice *dev)
s->timer[i].parent = s;
}
- memory_region_init_io(&s->iomem, OBJECT(s), &exynos4210_pwm_ops, s,
+ memory_region_init_io(&s->iomem, obj, &exynos4210_pwm_ops, s,
"exynos4210-pwm", EXYNOS4210_PWM_REG_MEM_SIZE);
sysbus_init_mmio(dev, &s->iomem);
-
- return 0;
}
static void exynos4210_pwm_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = exynos4210_pwm_init;
dc->reset = exynos4210_pwm_reset;
dc->vmsd = &vmstate_exynos4210_pwm_state;
}
@@ -414,6 +412,7 @@ static const TypeInfo exynos4210_pwm_info = {
.name = TYPE_EXYNOS4210_PWM,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(Exynos4210PWMState),
+ .instance_init = exynos4210_pwm_init,
.class_init = exynos4210_pwm_class_init,
};
diff --git a/qemu/hw/timer/exynos4210_rtc.c b/qemu/hw/timer/exynos4210_rtc.c
index bf2ee9f80..da4dd451b 100644
--- a/qemu/hw/timer/exynos4210_rtc.c
+++ b/qemu/hw/timer/exynos4210_rtc.c
@@ -25,9 +25,11 @@
* CLKOUTEN Bit[9] not used
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "qemu/timer.h"
#include "qemu-common.h"
+#include "qemu/bcd.h"
#include "hw/ptimer.h"
#include "hw/hw.h"
@@ -546,9 +548,10 @@ static const MemoryRegionOps exynos4210_rtc_ops = {
/*
* RTC timer initialization
*/
-static int exynos4210_rtc_init(SysBusDevice *dev)
+static void exynos4210_rtc_init(Object *obj)
{
- Exynos4210RTCState *s = EXYNOS4210_RTC(dev);
+ Exynos4210RTCState *s = EXYNOS4210_RTC(obj);
+ SysBusDevice *dev = SYS_BUS_DEVICE(obj);
QEMUBH *bh;
bh = qemu_bh_new(exynos4210_rtc_tick, s);
@@ -563,19 +566,15 @@ static int exynos4210_rtc_init(SysBusDevice *dev)
sysbus_init_irq(dev, &s->alm_irq);
sysbus_init_irq(dev, &s->tick_irq);
- memory_region_init_io(&s->iomem, OBJECT(s), &exynos4210_rtc_ops, s,
+ memory_region_init_io(&s->iomem, obj, &exynos4210_rtc_ops, s,
"exynos4210-rtc", EXYNOS4210_RTC_REG_MEM_SIZE);
sysbus_init_mmio(dev, &s->iomem);
-
- return 0;
}
static void exynos4210_rtc_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = exynos4210_rtc_init;
dc->reset = exynos4210_rtc_reset;
dc->vmsd = &vmstate_exynos4210_rtc_state;
}
@@ -584,6 +583,7 @@ static const TypeInfo exynos4210_rtc_info = {
.name = TYPE_EXYNOS4210_RTC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(Exynos4210RTCState),
+ .instance_init = exynos4210_rtc_init,
.class_init = exynos4210_rtc_class_init,
};
diff --git a/qemu/hw/timer/grlib_gptimer.c b/qemu/hw/timer/grlib_gptimer.c
index d655bb2a2..dd000f5af 100644
--- a/qemu/hw/timer/grlib_gptimer.c
+++ b/qemu/hw/timer/grlib_gptimer.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "qemu/timer.h"
#include "hw/ptimer.h"
diff --git a/qemu/hw/timer/hpet.c b/qemu/hw/timer/hpet.c
index 2bb62211c..a2c18b30c 100644
--- a/qemu/hw/timer/hpet.c
+++ b/qemu/hw/timer/hpet.c
@@ -24,9 +24,11 @@
* This driver attempts to emulate an HPET device in software.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "ui/console.h"
+#include "qapi/error.h"
#include "qemu/error-report.h"
#include "qemu/timer.h"
#include "hw/timer/hpet.h"
@@ -116,22 +118,22 @@ static uint32_t timer_enabled(HPETTimer *t)
static uint32_t hpet_time_after(uint64_t a, uint64_t b)
{
- return ((int32_t)(b) - (int32_t)(a) < 0);
+ return ((int32_t)(b - a) < 0);
}
static uint32_t hpet_time_after64(uint64_t a, uint64_t b)
{
- return ((int64_t)(b) - (int64_t)(a) < 0);
+ return ((int64_t)(b - a) < 0);
}
static uint64_t ticks_to_ns(uint64_t value)
{
- return (muldiv64(value, HPET_CLK_PERIOD, FS_PER_NS));
+ return value * HPET_CLK_PERIOD;
}
static uint64_t ns_to_ticks(uint64_t value)
{
- return (muldiv64(value, FS_PER_NS, HPET_CLK_PERIOD));
+ return value / HPET_CLK_PERIOD;
}
static uint64_t hpet_fixup_reg(uint64_t new, uint64_t old, uint64_t mask)
@@ -199,12 +201,7 @@ static void update_irq(struct HPETTimer *timer, int set)
if (!set || !timer_enabled(timer) || !hpet_enabled(timer->state)) {
s->isr &= ~mask;
if (!timer_fsb_route(timer)) {
- /* fold the ICH PIRQ# pin's internal inversion logic into hpet */
- if (route >= ISA_NUM_IRQS) {
- qemu_irq_raise(s->irqs[route]);
- } else {
- qemu_irq_lower(s->irqs[route]);
- }
+ qemu_irq_lower(s->irqs[route]);
}
} else if (timer_fsb_route(timer)) {
address_space_stl_le(&address_space_memory, timer->fsb >> 32,
@@ -212,12 +209,7 @@ static void update_irq(struct HPETTimer *timer, int set)
NULL);
} else if (timer->config & HPET_TN_TYPE_LEVEL) {
s->isr |= mask;
- /* fold the ICH PIRQ# pin's internal inversion logic into hpet */
- if (route >= ISA_NUM_IRQS) {
- qemu_irq_lower(s->irqs[route]);
- } else {
- qemu_irq_raise(s->irqs[route]);
- }
+ qemu_irq_raise(s->irqs[route]);
} else {
s->isr &= ~mask;
qemu_irq_pulse(s->irqs[route]);
@@ -713,7 +705,7 @@ static void hpet_init(Object *obj)
HPETState *s = HPET(obj);
/* HPET Area */
- memory_region_init_io(&s->iomem, obj, &hpet_ram_ops, s, "hpet", 0x400);
+ memory_region_init_io(&s->iomem, obj, &hpet_ram_ops, s, "hpet", HPET_LEN);
sysbus_init_mmio(sbd, &s->iomem);
}
@@ -758,7 +750,7 @@ static void hpet_realize(DeviceState *dev, Error **errp)
/* 64-bit main counter; LegacyReplacementRoute. */
s->capability = 0x8086a001ULL;
s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT;
- s->capability |= ((HPET_CLK_PERIOD) << 32);
+ s->capability |= ((uint64_t)(HPET_CLK_PERIOD * FS_PER_NS) << 32);
qdev_init_gpio_in(dev, hpet_handle_legacy_irq, 2);
qdev_init_gpio_out(dev, &s->pit_enabled, 1);
diff --git a/qemu/hw/timer/i8254.c b/qemu/hw/timer/i8254.c
index 9b65a3369..5e61ad50a 100644
--- a/qemu/hw/timer/i8254.c
+++ b/qemu/hw/timer/i8254.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/isa/isa.h"
@@ -52,7 +53,7 @@ static int pit_get_count(PITChannelState *s)
int counter;
d = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->count_load_time, PIT_FREQ,
- get_ticks_per_sec());
+ NANOSECONDS_PER_SECOND);
switch(s->mode) {
case 0:
case 1:
@@ -262,7 +263,7 @@ static void pit_irq_timer_update(PITChannelState *s, int64_t current_time)
#ifdef DEBUG_PIT
printf("irq_level=%d next_delay=%f\n",
irq_level,
- (double)(expire_time - current_time) / get_ticks_per_sec());
+ (double)(expire_time - current_time) / NANOSECONDS_PER_SECOND);
#endif
s->next_transition_time = expire_time;
if (expire_time != -1)
diff --git a/qemu/hw/timer/i8254_common.c b/qemu/hw/timer/i8254_common.c
index 07345f668..e18299a48 100644
--- a/qemu/hw/timer/i8254_common.c
+++ b/qemu/hw/timer/i8254_common.c
@@ -22,6 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/isa/isa.h"
@@ -46,7 +47,7 @@ int pit_get_out(PITChannelState *s, int64_t current_time)
int out;
d = muldiv64(current_time - s->count_load_time, PIT_FREQ,
- get_ticks_per_sec());
+ NANOSECONDS_PER_SECOND);
switch (s->mode) {
default:
case 0:
@@ -80,7 +81,7 @@ int64_t pit_get_next_transition_time(PITChannelState *s, int64_t current_time)
int period2;
d = muldiv64(current_time - s->count_load_time, PIT_FREQ,
- get_ticks_per_sec());
+ NANOSECONDS_PER_SECOND);
switch (s->mode) {
default:
case 0:
@@ -120,7 +121,7 @@ int64_t pit_get_next_transition_time(PITChannelState *s, int64_t current_time)
break;
}
/* convert to timer units */
- next_time = s->count_load_time + muldiv64(next_time, get_ticks_per_sec(),
+ next_time = s->count_load_time + muldiv64(next_time, NANOSECONDS_PER_SECOND,
PIT_FREQ);
/* fix potential rounding problems */
/* XXX: better solution: use a clock at PIT_FREQ Hz */
diff --git a/qemu/hw/timer/imx_epit.c b/qemu/hw/timer/imx_epit.c
index ffefc22f4..f5836e21f 100644
--- a/qemu/hw/timer/imx_epit.c
+++ b/qemu/hw/timer/imx_epit.c
@@ -5,25 +5,29 @@
* Copyright (c) 2011 NICTA Pty Ltd
* Originally written by Hans Jiang
* Updated by Peter Chubb
- * Updated by Jean-Christophe Dubois
+ * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
*
* This code is licensed under GPL version 2 or later. See
* the COPYING file in the top-level directory.
*
*/
-#include "hw/hw.h"
-#include "qemu/bitops.h"
-#include "qemu/timer.h"
-#include "hw/ptimer.h"
-#include "hw/sysbus.h"
-#include "hw/arm/imx.h"
+#include "qemu/osdep.h"
+#include "hw/timer/imx_epit.h"
+#include "hw/misc/imx_ccm.h"
#include "qemu/main-loop.h"
-#define TYPE_IMX_EPIT "imx.epit"
+#ifndef DEBUG_IMX_EPIT
+#define DEBUG_IMX_EPIT 0
+#endif
-#define DEBUG_TIMER 0
-#if DEBUG_TIMER
+#define DPRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX_EPIT) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_EPIT, \
+ __func__, ##args); \
+ } \
+ } while (0)
static char const *imx_epit_reg_name(uint32_t reg)
{
@@ -43,76 +47,17 @@ static char const *imx_epit_reg_name(uint32_t reg)
}
}
-# define DPRINTF(fmt, args...) \
- do { fprintf(stderr, "%s: " fmt , __func__, ##args); } while (0)
-#else
-# define DPRINTF(fmt, args...) do {} while (0)
-#endif
-
-/*
- * Define to 1 for messages about attempts to
- * access unimplemented registers or similar.
- */
-#define DEBUG_IMPLEMENTATION 1
-#if DEBUG_IMPLEMENTATION
-# define IPRINTF(fmt, args...) \
- do { fprintf(stderr, "%s: " fmt, __func__, ##args); } while (0)
-#else
-# define IPRINTF(fmt, args...) do {} while (0)
-#endif
-
-#define IMX_EPIT(obj) \
- OBJECT_CHECK(IMXEPITState, (obj), TYPE_IMX_EPIT)
-
-/*
- * EPIT: Enhanced periodic interrupt timer
- */
-
-#define CR_EN (1 << 0)
-#define CR_ENMOD (1 << 1)
-#define CR_OCIEN (1 << 2)
-#define CR_RLD (1 << 3)
-#define CR_PRESCALE_SHIFT (4)
-#define CR_PRESCALE_MASK (0xfff)
-#define CR_SWR (1 << 16)
-#define CR_IOVW (1 << 17)
-#define CR_DBGEN (1 << 18)
-#define CR_WAITEN (1 << 19)
-#define CR_DOZEN (1 << 20)
-#define CR_STOPEN (1 << 21)
-#define CR_CLKSRC_SHIFT (24)
-#define CR_CLKSRC_MASK (0x3 << CR_CLKSRC_SHIFT)
-
-#define EPIT_TIMER_MAX 0XFFFFFFFFUL
-
/*
* Exact clock frequencies vary from board to board.
* These are typical.
*/
static const IMXClk imx_epit_clocks[] = {
- 0, /* 00 disabled */
- IPG, /* 01 ipg_clk, ~532MHz */
- IPG, /* 10 ipg_clk_highfreq */
- CLK_32k, /* 11 ipg_clk_32k -- ~32kHz */
+ CLK_NONE, /* 00 disabled */
+ CLK_IPG, /* 01 ipg_clk, ~532MHz */
+ CLK_IPG_HIGH, /* 10 ipg_clk_highfreq */
+ CLK_32k, /* 11 ipg_clk_32k -- ~32kHz */
};
-typedef struct {
- SysBusDevice busdev;
- ptimer_state *timer_reload;
- ptimer_state *timer_cmp;
- MemoryRegion iomem;
- DeviceState *ccm;
-
- uint32_t cr;
- uint32_t sr;
- uint32_t lr;
- uint32_t cmp;
- uint32_t cnt;
-
- uint32_t freq;
- qemu_irq irq;
-} IMXEPITState;
-
/*
* Update interrupt status
*/
@@ -129,20 +74,18 @@ static void imx_epit_set_freq(IMXEPITState *s)
{
uint32_t clksrc;
uint32_t prescaler;
- uint32_t freq;
clksrc = extract32(s->cr, CR_CLKSRC_SHIFT, 2);
prescaler = 1 + extract32(s->cr, CR_PRESCALE_SHIFT, 12);
- freq = imx_clock_frequency(s->ccm, imx_epit_clocks[clksrc]) / prescaler;
+ s->freq = imx_ccm_get_clock_frequency(s->ccm,
+ imx_epit_clocks[clksrc]) / prescaler;
- s->freq = freq;
+ DPRINTF("Setting ptimer frequency to %u\n", s->freq);
- DPRINTF("Setting ptimer frequency to %u\n", freq);
-
- if (freq) {
- ptimer_set_freq(s->timer_reload, freq);
- ptimer_set_freq(s->timer_cmp, freq);
+ if (s->freq) {
+ ptimer_set_freq(s->timer_reload, s->freq);
+ ptimer_set_freq(s->timer_cmp, s->freq);
}
}
@@ -174,18 +117,17 @@ static void imx_epit_reset(DeviceState *dev)
static uint32_t imx_epit_update_count(IMXEPITState *s)
{
- s->cnt = ptimer_get_count(s->timer_reload);
+ s->cnt = ptimer_get_count(s->timer_reload);
- return s->cnt;
+ return s->cnt;
}
static uint64_t imx_epit_read(void *opaque, hwaddr offset, unsigned size)
{
IMXEPITState *s = IMX_EPIT(opaque);
uint32_t reg_value = 0;
- uint32_t reg = offset >> 2;
- switch (reg) {
+ switch (offset >> 2) {
case 0: /* Control Register */
reg_value = s->cr;
break;
@@ -208,11 +150,12 @@ static uint64_t imx_epit_read(void *opaque, hwaddr offset, unsigned size)
break;
default:
- IPRINTF("Bad offset %x\n", reg);
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_EPIT, __func__, offset);
break;
}
- DPRINTF("(%s) = 0x%08x\n", imx_epit_reg_name(reg), reg_value);
+ DPRINTF("(%s) = 0x%08x\n", imx_epit_reg_name(offset >> 2), reg_value);
return reg_value;
}
@@ -237,12 +180,12 @@ static void imx_epit_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
IMXEPITState *s = IMX_EPIT(opaque);
- uint32_t reg = offset >> 2;
uint64_t oldcr;
- DPRINTF("(%s, value = 0x%08x)\n", imx_epit_reg_name(reg), (uint32_t)value);
+ DPRINTF("(%s, value = 0x%08x)\n", imx_epit_reg_name(offset >> 2),
+ (uint32_t)value);
- switch (reg) {
+ switch (offset >> 2) {
case 0: /* CR */
oldcr = s->cr;
@@ -318,7 +261,8 @@ static void imx_epit_write(void *opaque, hwaddr offset, uint64_t value,
break;
default:
- IPRINTF("Bad offset %x\n", reg);
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_EPIT, __func__, offset);
break;
}
@@ -333,24 +277,14 @@ static void imx_epit_cmp(void *opaque)
imx_epit_update_int(s);
}
-void imx_timerp_create(const hwaddr addr, qemu_irq irq, DeviceState *ccm)
-{
- IMXEPITState *pp;
- DeviceState *dev;
-
- dev = sysbus_create_simple(TYPE_IMX_EPIT, addr, irq);
- pp = IMX_EPIT(dev);
- pp->ccm = ccm;
-}
-
static const MemoryRegionOps imx_epit_ops = {
- .read = imx_epit_read,
- .write = imx_epit_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
+ .read = imx_epit_read,
+ .write = imx_epit_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
};
static const VMStateDescription vmstate_imx_timer_epit = {
- .name = "imx.epit",
+ .name = TYPE_IMX_EPIT,
.version_id = 2,
.minimum_version_id = 2,
.fields = (VMStateField[]) {
diff --git a/qemu/hw/timer/imx_gpt.c b/qemu/hw/timer/imx_gpt.c
index 3b3101084..ab2e213a1 100644
--- a/qemu/hw/timer/imx_gpt.c
+++ b/qemu/hw/timer/imx_gpt.c
@@ -5,28 +5,29 @@
* Copyright (c) 2011 NICTA Pty Ltd
* Originally written by Hans Jiang
* Updated by Peter Chubb
- * Updated by Jean-Christophe Dubois
+ * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
*
* This code is licensed under GPL version 2 or later. See
* the COPYING file in the top-level directory.
*
*/
-#include "hw/hw.h"
-#include "qemu/bitops.h"
-#include "qemu/timer.h"
-#include "hw/ptimer.h"
-#include "hw/sysbus.h"
-#include "hw/arm/imx.h"
+#include "qemu/osdep.h"
+#include "hw/timer/imx_gpt.h"
+#include "hw/misc/imx_ccm.h"
#include "qemu/main-loop.h"
-#define TYPE_IMX_GPT "imx.gpt"
+#ifndef DEBUG_IMX_GPT
+#define DEBUG_IMX_GPT 0
+#endif
-/*
- * Define to 1 for debug messages
- */
-#define DEBUG_TIMER 0
-#if DEBUG_TIMER
+#define DPRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX_GPT) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_GPT, \
+ __func__, ##args); \
+ } \
+ } while (0)
static char const *imx_gpt_reg_name(uint32_t reg)
{
@@ -56,94 +57,8 @@ static char const *imx_gpt_reg_name(uint32_t reg)
}
}
-# define DPRINTF(fmt, args...) \
- do { printf("%s: " fmt , __func__, ##args); } while (0)
-#else
-# define DPRINTF(fmt, args...) do {} while (0)
-#endif
-
-/*
- * Define to 1 for messages about attempts to
- * access unimplemented registers or similar.
- */
-#define DEBUG_IMPLEMENTATION 1
-#if DEBUG_IMPLEMENTATION
-# define IPRINTF(fmt, args...) \
- do { fprintf(stderr, "%s: " fmt, __func__, ##args); } while (0)
-#else
-# define IPRINTF(fmt, args...) do {} while (0)
-#endif
-
-#define IMX_GPT(obj) \
- OBJECT_CHECK(IMXGPTState, (obj), TYPE_IMX_GPT)
-/*
- * GPT : General purpose timer
- *
- * This timer counts up continuously while it is enabled, resetting itself
- * to 0 when it reaches GPT_TIMER_MAX (in freerun mode) or when it
- * reaches the value of one of the ocrX (in periodic mode).
- */
-
-#define GPT_TIMER_MAX 0XFFFFFFFFUL
-
-/* Control register. Not all of these bits have any effect (yet) */
-#define GPT_CR_EN (1 << 0) /* GPT Enable */
-#define GPT_CR_ENMOD (1 << 1) /* GPT Enable Mode */
-#define GPT_CR_DBGEN (1 << 2) /* GPT Debug mode enable */
-#define GPT_CR_WAITEN (1 << 3) /* GPT Wait Mode Enable */
-#define GPT_CR_DOZEN (1 << 4) /* GPT Doze mode enable */
-#define GPT_CR_STOPEN (1 << 5) /* GPT Stop Mode Enable */
-#define GPT_CR_CLKSRC_SHIFT (6)
-#define GPT_CR_CLKSRC_MASK (0x7)
-
-#define GPT_CR_FRR (1 << 9) /* Freerun or Restart */
-#define GPT_CR_SWR (1 << 15) /* Software Reset */
-#define GPT_CR_IM1 (3 << 16) /* Input capture channel 1 mode (2 bits) */
-#define GPT_CR_IM2 (3 << 18) /* Input capture channel 2 mode (2 bits) */
-#define GPT_CR_OM1 (7 << 20) /* Output Compare Channel 1 Mode (3 bits) */
-#define GPT_CR_OM2 (7 << 23) /* Output Compare Channel 2 Mode (3 bits) */
-#define GPT_CR_OM3 (7 << 26) /* Output Compare Channel 3 Mode (3 bits) */
-#define GPT_CR_FO1 (1 << 29) /* Force Output Compare Channel 1 */
-#define GPT_CR_FO2 (1 << 30) /* Force Output Compare Channel 2 */
-#define GPT_CR_FO3 (1 << 31) /* Force Output Compare Channel 3 */
-
-#define GPT_SR_OF1 (1 << 0)
-#define GPT_SR_OF2 (1 << 1)
-#define GPT_SR_OF3 (1 << 2)
-#define GPT_SR_ROV (1 << 5)
-
-#define GPT_IR_OF1IE (1 << 0)
-#define GPT_IR_OF2IE (1 << 1)
-#define GPT_IR_OF3IE (1 << 2)
-#define GPT_IR_ROVIE (1 << 5)
-
-typedef struct {
- SysBusDevice busdev;
- ptimer_state *timer;
- MemoryRegion iomem;
- DeviceState *ccm;
-
- uint32_t cr;
- uint32_t pr;
- uint32_t sr;
- uint32_t ir;
- uint32_t ocr1;
- uint32_t ocr2;
- uint32_t ocr3;
- uint32_t icr1;
- uint32_t icr2;
- uint32_t cnt;
-
- uint32_t next_timeout;
- uint32_t next_int;
-
- uint32_t freq;
-
- qemu_irq irq;
-} IMXGPTState;
-
static const VMStateDescription vmstate_imx_timer_gpt = {
- .name = "imx.gpt",
+ .name = TYPE_IMX_GPT,
.version_id = 3,
.minimum_version_id = 3,
.fields = (VMStateField[]) {
@@ -166,27 +81,27 @@ static const VMStateDescription vmstate_imx_timer_gpt = {
};
static const IMXClk imx_gpt_clocks[] = {
- NOCLK, /* 000 No clock source */
- IPG, /* 001 ipg_clk, 532MHz*/
- IPG, /* 010 ipg_clk_highfreq */
- NOCLK, /* 011 not defined */
- CLK_32k, /* 100 ipg_clk_32k */
- NOCLK, /* 101 not defined */
- NOCLK, /* 110 not defined */
- NOCLK, /* 111 not defined */
+ CLK_NONE, /* 000 No clock source */
+ CLK_IPG, /* 001 ipg_clk, 532MHz*/
+ CLK_IPG_HIGH, /* 010 ipg_clk_highfreq */
+ CLK_NONE, /* 011 not defined */
+ CLK_32k, /* 100 ipg_clk_32k */
+ CLK_NONE, /* 101 not defined */
+ CLK_NONE, /* 110 not defined */
+ CLK_NONE, /* 111 not defined */
};
static void imx_gpt_set_freq(IMXGPTState *s)
{
uint32_t clksrc = extract32(s->cr, GPT_CR_CLKSRC_SHIFT, 3);
- uint32_t freq = imx_clock_frequency(s->ccm, imx_gpt_clocks[clksrc])
- / (1 + s->pr);
- s->freq = freq;
- DPRINTF("Setting clksrc %d to frequency %d\n", clksrc, freq);
+ s->freq = imx_ccm_get_clock_frequency(s->ccm,
+ imx_gpt_clocks[clksrc]) / (1 + s->pr);
+
+ DPRINTF("Setting clksrc %d to frequency %d\n", clksrc, s->freq);
- if (freq) {
- ptimer_set_freq(s->timer, freq);
+ if (s->freq) {
+ ptimer_set_freq(s->timer, s->freq);
}
}
@@ -207,7 +122,7 @@ static uint32_t imx_gpt_update_count(IMXGPTState *s)
}
static inline uint32_t imx_gpt_find_limit(uint32_t count, uint32_t reg,
- uint32_t timeout)
+ uint32_t timeout)
{
if ((count < reg) && (timeout > reg)) {
timeout = reg;
@@ -219,7 +134,7 @@ static inline uint32_t imx_gpt_find_limit(uint32_t count, uint32_t reg,
static void imx_gpt_compute_next_timeout(IMXGPTState *s, bool event)
{
uint32_t timeout = GPT_TIMER_MAX;
- uint32_t count = 0;
+ uint32_t count;
long long limit;
if (!(s->cr & GPT_CR_EN)) {
@@ -227,20 +142,23 @@ static void imx_gpt_compute_next_timeout(IMXGPTState *s, bool event)
return;
}
- if (event) {
- /* This is a timer event */
+ /* update the count */
+ count = imx_gpt_update_count(s);
- if ((s->cr & GPT_CR_FRR) && (s->next_timeout != GPT_TIMER_MAX)) {
- /*
- * if we are in free running mode and we have not reached
- * the GPT_TIMER_MAX limit, then update the count
+ if (event) {
+ /*
+ * This is an event (the ptimer reached 0 and stopped), and the
+ * timer counter is now equal to s->next_timeout.
+ */
+ if (!(s->cr & GPT_CR_FRR) && (count == s->ocr1)) {
+ /* We are in restart mode and we crossed the compare channel 1
+ * value. We need to reset the counter to 0.
*/
- count = imx_gpt_update_count(s);
+ count = s->cnt = s->next_timeout = 0;
+ } else if (count == GPT_TIMER_MAX) {
+ /* We reached GPT_TIMER_MAX so we need to rollover */
+ count = s->cnt = s->next_timeout = 0;
}
- } else {
- /* not a timer event, then just update the count */
-
- count = imx_gpt_update_count(s);
}
/* now, find the next timeout related to count */
@@ -298,9 +216,8 @@ static uint64_t imx_gpt_read(void *opaque, hwaddr offset, unsigned size)
{
IMXGPTState *s = IMX_GPT(opaque);
uint32_t reg_value = 0;
- uint32_t reg = offset >> 2;
- switch (reg) {
+ switch (offset >> 2) {
case 0: /* Control Register */
reg_value = s->cr;
break;
@@ -330,12 +247,14 @@ static uint64_t imx_gpt_read(void *opaque, hwaddr offset, unsigned size)
break;
case 7: /* input Capture Register 1 */
- qemu_log_mask(LOG_UNIMP, "icr1 feature is not implemented\n");
+ qemu_log_mask(LOG_UNIMP, "[%s]%s: icr1 feature is not implemented\n",
+ TYPE_IMX_GPT, __func__);
reg_value = s->icr1;
break;
case 8: /* input Capture Register 2 */
- qemu_log_mask(LOG_UNIMP, "icr2 feature is not implemented\n");
+ qemu_log_mask(LOG_UNIMP, "[%s]%s: icr2 feature is not implemented\n",
+ TYPE_IMX_GPT, __func__);
reg_value = s->icr2;
break;
@@ -345,11 +264,12 @@ static uint64_t imx_gpt_read(void *opaque, hwaddr offset, unsigned size)
break;
default:
- IPRINTF("Bad offset %x\n", reg);
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_GPT, __func__, offset);
break;
}
- DPRINTF("(%s) = 0x%08x\n", imx_gpt_reg_name(reg), reg_value);
+ DPRINTF("(%s) = 0x%08x\n", imx_gpt_reg_name(offset >> 2), reg_value);
return reg_value;
}
@@ -396,12 +316,11 @@ static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value,
{
IMXGPTState *s = IMX_GPT(opaque);
uint32_t oldreg;
- uint32_t reg = offset >> 2;
- DPRINTF("(%s, value = 0x%08x)\n", imx_gpt_reg_name(reg),
+ DPRINTF("(%s, value = 0x%08x)\n", imx_gpt_reg_name(offset >> 2),
(uint32_t)value);
- switch (reg) {
+ switch (offset >> 2) {
case 0:
oldreg = s->cr;
s->cr = value & ~0x7c14;
@@ -477,7 +396,8 @@ static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value,
break;
default:
- IPRINTF("Bad offset %x\n", reg);
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_GPT, __func__, offset);
break;
}
}
@@ -522,16 +442,6 @@ static void imx_gpt_realize(DeviceState *dev, Error **errp)
s->timer = ptimer_init(bh);
}
-void imx_timerg_create(const hwaddr addr, qemu_irq irq, DeviceState *ccm)
-{
- IMXGPTState *pp;
- DeviceState *dev;
-
- dev = sysbus_create_simple(TYPE_IMX_GPT, addr, irq);
- pp = IMX_GPT(dev);
- pp->ccm = ccm;
-}
-
static void imx_gpt_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
diff --git a/qemu/hw/timer/lm32_timer.c b/qemu/hw/timer/lm32_timer.c
index d2ab1e74b..3198355aa 100644
--- a/qemu/hw/timer/lm32_timer.c
+++ b/qemu/hw/timer/lm32_timer.c
@@ -21,6 +21,7 @@
* http://www.latticesemi.com/documents/mico32timer.pdf
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "trace.h"
diff --git a/qemu/hw/timer/m48t59.c b/qemu/hw/timer/m48t59.c
index 8ab683dda..e46ca8839 100644
--- a/qemu/hw/timer/m48t59.c
+++ b/qemu/hw/timer/m48t59.c
@@ -22,13 +22,16 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/timer/m48t59.h"
+#include "qapi/error.h"
#include "qemu/timer.h"
#include "sysemu/sysemu.h"
#include "hw/sysbus.h"
#include "hw/isa/isa.h"
#include "exec/address-spaces.h"
+#include "qemu/bcd.h"
//#define DEBUG_NVRAM
@@ -590,10 +593,8 @@ static void nvram_writel (void *opaque, hwaddr addr, uint32_t value)
static uint32_t nvram_readb (void *opaque, hwaddr addr)
{
M48t59State *NVRAM = opaque;
- uint32_t retval;
- retval = m48t59_read(NVRAM, addr);
- return retval;
+ return m48t59_read(NVRAM, addr);
}
static uint32_t nvram_readw (void *opaque, hwaddr addr)
diff --git a/qemu/hw/timer/mc146818rtc.c b/qemu/hw/timer/mc146818rtc.c
index a9f0efd5e..2ac0fd3e4 100644
--- a/qemu/hw/timer/mc146818rtc.c
+++ b/qemu/hw/timer/mc146818rtc.c
@@ -21,6 +21,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "config-target.h"
+#include "qemu/cutils.h"
+#include "qemu/bcd.h"
#include "hw/hw.h"
#include "qemu/timer.h"
#include "sysemu/sysemu.h"
@@ -105,8 +109,8 @@ static uint64_t get_guest_rtc_ns(RTCState *s)
uint64_t guest_rtc;
uint64_t guest_clock = qemu_clock_get_ns(rtc_clock);
- guest_rtc = s->base_rtc * NANOSECONDS_PER_SECOND
- + guest_clock - s->last_update + s->offset;
+ guest_rtc = s->base_rtc * NANOSECONDS_PER_SECOND +
+ guest_clock - s->last_update + s->offset;
return guest_rtc;
}
@@ -119,7 +123,7 @@ static void rtc_coalesced_timer_update(RTCState *s)
/* divide each RTC interval to 2 - 8 smaller intervals */
int c = MIN(s->irq_coalesced, 7) + 1;
int64_t next_clock = qemu_clock_get_ns(rtc_clock) +
- muldiv64(s->period / c, get_ticks_per_sec(), RTC_CLOCK_RATE);
+ muldiv64(s->period / c, NANOSECONDS_PER_SECOND, RTC_CLOCK_RATE);
timer_mod(s->coalesced_timer, next_clock);
}
}
@@ -165,10 +169,12 @@ static void periodic_timer_update(RTCState *s, int64_t current_time)
s->period = period;
#endif
/* compute 32 khz clock */
- cur_clock = muldiv64(current_time, RTC_CLOCK_RATE, get_ticks_per_sec());
+ cur_clock =
+ muldiv64(current_time, RTC_CLOCK_RATE, NANOSECONDS_PER_SECOND);
+
next_irq_clock = (cur_clock & ~(period - 1)) + period;
- s->next_periodic_time =
- muldiv64(next_irq_clock, get_ticks_per_sec(), RTC_CLOCK_RATE) + 1;
+ s->next_periodic_time = muldiv64(next_irq_clock, NANOSECONDS_PER_SECOND,
+ RTC_CLOCK_RATE) + 1;
timer_mod(s->periodic_timer, s->next_periodic_time);
} else {
#ifdef TARGET_I386
diff --git a/qemu/hw/timer/milkymist-sysctl.c b/qemu/hw/timer/milkymist-sysctl.c
index 30535a4e3..5f2948037 100644
--- a/qemu/hw/timer/milkymist-sysctl.c
+++ b/qemu/hw/timer/milkymist-sysctl.c
@@ -21,6 +21,7 @@
* http://www.milkymist.org/socdoc/sysctl.pdf
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "sysemu/sysemu.h"
diff --git a/qemu/hw/timer/omap_gptimer.c b/qemu/hw/timer/omap_gptimer.c
index b8c8c0137..3a4386304 100644
--- a/qemu/hw/timer/omap_gptimer.c
+++ b/qemu/hw/timer/omap_gptimer.c
@@ -17,6 +17,7 @@
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "qemu/timer.h"
#include "hw/arm/omap.h"
@@ -401,7 +402,7 @@ static void omap_gp_timer_write(void *opaque, hwaddr addr,
if (s->trigger == gpt_trigger_none)
omap_gp_timer_out(s, s->scpwm);
/* TODO: make sure this doesn't overflow 32-bits */
- s->ticks_per_sec = get_ticks_per_sec() << (s->pre ? s->ptv + 1 : 0);
+ s->ticks_per_sec = NANOSECONDS_PER_SECOND << (s->pre ? s->ptv + 1 : 0);
omap_gp_timer_update(s);
break;
@@ -468,8 +469,7 @@ static const MemoryRegionOps omap_gp_timer_ops = {
struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta,
qemu_irq irq, omap_clk fclk, omap_clk iclk)
{
- struct omap_gp_timer_s *s = (struct omap_gp_timer_s *)
- g_malloc0(sizeof(struct omap_gp_timer_s));
+ struct omap_gp_timer_s *s = g_new0(struct omap_gp_timer_s, 1);
s->ta = ta;
s->irq = irq;
diff --git a/qemu/hw/timer/omap_synctimer.c b/qemu/hw/timer/omap_synctimer.c
index 8e50488d1..9ee651979 100644
--- a/qemu/hw/timer/omap_synctimer.c
+++ b/qemu/hw/timer/omap_synctimer.c
@@ -17,6 +17,7 @@
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "qemu/timer.h"
#include "hw/arm/omap.h"
@@ -28,7 +29,8 @@ struct omap_synctimer_s {
/* 32-kHz Sync Timer of the OMAP2 */
static uint32_t omap_synctimer_read(struct omap_synctimer_s *s) {
- return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 0x8000, get_ticks_per_sec());
+ return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 0x8000,
+ NANOSECONDS_PER_SECOND);
}
void omap_synctimer_reset(struct omap_synctimer_s *s)
diff --git a/qemu/hw/timer/pl031.c b/qemu/hw/timer/pl031.c
index 34d9b44e7..38e0cb5ad 100644
--- a/qemu/hw/timer/pl031.c
+++ b/qemu/hw/timer/pl031.c
@@ -11,9 +11,11 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "qemu/timer.h"
#include "sysemu/sysemu.h"
+#include "qemu/cutils.h"
//#define DEBUG_PL031
@@ -79,7 +81,7 @@ static void pl031_interrupt(void * opaque)
static uint32_t pl031_get_count(PL031State *s)
{
int64_t now = qemu_clock_get_ns(rtc_clock);
- return s->tick_offset + now / get_ticks_per_sec();
+ return s->tick_offset + now / NANOSECONDS_PER_SECOND;
}
static void pl031_set_alarm(PL031State *s)
@@ -95,7 +97,7 @@ static void pl031_set_alarm(PL031State *s)
pl031_interrupt(s);
} else {
int64_t now = qemu_clock_get_ns(rtc_clock);
- timer_mod(s->timer, now + (int64_t)ticks * get_ticks_per_sec());
+ timer_mod(s->timer, now + (int64_t)ticks * NANOSECONDS_PER_SECOND);
}
}
@@ -191,21 +193,21 @@ static const MemoryRegionOps pl031_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static int pl031_init(SysBusDevice *dev)
+static void pl031_init(Object *obj)
{
- PL031State *s = PL031(dev);
+ PL031State *s = PL031(obj);
+ SysBusDevice *dev = SYS_BUS_DEVICE(obj);
struct tm tm;
- memory_region_init_io(&s->iomem, OBJECT(s), &pl031_ops, s, "pl031", 0x1000);
+ memory_region_init_io(&s->iomem, obj, &pl031_ops, s, "pl031", 0x1000);
sysbus_init_mmio(dev, &s->iomem);
sysbus_init_irq(dev, &s->irq);
qemu_get_timedate(&tm, 0);
s->tick_offset = mktimegm(&tm) -
- qemu_clock_get_ns(rtc_clock) / get_ticks_per_sec();
+ qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND;
s->timer = timer_new_ns(rtc_clock, pl031_interrupt, s);
- return 0;
}
static void pl031_pre_save(void *opaque)
@@ -215,7 +217,7 @@ static void pl031_pre_save(void *opaque)
/* tick_offset is base_time - rtc_clock base time. Instead, we want to
* store the base time relative to the QEMU_CLOCK_VIRTUAL for backwards-compatibility. */
int64_t delta = qemu_clock_get_ns(rtc_clock) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- s->tick_offset_vmstate = s->tick_offset + delta / get_ticks_per_sec();
+ s->tick_offset_vmstate = s->tick_offset + delta / NANOSECONDS_PER_SECOND;
}
static int pl031_post_load(void *opaque, int version_id)
@@ -223,7 +225,7 @@ static int pl031_post_load(void *opaque, int version_id)
PL031State *s = opaque;
int64_t delta = qemu_clock_get_ns(rtc_clock) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- s->tick_offset = s->tick_offset_vmstate - delta / get_ticks_per_sec();
+ s->tick_offset = s->tick_offset_vmstate - delta / NANOSECONDS_PER_SECOND;
pl031_set_alarm(s);
return 0;
}
@@ -248,9 +250,7 @@ static const VMStateDescription vmstate_pl031 = {
static void pl031_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = pl031_init;
dc->vmsd = &vmstate_pl031;
}
@@ -258,6 +258,7 @@ static const TypeInfo pl031_info = {
.name = TYPE_PL031,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(PL031State),
+ .instance_init = pl031_init,
.class_init = pl031_class_init,
};
diff --git a/qemu/hw/timer/puv3_ost.c b/qemu/hw/timer/puv3_ost.c
index fa9eefd92..93650b799 100644
--- a/qemu/hw/timer/puv3_ost.c
+++ b/qemu/hw/timer/puv3_ost.c
@@ -8,6 +8,7 @@
* published by the Free Software Foundation, or any later version.
* See the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/ptimer.h"
#include "qemu/main-loop.h"
diff --git a/qemu/hw/timer/pxa2xx_timer.c b/qemu/hw/timer/pxa2xx_timer.c
index 130e9dc34..59002b407 100644
--- a/qemu/hw/timer/pxa2xx_timer.c
+++ b/qemu/hw/timer/pxa2xx_timer.c
@@ -7,6 +7,7 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "qemu/timer.h"
#include "sysemu/sysemu.h"
@@ -118,11 +119,11 @@ static void pxa2xx_timer_update(void *opaque, uint64_t now_qemu)
uint64_t new_qemu;
now_vm = s->clock +
- muldiv64(now_qemu - s->lastload, s->freq, get_ticks_per_sec());
+ muldiv64(now_qemu - s->lastload, s->freq, NANOSECONDS_PER_SECOND);
for (i = 0; i < 4; i ++) {
new_qemu = now_qemu + muldiv64((uint32_t) (s->timer[i].value - now_vm),
- get_ticks_per_sec(), s->freq);
+ NANOSECONDS_PER_SECOND, s->freq);
timer_mod(s->timer[i].qtimer, new_qemu);
}
}
@@ -147,10 +148,10 @@ static void pxa2xx_timer_update4(void *opaque, uint64_t now_qemu, int n)
now_vm = s->tm4[counter].clock + muldiv64(now_qemu -
s->tm4[counter].lastload,
- s->tm4[counter].freq, get_ticks_per_sec());
+ s->tm4[counter].freq, NANOSECONDS_PER_SECOND);
new_qemu = now_qemu + muldiv64((uint32_t) (s->tm4[n].tm.value - now_vm),
- get_ticks_per_sec(), s->tm4[counter].freq);
+ NANOSECONDS_PER_SECOND, s->tm4[counter].freq);
timer_mod(s->tm4[n].tm.qtimer, new_qemu);
}
@@ -189,7 +190,7 @@ static uint64_t pxa2xx_timer_read(void *opaque, hwaddr offset,
return s->tm4[tm].tm.value;
case OSCR:
return s->clock + muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) -
- s->lastload, s->freq, get_ticks_per_sec());
+ s->lastload, s->freq, NANOSECONDS_PER_SECOND);
case OSCR11: tm ++;
/* fall through */
case OSCR10: tm ++;
@@ -213,15 +214,17 @@ static uint64_t pxa2xx_timer_read(void *opaque, hwaddr offset,
s->snapshot = s->tm4[tm - 1].clock + muldiv64(
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) -
s->tm4[tm - 1].lastload,
- s->tm4[tm - 1].freq, get_ticks_per_sec());
+ s->tm4[tm - 1].freq, NANOSECONDS_PER_SECOND);
else
s->snapshot = s->tm4[tm - 1].clock;
}
if (!s->tm4[tm].freq)
return s->tm4[tm].clock;
- return s->tm4[tm].clock + muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) -
- s->tm4[tm].lastload, s->tm4[tm].freq, get_ticks_per_sec());
+ return s->tm4[tm].clock +
+ muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) -
+ s->tm4[tm].lastload, s->tm4[tm].freq,
+ NANOSECONDS_PER_SECOND);
case OIER:
return s->irq_enabled;
case OSSR: /* Status register */
@@ -432,10 +435,10 @@ static int pxa25x_timer_post_load(void *opaque, int version_id)
return 0;
}
-static int pxa2xx_timer_init(SysBusDevice *dev)
+static void pxa2xx_timer_init(Object *obj)
{
- PXA2xxTimerInfo *s = PXA2XX_TIMER(dev);
- int i;
+ PXA2xxTimerInfo *s = PXA2XX_TIMER(obj);
+ SysBusDevice *dev = SYS_BUS_DEVICE(obj);
s->irq_enabled = 0;
s->oldclock = 0;
@@ -443,16 +446,28 @@ static int pxa2xx_timer_init(SysBusDevice *dev)
s->lastload = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
s->reset3 = 0;
+ memory_region_init_io(&s->iomem, obj, &pxa2xx_timer_ops, s,
+ "pxa2xx-timer", 0x00001000);
+ sysbus_init_mmio(dev, &s->iomem);
+}
+
+static void pxa2xx_timer_realize(DeviceState *dev, Error **errp)
+{
+ PXA2xxTimerInfo *s = PXA2XX_TIMER(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ int i;
+
for (i = 0; i < 4; i ++) {
s->timer[i].value = 0;
- sysbus_init_irq(dev, &s->timer[i].irq);
+ sysbus_init_irq(sbd, &s->timer[i].irq);
s->timer[i].info = s;
s->timer[i].num = i;
s->timer[i].qtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
- pxa2xx_timer_tick, &s->timer[i]);
+ pxa2xx_timer_tick, &s->timer[i]);
}
+
if (s->flags & (1 << PXA2XX_TIMER_HAVE_TM4)) {
- sysbus_init_irq(dev, &s->irq4);
+ sysbus_init_irq(sbd, &s->irq4);
for (i = 0; i < 8; i ++) {
s->tm4[i].tm.value = 0;
@@ -461,15 +476,9 @@ static int pxa2xx_timer_init(SysBusDevice *dev)
s->tm4[i].freq = 0;
s->tm4[i].control = 0x0;
s->tm4[i].tm.qtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
- pxa2xx_timer_tick4, &s->tm4[i]);
+ pxa2xx_timer_tick4, &s->tm4[i]);
}
}
-
- memory_region_init_io(&s->iomem, OBJECT(s), &pxa2xx_timer_ops, s,
- "pxa2xx-timer", 0x00001000);
- sysbus_init_mmio(dev, &s->iomem);
-
- return 0;
}
static const VMStateDescription vmstate_pxa2xx_timer0_regs = {
@@ -572,9 +581,8 @@ static const TypeInfo pxa27x_timer_dev_info = {
static void pxa2xx_timer_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
- SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(oc);
- sdc->init = pxa2xx_timer_init;
+ dc->realize = pxa2xx_timer_realize;
dc->vmsd = &vmstate_pxa2xx_timer_regs;
}
@@ -582,6 +590,7 @@ static const TypeInfo pxa2xx_timer_type_info = {
.name = TYPE_PXA2XX_TIMER,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(PXA2xxTimerInfo),
+ .instance_init = pxa2xx_timer_init,
.abstract = true,
.class_init = pxa2xx_timer_class_init,
};
diff --git a/qemu/hw/timer/sh_timer.c b/qemu/hw/timer/sh_timer.c
index 07f0670b5..255b2fc91 100644
--- a/qemu/hw/timer/sh_timer.c
+++ b/qemu/hw/timer/sh_timer.c
@@ -8,6 +8,7 @@
* This code is licensed under the GPL.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sh4/sh.h"
#include "qemu/timer.h"
diff --git a/qemu/hw/timer/slavio_timer.c b/qemu/hw/timer/slavio_timer.c
index 45d97e667..fb3e08bed 100644
--- a/qemu/hw/timer/slavio_timer.c
+++ b/qemu/hw/timer/slavio_timer.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sparc/sun4m.h"
#include "qemu/timer.h"
#include "hw/ptimer.h"
diff --git a/qemu/hw/timer/stm32f2xx_timer.c b/qemu/hw/timer/stm32f2xx_timer.c
index ecadf9df8..55dacbbe3 100644
--- a/qemu/hw/timer/stm32f2xx_timer.c
+++ b/qemu/hw/timer/stm32f2xx_timer.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/timer/stm32f2xx_timer.h"
#ifndef STM_TIMER_ERR_DEBUG
diff --git a/qemu/hw/timer/twl92230.c b/qemu/hw/timer/twl92230.c
index 7ded4ba2a..7ba4e9a7c 100644
--- a/qemu/hw/timer/twl92230.c
+++ b/qemu/hw/timer/twl92230.c
@@ -19,11 +19,13 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "qemu/timer.h"
#include "hw/i2c/i2c.h"
#include "sysemu/sysemu.h"
#include "ui/console.h"
+#include "qemu/bcd.h"
#define VERBOSE 1
diff --git a/qemu/hw/timer/xilinx_timer.c b/qemu/hw/timer/xilinx_timer.c
index cdb335517..2ea970dc9 100644
--- a/qemu/hw/timer/xilinx_timer.c
+++ b/qemu/hw/timer/xilinx_timer.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/ptimer.h"
#include "qemu/log.h"
diff --git a/qemu/hw/tpm/tpm_passthrough.c b/qemu/hw/tpm/tpm_passthrough.c
index 79a8f98af..e88c0d20b 100644
--- a/qemu/hw/tpm/tpm_passthrough.c
+++ b/qemu/hw/tpm/tpm_passthrough.c
@@ -22,10 +22,8 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>
*/
-#include <dirent.h>
-
+#include "qemu/osdep.h"
#include "qemu-common.h"
-#include "qapi/error.h"
#include "qemu/error-report.h"
#include "qemu/sockets.h"
#include "sysemu/tpm_backend.h"
@@ -85,12 +83,37 @@ static void tpm_passthrough_cancel_cmd(TPMBackend *tb);
static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t len)
{
- return send_all(fd, buf, len);
+ int ret, remain;
+
+ remain = len;
+ while (remain > 0) {
+ ret = write(fd, buf, remain);
+ if (ret < 0) {
+ if (errno != EINTR && errno != EAGAIN) {
+ return -1;
+ }
+ } else if (ret == 0) {
+ break;
+ } else {
+ buf += ret;
+ remain -= ret;
+ }
+ }
+ return len - remain;
}
static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
{
- return recv_all(fd, buf, len, true);
+ int ret;
+ reread:
+ ret = read(fd, buf, len);
+ if (ret < 0) {
+ if (errno != EINTR && errno != EAGAIN) {
+ return -1;
+ }
+ goto reread;
+ }
+ return ret;
}
static uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf)
diff --git a/qemu/hw/tpm/tpm_tis.c b/qemu/hw/tpm/tpm_tis.c
index 0806b5f82..381e7266e 100644
--- a/qemu/hw/tpm/tpm_tis.c
+++ b/qemu/hw/tpm/tpm_tis.c
@@ -22,6 +22,7 @@
* TPM Profile (PTP) Specification, Familiy 2.0, Revision 00.43
*/
+#include "qemu/osdep.h"
#include "sysemu/tpm_backend.h"
#include "tpm_int.h"
#include "sysemu/block-backend.h"
@@ -30,6 +31,7 @@
#include "hw/i386/pc.h"
#include "hw/pci/pci_ids.h"
#include "tpm_tis.h"
+#include "qapi/error.h"
#include "qemu-common.h"
#include "qemu/main-loop.h"
#include "sysemu/tpm_backend.h"
@@ -141,7 +143,7 @@
#define TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3 \
(TPM_TIS_IFACE_ID_INTERFACE_TIS1_3 | \
- (~0 << 4)/* all of it is don't care */)
+ (~0u << 4)/* all of it is don't care */)
/* if backend was a TPM 2.0: */
#define TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0 \
@@ -1051,7 +1053,7 @@ static void tpm_tis_realizefn(DeviceState *dev, Error **errp)
if (tis->irq_num > 15) {
error_setg(errp, "tpm_tis: IRQ %d for TPM TIS is outside valid range "
- "of 0 to 15.\n", tis->irq_num);
+ "of 0 to 15", tis->irq_num);
return;
}
diff --git a/qemu/hw/tpm/tpm_util.c b/qemu/hw/tpm/tpm_util.c
index 4ace5852e..7b3542972 100644
--- a/qemu/hw/tpm/tpm_util.c
+++ b/qemu/hw/tpm/tpm_util.c
@@ -19,6 +19,7 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>
*/
+#include "qemu/osdep.h"
#include "tpm_util.h"
#include "tpm_int.h"
diff --git a/qemu/hw/tricore/tricore_testboard.c b/qemu/hw/tricore/tricore_testboard.c
index a059a20a3..8d3520f5b 100644
--- a/qemu/hw/tricore/tricore_testboard.c
+++ b/qemu/hw/tricore/tricore_testboard.c
@@ -18,6 +18,10 @@
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "hw/devices.h"
#include "net/net.h"
@@ -44,7 +48,7 @@ static void tricore_load_kernel(CPUTriCoreState *env)
kernel_size = load_elf(tricoretb_binfo.kernel_filename, NULL,
NULL, (uint64_t *)&entry, NULL,
NULL, 0,
- ELF_MACHINE, 1);
+ EM_TRICORE, 1, 0);
if (kernel_size <= 0) {
error_report("qemu: no kernel file '%s'",
tricoretb_binfo.kernel_filename);
@@ -76,17 +80,23 @@ static void tricore_testboard_init(MachineState *machine, int board_id)
exit(1);
}
env = &cpu->env;
- memory_region_init_ram(ext_cram, NULL, "powerlink_ext_c.ram", 2*1024*1024, &error_abort);
+ memory_region_init_ram(ext_cram, NULL, "powerlink_ext_c.ram", 2*1024*1024,
+ &error_fatal);
vmstate_register_ram_global(ext_cram);
- memory_region_init_ram(ext_dram, NULL, "powerlink_ext_d.ram", 4*1024*1024, &error_abort);
+ memory_region_init_ram(ext_dram, NULL, "powerlink_ext_d.ram", 4*1024*1024,
+ &error_fatal);
vmstate_register_ram_global(ext_dram);
- memory_region_init_ram(int_cram, NULL, "powerlink_int_c.ram", 48*1024, &error_abort);
+ memory_region_init_ram(int_cram, NULL, "powerlink_int_c.ram", 48*1024,
+ &error_fatal);
vmstate_register_ram_global(int_cram);
- memory_region_init_ram(int_dram, NULL, "powerlink_int_d.ram", 48*1024, &error_abort);
+ memory_region_init_ram(int_dram, NULL, "powerlink_int_d.ram", 48*1024,
+ &error_fatal);
vmstate_register_ram_global(int_dram);
- memory_region_init_ram(pcp_data, NULL, "powerlink_pcp_data.ram", 16*1024, &error_abort);
+ memory_region_init_ram(pcp_data, NULL, "powerlink_pcp_data.ram", 16*1024,
+ &error_fatal);
vmstate_register_ram_global(pcp_data);
- memory_region_init_ram(pcp_text, NULL, "powerlink_pcp_text.ram", 32*1024, &error_abort);
+ memory_region_init_ram(pcp_text, NULL, "powerlink_pcp_text.ram", 32*1024,
+ &error_fatal);
vmstate_register_ram_global(pcp_text);
memory_region_add_subregion(sysmem, 0x80000000, ext_cram);
@@ -109,16 +119,11 @@ static void tricoreboard_init(MachineState *machine)
tricore_testboard_init(machine, 0x183);
}
-static QEMUMachine ttb_machine = {
- .name = "tricore_testboard",
- .desc = "a minimal TriCore board",
- .init = tricoreboard_init,
- .is_default = 0,
-};
-
-static void tricore_testboard_machine_init(void)
+static void ttb_machine_init(MachineClass *mc)
{
- qemu_register_machine(&ttb_machine);
+ mc->desc = "a minimal TriCore board";
+ mc->init = tricoreboard_init;
+ mc->is_default = 0;
}
-machine_init(tricore_testboard_machine_init);
+DEFINE_MACHINE("tricore_testboard", ttb_machine_init)
diff --git a/qemu/hw/unicore32/puv3.c b/qemu/hw/unicore32/puv3.c
index 703e29d6d..31cd17101 100644
--- a/qemu/hw/unicore32/puv3.c
+++ b/qemu/hw/unicore32/puv3.c
@@ -9,6 +9,10 @@
* See the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "qemu-common.h"
#include "ui/console.h"
#include "elf.h"
@@ -17,6 +21,7 @@
#include "hw/boards.h"
#include "hw/loader.h"
#include "hw/i386/pc.h"
+#include "qemu/error-report.h"
#include "sysemu/qtest.h"
#undef DEBUG_PUV3
@@ -75,7 +80,7 @@ static void puv3_board_init(CPUUniCore32State *env, ram_addr_t ram_size)
/* SDRAM at address zero. */
memory_region_init_ram(ram_memory, NULL, "puv3.ram", ram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(ram_memory);
memory_region_add_subregion(get_system_memory(), 0, ram_memory);
}
@@ -95,7 +100,8 @@ static void puv3_load_kernel(const char *kernel_filename)
size = load_image_targphys(kernel_filename, KERNEL_LOAD_ADDR,
KERNEL_MAX_SIZE);
if (size < 0) {
- hw_error("Load kernel error: '%s'\n", kernel_filename);
+ error_report("Load kernel error: '%s'", kernel_filename);
+ exit(1);
}
/* cheat curses that we have a graphic console, only under ocd console */
@@ -112,7 +118,8 @@ static void puv3_init(MachineState *machine)
UniCore32CPU *cpu;
if (initrd_filename) {
- hw_error("Please use kernel built-in initramdisk.\n");
+ error_report("Please use kernel built-in initramdisk");
+ exit(1);
}
if (!cpu_model) {
@@ -121,7 +128,8 @@ static void puv3_init(MachineState *machine)
cpu = uc32_cpu_init(cpu_model);
if (!cpu) {
- hw_error("Unable to find CPU definition\n");
+ error_report("Unable to find CPU definition");
+ exit(1);
}
env = &cpu->env;
@@ -130,16 +138,11 @@ static void puv3_init(MachineState *machine)
puv3_load_kernel(kernel_filename);
}
-static QEMUMachine puv3_machine = {
- .name = "puv3",
- .desc = "PKUnity Version-3 based on UniCore32",
- .init = puv3_init,
- .is_default = 1,
-};
-
-static void puv3_machine_init(void)
+static void puv3_machine_init(MachineClass *mc)
{
- qemu_register_machine(&puv3_machine);
+ mc->desc = "PKUnity Version-3 based on UniCore32";
+ mc->init = puv3_init;
+ mc->is_default = 1;
}
-machine_init(puv3_machine_init)
+DEFINE_MACHINE("puv3", puv3_machine_init)
diff --git a/qemu/hw/usb/Makefile.objs b/qemu/hw/usb/Makefile.objs
index 7443e386b..2717027d3 100644
--- a/qemu/hw/usb/Makefile.objs
+++ b/qemu/hw/usb/Makefile.objs
@@ -10,6 +10,8 @@ common-obj-$(CONFIG_USB_EHCI_SYSBUS) += hcd-ehci-sysbus.o
common-obj-$(CONFIG_USB_XHCI) += hcd-xhci.o
common-obj-$(CONFIG_USB_MUSB) += hcd-musb.o
+obj-$(CONFIG_TUSB6010) += tusb6010.o
+
# emulated usb devices
common-obj-$(CONFIG_USB) += dev-hub.o
common-obj-$(CONFIG_USB) += dev-hid.o
@@ -23,9 +25,8 @@ common-obj-$(CONFIG_USB_BLUETOOTH) += dev-bluetooth.o
ifeq ($(CONFIG_USB_SMARTCARD),y)
common-obj-y += dev-smartcard-reader.o
-common-obj-y += ccid-card-passthru.o
-common-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o
-ccid-card-emulated.o-cflags := -I$(SRC_PATH)/libcacard
+common-obj-$(CONFIG_SMARTCARD) += ccid-card-passthru.o
+common-obj-$(CONFIG_SMARTCARD) += ccid-card-emulated.o
endif
ifeq ($(CONFIG_POSIX),y)
diff --git a/qemu/hw/usb/bus.c b/qemu/hw/usb/bus.c
index 5f39e1e3a..16c3461d9 100644
--- a/qemu/hw/usb/bus.c
+++ b/qemu/hw/usb/bus.c
@@ -1,10 +1,13 @@
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/usb.h"
#include "hw/qdev.h"
+#include "qapi/error.h"
#include "qemu/error-report.h"
#include "sysemu/sysemu.h"
#include "monitor/monitor.h"
#include "trace.h"
+#include "qemu/cutils.h"
static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent);
@@ -329,9 +332,9 @@ static USBDevice *usb_try_create_simple(USBBus *bus, const char *name,
}
object_property_set_bool(OBJECT(dev), true, "realized", &err);
if (err) {
- error_setg(errp, "Failed to initialize USB device '%s': %s",
- name, error_get_pretty(err));
- error_free(err);
+ error_propagate(errp, err);
+ error_prepend(errp, "Failed to initialize USB device '%s': ",
+ name);
object_unparent(OBJECT(dev));
return NULL;
}
@@ -655,9 +658,12 @@ void hmp_info_usb(Monitor *mon, const QDict *qdict)
dev = port->dev;
if (!dev)
continue;
- monitor_printf(mon, " Device %d.%d, Port %s, Speed %s Mb/s, Product %s\n",
- bus->busnr, dev->addr, port->path, usb_speed(dev->speed),
- dev->product_desc);
+ monitor_printf(mon, " Device %d.%d, Port %s, Speed %s Mb/s, "
+ "Product %s%s%s\n",
+ bus->busnr, dev->addr, port->path,
+ usb_speed(dev->speed), dev->product_desc,
+ dev->qdev.id ? ", ID: " : "",
+ dev->qdev.id ?: "");
}
}
}
@@ -722,9 +728,8 @@ USBDevice *usbdevice_create(const char *cmdline)
}
object_property_set_bool(OBJECT(dev), true, "realized", &err);
if (err) {
- error_report("Failed to initialize USB device '%s': %s",
- f->name, error_get_pretty(err));
- error_free(err);
+ error_reportf_err(err, "Failed to initialize USB device '%s': ",
+ f->name);
object_unparent(OBJECT(dev));
return NULL;
}
diff --git a/qemu/hw/usb/ccid-card-emulated.c b/qemu/hw/usb/ccid-card-emulated.c
index 72329ed7d..3213f9f8a 100644
--- a/qemu/hw/usb/ccid-card-emulated.c
+++ b/qemu/hw/usb/ccid-card-emulated.c
@@ -26,6 +26,7 @@
* the db parameter.
*/
+#include "qemu/osdep.h"
#include <eventt.h>
#include <vevent.h>
#include <vreader.h>
@@ -42,7 +43,10 @@ do {\
} \
} while (0)
-#define EMULATED_DEV_NAME "ccid-card-emulated"
+
+#define TYPE_EMULATED_CCID "ccid-card-emulated"
+#define EMULATED_CCID_CARD(obj) \
+ OBJECT_CHECK(EmulatedState, (obj), TYPE_EMULATED_CCID)
#define BACKEND_NSS_EMULATED_NAME "nss-emulated"
#define BACKEND_CERTIFICATES_NAME "certificates"
@@ -133,7 +137,7 @@ struct EmulatedState {
static void emulated_apdu_from_guest(CCIDCardState *base,
const uint8_t *apdu, uint32_t len)
{
- EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
+ EmulatedState *card = EMULATED_CCID_CARD(base);
EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent) + len);
assert(event);
@@ -150,7 +154,7 @@ static void emulated_apdu_from_guest(CCIDCardState *base,
static const uint8_t *emulated_get_atr(CCIDCardState *base, uint32_t *len)
{
- EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
+ EmulatedState *card = EMULATED_CCID_CARD(base);
*len = card->atr_length;
return card->atr;
@@ -166,7 +170,7 @@ static void emulated_push_event(EmulatedState *card, EmulEvent *event)
static void emulated_push_type(EmulatedState *card, uint32_t type)
{
- EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent));
+ EmulEvent *event = g_new(EmulEvent, 1);
assert(event);
event->p.gen.type = type;
@@ -175,7 +179,7 @@ static void emulated_push_type(EmulatedState *card, uint32_t type)
static void emulated_push_error(EmulatedState *card, uint64_t code)
{
- EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent));
+ EmulEvent *event = g_new(EmulEvent, 1);
assert(event);
event->p.error.type = EMUL_ERROR;
@@ -403,7 +407,7 @@ static int init_event_notifier(EmulatedState *card)
DPRINTF(card, 2, "event notifier creation failed\n");
return -1;
}
- event_notifier_set_handler(&card->notifier, card_event_handler);
+ event_notifier_set_handler(&card->notifier, false, card_event_handler);
return 0;
}
@@ -478,7 +482,7 @@ static uint32_t parse_enumeration(char *str,
static int emulated_initfn(CCIDCardState *base)
{
- EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
+ EmulatedState *card = EMULATED_CCID_CARD(base);
VCardEmulError ret;
const EnumTable *ptable;
@@ -514,26 +518,26 @@ static int emulated_initfn(CCIDCardState *base)
ret = emulated_initialize_vcard_from_certificates(card);
} else {
printf("%s: you must provide all three certs for"
- " certificates backend\n", EMULATED_DEV_NAME);
+ " certificates backend\n", TYPE_EMULATED_CCID);
return -1;
}
} else {
if (card->backend != BACKEND_NSS_EMULATED) {
printf("%s: bad backend specified. The options are:\n%s (default),"
- " %s.\n", EMULATED_DEV_NAME, BACKEND_NSS_EMULATED_NAME,
+ " %s.\n", TYPE_EMULATED_CCID, BACKEND_NSS_EMULATED_NAME,
BACKEND_CERTIFICATES_NAME);
return -1;
}
if (card->cert1 != NULL || card->cert2 != NULL || card->cert3 != NULL) {
printf("%s: unexpected cert parameters to nss emulated backend\n",
- EMULATED_DEV_NAME);
+ TYPE_EMULATED_CCID);
return -1;
}
/* default to mirroring the local hardware readers */
ret = wrap_vcard_emul_init(NULL);
}
if (ret != VCARD_EMUL_OK) {
- printf("%s: failed to initialize vcard\n", EMULATED_DEV_NAME);
+ printf("%s: failed to initialize vcard\n", TYPE_EMULATED_CCID);
return -1;
}
qemu_thread_create(&card->event_thread_id, "ccid/event", event_thread,
@@ -545,7 +549,7 @@ static int emulated_initfn(CCIDCardState *base)
static int emulated_exitfn(CCIDCardState *base)
{
- EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
+ EmulatedState *card = EMULATED_CCID_CARD(base);
VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL);
vevent_queue_vevent(vevent); /* stop vevent thread */
@@ -588,7 +592,7 @@ static void emulated_class_initfn(ObjectClass *klass, void *data)
}
static const TypeInfo emulated_card_info = {
- .name = EMULATED_DEV_NAME,
+ .name = TYPE_EMULATED_CCID,
.parent = TYPE_CCID_CARD,
.instance_size = sizeof(EmulatedState),
.class_init = emulated_class_initfn,
diff --git a/qemu/hw/usb/ccid-card-passthru.c b/qemu/hw/usb/ccid-card-passthru.c
index 85a4fc3e5..c0e90e501 100644
--- a/qemu/hw/usb/ccid-card-passthru.c
+++ b/qemu/hw/usb/ccid-card-passthru.c
@@ -8,11 +8,12 @@
* See the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
#include "sysemu/char.h"
#include "qemu/error-report.h"
#include "qemu/sockets.h"
#include "ccid.h"
-#include "libcacard/vscard_common.h"
+#include "cacard/vscard_common.h"
#define DPRINTF(card, lvl, fmt, ...) \
do { \
@@ -38,8 +39,6 @@ static const uint8_t DEFAULT_ATR[] = {
0x13, 0x08
};
-
-#define PASSTHRU_DEV_NAME "ccid-card-passthru"
#define VSCARD_IN_SIZE 65536
/* maximum size of ATR - from 7816-3 */
@@ -58,6 +57,10 @@ struct PassthruState {
uint8_t debug;
};
+#define TYPE_CCID_PASSTHRU "ccid-card-passthru"
+#define PASSTHRU_CCID_CARD(obj) \
+ OBJECT_CHECK(PassthruState, (obj), TYPE_CCID_PASSTHRU)
+
/*
* VSCard protocol over chardev
* This code should not depend on the card type.
@@ -316,7 +319,7 @@ static void ccid_card_vscard_event(void *opaque, int event)
static void passthru_apdu_from_guest(
CCIDCardState *base, const uint8_t *apdu, uint32_t len)
{
- PassthruState *card = DO_UPCAST(PassthruState, base, base);
+ PassthruState *card = PASSTHRU_CCID_CARD(base);
if (!card->cs) {
printf("ccid-passthru: no chardev, discarding apdu length %d\n", len);
@@ -327,7 +330,7 @@ static void passthru_apdu_from_guest(
static const uint8_t *passthru_get_atr(CCIDCardState *base, uint32_t *len)
{
- PassthruState *card = DO_UPCAST(PassthruState, base, base);
+ PassthruState *card = PASSTHRU_CCID_CARD(base);
*len = card->atr_length;
return card->atr;
@@ -335,7 +338,7 @@ static const uint8_t *passthru_get_atr(CCIDCardState *base, uint32_t *len)
static int passthru_initfn(CCIDCardState *base)
{
- PassthruState *card = DO_UPCAST(PassthruState, base, base);
+ PassthruState *card = PASSTHRU_CCID_CARD(base);
card->vscard_in_pos = 0;
card->vscard_in_hdr = 0;
@@ -399,7 +402,7 @@ static void passthru_class_initfn(ObjectClass *klass, void *data)
}
static const TypeInfo passthru_card_info = {
- .name = PASSTHRU_DEV_NAME,
+ .name = TYPE_CCID_PASSTHRU,
.parent = TYPE_CCID_CARD,
.instance_size = sizeof(PassthruState),
.class_init = passthru_class_initfn,
diff --git a/qemu/hw/usb/combined-packet.c b/qemu/hw/usb/combined-packet.c
index ad77705f8..48cac87f6 100644
--- a/qemu/hw/usb/combined-packet.c
+++ b/qemu/hw/usb/combined-packet.c
@@ -19,6 +19,7 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "hw/usb.h"
#include "qemu/iov.h"
diff --git a/qemu/hw/usb/core.c b/qemu/hw/usb/core.c
index d0025db60..45fa00c51 100644
--- a/qemu/hw/usb/core.c
+++ b/qemu/hw/usb/core.c
@@ -23,6 +23,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "hw/usb.h"
#include "qemu/iov.h"
@@ -128,9 +129,16 @@ static void do_token_setup(USBDevice *s, USBPacket *p)
}
usb_packet_copy(p, s->setup_buf, p->iov.size);
+ s->setup_index = 0;
p->actual_length = 0;
s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6];
- s->setup_index = 0;
+ if (s->setup_len > sizeof(s->data_buf)) {
+ fprintf(stderr,
+ "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n",
+ s->setup_len, sizeof(s->data_buf));
+ p->status = USB_RET_STALL;
+ return;
+ }
request = (s->setup_buf[0] << 8) | s->setup_buf[1];
value = (s->setup_buf[3] << 8) | s->setup_buf[2];
@@ -151,13 +159,6 @@ static void do_token_setup(USBDevice *s, USBPacket *p)
}
s->setup_state = SETUP_STATE_DATA;
} else {
- if (s->setup_len > sizeof(s->data_buf)) {
- fprintf(stderr,
- "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n",
- s->setup_len, sizeof(s->data_buf));
- p->status = USB_RET_STALL;
- return;
- }
if (s->setup_len == 0)
s->setup_state = SETUP_STATE_ACK;
else
@@ -176,7 +177,7 @@ static void do_token_in(USBDevice *s, USBPacket *p)
request = (s->setup_buf[0] << 8) | s->setup_buf[1];
value = (s->setup_buf[3] << 8) | s->setup_buf[2];
index = (s->setup_buf[5] << 8) | s->setup_buf[4];
-
+
switch(s->setup_state) {
case SETUP_STATE_ACK:
if (!(s->setup_buf[0] & USB_DIR_IN)) {
diff --git a/qemu/hw/usb/desc-msos.c b/qemu/hw/usb/desc-msos.c
index 32c3600df..365291981 100644
--- a/qemu/hw/usb/desc-msos.c
+++ b/qemu/hw/usb/desc-msos.c
@@ -1,3 +1,4 @@
+#include "qemu/osdep.h"
#include "hw/usb.h"
#include "hw/usb/desc.h"
diff --git a/qemu/hw/usb/desc.c b/qemu/hw/usb/desc.c
index b82c397ef..adb026e43 100644
--- a/qemu/hw/usb/desc.c
+++ b/qemu/hw/usb/desc.c
@@ -1,4 +1,4 @@
-#include <ctype.h>
+#include "qemu/osdep.h"
#include "hw/usb.h"
#include "hw/usb/desc.h"
diff --git a/qemu/hw/usb/desc.h b/qemu/hw/usb/desc.h
index 8e8db03a0..4d81c68e0 100644
--- a/qemu/hw/usb/desc.h
+++ b/qemu/hw/usb/desc.h
@@ -1,7 +1,6 @@
#ifndef QEMU_HW_USB_DESC_H
#define QEMU_HW_USB_DESC_H
-#include <inttypes.h>
#include <wchar.h>
/* binary representation */
diff --git a/qemu/hw/usb/dev-audio.c b/qemu/hw/usb/dev-audio.c
index f092bb849..87cab0a3d 100644
--- a/qemu/hw/usb/dev-audio.c
+++ b/qemu/hw/usb/dev-audio.c
@@ -29,6 +29,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "hw/usb.h"
#include "hw/usb/desc.h"
@@ -664,7 +665,7 @@ static const VMStateDescription vmstate_usb_audio = {
static Property usb_audio_properties[] = {
DEFINE_PROP_UINT32("debug", USBAudioState, debug, 0),
DEFINE_PROP_UINT32("buffer", USBAudioState, buffer,
- 8 * USBAUDIO_PACKET_SIZE),
+ 32 * USBAUDIO_PACKET_SIZE),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/qemu/hw/usb/dev-bluetooth.c b/qemu/hw/usb/dev-bluetooth.c
index b19ec76b0..91a4a0b8b 100644
--- a/qemu/hw/usb/dev-bluetooth.c
+++ b/qemu/hw/usb/dev-bluetooth.c
@@ -18,6 +18,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/error-report.h"
#include "hw/usb.h"
diff --git a/qemu/hw/usb/dev-hid.c b/qemu/hw/usb/dev-hid.c
index 2e7dcd96c..24d05f76f 100644
--- a/qemu/hw/usb/dev-hid.c
+++ b/qemu/hw/usb/dev-hid.c
@@ -22,10 +22,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "ui/console.h"
#include "hw/usb.h"
#include "hw/usb/desc.h"
+#include "qapi/error.h"
#include "qemu/timer.h"
#include "hw/input/hid.h"
diff --git a/qemu/hw/usb/dev-hub.c b/qemu/hw/usb/dev-hub.c
index c8c685550..a33f21cb3 100644
--- a/qemu/hw/usb/dev-hub.c
+++ b/qemu/hw/usb/dev-hub.c
@@ -21,6 +21,8 @@
* 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-common.h"
#include "trace.h"
#include "hw/usb.h"
diff --git a/qemu/hw/usb/dev-mtp.c b/qemu/hw/usb/dev-mtp.c
index 809b1cb11..bda84a64b 100644
--- a/qemu/hw/usb/dev-mtp.c
+++ b/qemu/hw/usb/dev-mtp.c
@@ -9,12 +9,17 @@
* This code is licensed under the GPL v2 or later.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include <wchar.h>
#include <dirent.h>
-#include <unistd.h>
-#include <sys/stat.h>
#include <sys/statvfs.h>
+#ifdef CONFIG_INOTIFY1
+#include <sys/inotify.h>
+#include "qapi/error.h"
+#include "qemu/main-loop.h"
+#endif
#include "qemu-common.h"
#include "qemu/iov.h"
@@ -62,6 +67,11 @@ enum mtp_code {
/* format codes */
FMT_UNDEFINED_OBJECT = 0x3000,
FMT_ASSOCIATION = 0x3001,
+
+ /* event codes */
+ EVT_OBJ_ADDED = 0x4002,
+ EVT_OBJ_REMOVED = 0x4003,
+ EVT_OBJ_INFO_CHANGED = 0x4007,
};
typedef struct {
@@ -84,6 +94,17 @@ enum {
EP_EVENT,
};
+#ifdef CONFIG_INOTIFY1
+typedef struct MTPMonEntry MTPMonEntry;
+
+struct MTPMonEntry {
+ uint32_t event;
+ uint32_t handle;
+
+ QTAILQ_ENTRY(MTPMonEntry) next;
+};
+#endif
+
struct MTPControl {
uint16_t code;
uint32_t trans;
@@ -108,9 +129,14 @@ struct MTPObject {
char *name;
char *path;
struct stat stat;
+#ifdef CONFIG_INOTIFY1
+ /* inotify watch cookie */
+ int watchfd;
+#endif
MTPObject *parent;
- MTPObject **children;
uint32_t nchildren;
+ QLIST_HEAD(, MTPObject) children;
+ QLIST_ENTRY(MTPObject) list;
bool have_children;
QTAILQ_ENTRY(MTPObject) next;
};
@@ -128,6 +154,11 @@ struct MTPState {
uint32_t next_handle;
QTAILQ_HEAD(, MTPObject) objects;
+#ifdef CONFIG_INOTIFY1
+ /* inotify descriptor */
+ int inotifyfd;
+ QTAILQ_HEAD(events, MTPMonEntry) events;
+#endif
};
#define TYPE_USB_MTP "usb-mtp"
@@ -183,7 +214,7 @@ static const USBDescIface desc_iface_full = {
},{
.bEndpointAddress = USB_DIR_IN | EP_EVENT,
.bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = 8,
+ .wMaxPacketSize = 64,
.bInterval = 0x0a,
},
}
@@ -225,7 +256,7 @@ static const USBDescIface desc_iface_high = {
},{
.bEndpointAddress = USB_DIR_IN | EP_EVENT,
.bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = 8,
+ .wMaxPacketSize = 64,
.bInterval = 0x0a,
},
}
@@ -317,15 +348,24 @@ ignore:
static void usb_mtp_object_free(MTPState *s, MTPObject *o)
{
- int i;
+ MTPObject *iter;
+
+ if (!o) {
+ return;
+ }
trace_usb_mtp_object_free(s->dev.addr, o->handle, o->path);
QTAILQ_REMOVE(&s->objects, o, next);
- for (i = 0; i < o->nchildren; i++) {
- usb_mtp_object_free(s, o->children[i]);
+ if (o->parent) {
+ QLIST_REMOVE(o, list);
+ o->parent->nchildren--;
+ }
+
+ while (!QLIST_EMPTY(&o->children)) {
+ iter = QLIST_FIRST(&o->children);
+ usb_mtp_object_free(s, iter);
}
- g_free(o->children);
g_free(o->name);
g_free(o->path);
g_free(o);
@@ -343,6 +383,203 @@ static MTPObject *usb_mtp_object_lookup(MTPState *s, uint32_t handle)
return NULL;
}
+static MTPObject *usb_mtp_add_child(MTPState *s, MTPObject *o,
+ char *name)
+{
+ MTPObject *child =
+ usb_mtp_object_alloc(s, s->next_handle++, o, name);
+
+ if (child) {
+ trace_usb_mtp_add_child(s->dev.addr, child->handle, child->path);
+ QLIST_INSERT_HEAD(&o->children, child, list);
+ o->nchildren++;
+
+ if (child->format == FMT_ASSOCIATION) {
+ QLIST_INIT(&child->children);
+ }
+ }
+
+ return child;
+}
+
+#ifdef CONFIG_INOTIFY1
+static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent,
+ char *name, int len)
+{
+ MTPObject *iter;
+
+ QLIST_FOREACH(iter, &parent->children, list) {
+ if (strncmp(iter->name, name, len) == 0) {
+ return iter;
+ }
+ }
+
+ return NULL;
+}
+
+static MTPObject *usb_mtp_object_lookup_wd(MTPState *s, int wd)
+{
+ MTPObject *iter;
+
+ QTAILQ_FOREACH(iter, &s->objects, next) {
+ if (iter->watchfd == wd) {
+ return iter;
+ }
+ }
+
+ return NULL;
+}
+
+static void inotify_watchfn(void *arg)
+{
+ MTPState *s = arg;
+ ssize_t bytes;
+ /* From the man page: atleast one event can be read */
+ int pos;
+ char buf[sizeof(struct inotify_event) + NAME_MAX + 1];
+
+ for (;;) {
+ bytes = read(s->inotifyfd, buf, sizeof(buf));
+ pos = 0;
+
+ if (bytes <= 0) {
+ /* Better luck next time */
+ return;
+ }
+
+ /*
+ * TODO: Ignore initiator initiated events.
+ * For now we are good because the store is RO
+ */
+ while (bytes > 0) {
+ char *p = buf + pos;
+ struct inotify_event *event = (struct inotify_event *)p;
+ int watchfd = 0;
+ uint32_t mask = event->mask & (IN_CREATE | IN_DELETE |
+ IN_MODIFY | IN_IGNORED);
+ MTPObject *parent = usb_mtp_object_lookup_wd(s, event->wd);
+ MTPMonEntry *entry = NULL;
+ MTPObject *o;
+
+ pos = pos + sizeof(struct inotify_event) + event->len;
+ bytes = bytes - pos;
+
+ if (!parent) {
+ continue;
+ }
+
+ switch (mask) {
+ case IN_CREATE:
+ if (usb_mtp_object_lookup_name
+ (parent, event->name, event->len)) {
+ /* Duplicate create event */
+ continue;
+ }
+ entry = g_new0(MTPMonEntry, 1);
+ entry->handle = s->next_handle;
+ entry->event = EVT_OBJ_ADDED;
+ o = usb_mtp_add_child(s, parent, event->name);
+ if (!o) {
+ g_free(entry);
+ continue;
+ }
+ o->watchfd = watchfd;
+ trace_usb_mtp_inotify_event(s->dev.addr, event->name,
+ event->mask, "Obj Added");
+ break;
+
+ case IN_DELETE:
+ /*
+ * The kernel issues a IN_IGNORED event
+ * when a dir containing a watchpoint is
+ * deleted, so we don't have to delete the
+ * watchpoint
+ */
+ o = usb_mtp_object_lookup_name(parent, event->name, event->len);
+ if (!o) {
+ continue;
+ }
+ entry = g_new0(MTPMonEntry, 1);
+ entry->handle = o->handle;
+ entry->event = EVT_OBJ_REMOVED;
+ trace_usb_mtp_inotify_event(s->dev.addr, o->path,
+ event->mask, "Obj Deleted");
+ usb_mtp_object_free(s, o);
+ break;
+
+ case IN_MODIFY:
+ o = usb_mtp_object_lookup_name(parent, event->name, event->len);
+ if (!o) {
+ continue;
+ }
+ entry = g_new0(MTPMonEntry, 1);
+ entry->handle = o->handle;
+ entry->event = EVT_OBJ_INFO_CHANGED;
+ trace_usb_mtp_inotify_event(s->dev.addr, o->path,
+ event->mask, "Obj Modified");
+ break;
+
+ case IN_IGNORED:
+ o = usb_mtp_object_lookup_name(parent, event->name, event->len);
+ trace_usb_mtp_inotify_event(s->dev.addr, o->path,
+ event->mask, "Obj ignored");
+ break;
+
+ default:
+ fprintf(stderr, "usb-mtp: failed to parse inotify event\n");
+ continue;
+ }
+
+ if (entry) {
+ QTAILQ_INSERT_HEAD(&s->events, entry, next);
+ }
+ }
+ }
+}
+
+static int usb_mtp_inotify_init(MTPState *s)
+{
+ int fd;
+
+ fd = inotify_init1(IN_NONBLOCK);
+ if (fd == -1) {
+ return 1;
+ }
+
+ QTAILQ_INIT(&s->events);
+ s->inotifyfd = fd;
+
+ qemu_set_fd_handler(fd, inotify_watchfn, NULL, s);
+
+ return 0;
+}
+
+static void usb_mtp_inotify_cleanup(MTPState *s)
+{
+ MTPMonEntry *e, *p;
+
+ if (!s->inotifyfd) {
+ return;
+ }
+
+ qemu_set_fd_handler(s->inotifyfd, NULL, NULL, s);
+ close(s->inotifyfd);
+
+ QTAILQ_FOREACH_SAFE(e, &s->events, next, p) {
+ QTAILQ_REMOVE(&s->events, e, next);
+ g_free(e);
+ }
+}
+
+static int usb_mtp_add_watch(int inotifyfd, char *path)
+{
+ uint32_t mask = IN_CREATE | IN_DELETE | IN_MODIFY |
+ IN_ISDIR;
+
+ return inotify_add_watch(inotifyfd, path, mask);
+}
+#endif
+
static void usb_mtp_object_readdir(MTPState *s, MTPObject *o)
{
struct dirent *entry;
@@ -357,16 +594,18 @@ static void usb_mtp_object_readdir(MTPState *s, MTPObject *o)
if (!dir) {
return;
}
+#ifdef CONFIG_INOTIFY1
+ int watchfd = usb_mtp_add_watch(s->inotifyfd, o->path);
+ if (watchfd == -1) {
+ fprintf(stderr, "usb-mtp: failed to add watch for %s\n", o->path);
+ } else {
+ trace_usb_mtp_inotify_event(s->dev.addr, o->path,
+ 0, "Watch Added");
+ o->watchfd = watchfd;
+ }
+#endif
while ((entry = readdir(dir)) != NULL) {
- if ((o->nchildren % 32) == 0) {
- o->children = g_realloc(o->children,
- (o->nchildren + 32) * sizeof(MTPObject *));
- }
- o->children[o->nchildren] =
- usb_mtp_object_alloc(s, s->next_handle++, o, entry->d_name);
- if (o->children[o->nchildren] != NULL) {
- o->nchildren++;
- }
+ usb_mtp_add_child(s, o, entry->d_name);
}
closedir(dir);
}
@@ -480,7 +719,7 @@ static void usb_mtp_add_wstr(MTPData *data, const wchar_t *str)
static void usb_mtp_add_str(MTPData *data, const char *str)
{
uint32_t len = strlen(str)+1;
- wchar_t wstr[len];
+ wchar_t *wstr = g_new(wchar_t, len);
size_t ret;
ret = mbstowcs(wstr, str, len);
@@ -489,6 +728,8 @@ static void usb_mtp_add_str(MTPData *data, const char *str)
} else {
usb_mtp_add_wstr(data, wstr);
}
+
+ g_free(wstr);
}
static void usb_mtp_add_time(MTPData *data, time_t time)
@@ -618,13 +859,15 @@ static MTPData *usb_mtp_get_object_handles(MTPState *s, MTPControl *c,
MTPObject *o)
{
MTPData *d = usb_mtp_data_alloc(c);
- uint32_t i, handles[o->nchildren];
+ uint32_t i = 0, handles[o->nchildren];
+ MTPObject *iter;
trace_usb_mtp_op_get_object_handles(s->dev.addr, o->handle, o->path);
- for (i = 0; i < o->nchildren; i++) {
- handles[i] = o->children[i]->handle;
+ QLIST_FOREACH(iter, &o->children, list) {
+ handles[i++] = iter->handle;
}
+ assert(i == o->nchildren);
usb_mtp_add_u32_array(d, o->nchildren, handles);
return d;
@@ -755,11 +998,19 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
trace_usb_mtp_op_open_session(s->dev.addr);
s->session = c->argv[0];
usb_mtp_object_alloc(s, s->next_handle++, NULL, s->root);
+#ifdef CONFIG_INOTIFY1
+ if (usb_mtp_inotify_init(s)) {
+ fprintf(stderr, "usb-mtp: file monitoring init failed\n");
+ }
+#endif
break;
case CMD_CLOSE_SESSION:
trace_usb_mtp_op_close_session(s->dev.addr);
s->session = 0;
s->next_handle = 0;
+#ifdef CONFIG_INOTIFY1
+ usb_mtp_inotify_cleanup(s);
+#endif
usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects));
assert(QTAILQ_EMPTY(&s->objects));
break;
@@ -885,6 +1136,10 @@ static void usb_mtp_handle_reset(USBDevice *dev)
trace_usb_mtp_reset(s->dev.addr);
+#ifdef CONFIG_INOTIFY1
+ usb_mtp_inotify_cleanup(s);
+#endif
+ usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects));
s->session = 0;
usb_mtp_data_free(s->data_in);
s->data_in = NULL;
@@ -1044,6 +1299,31 @@ static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p)
}
break;
case EP_EVENT:
+#ifdef CONFIG_INOTIFY1
+ if (!QTAILQ_EMPTY(&s->events)) {
+ struct MTPMonEntry *e = QTAILQ_LAST(&s->events, events);
+ uint32_t handle;
+ int len = sizeof(container) + sizeof(uint32_t);
+
+ if (p->iov.size < len) {
+ trace_usb_mtp_stall(s->dev.addr,
+ "packet too small to send event");
+ p->status = USB_RET_STALL;
+ return;
+ }
+
+ QTAILQ_REMOVE(&s->events, e, next);
+ container.length = cpu_to_le32(len);
+ container.type = cpu_to_le32(TYPE_EVENT);
+ container.code = cpu_to_le16(e->event);
+ container.trans = 0; /* no trans specific events */
+ handle = cpu_to_le32(e->handle);
+ usb_packet_copy(p, &container, sizeof(container));
+ usb_packet_copy(p, &handle, sizeof(uint32_t));
+ g_free(e);
+ return;
+ }
+#endif
p->status = USB_RET_NAK;
return;
default:
diff --git a/qemu/hw/usb/dev-network.c b/qemu/hw/usb/dev-network.c
index 7800ceea5..74306b58e 100644
--- a/qemu/hw/usb/dev-network.c
+++ b/qemu/hw/usb/dev-network.c
@@ -23,6 +23,8 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu-common.h"
#include "hw/usb.h"
#include "hw/usb/desc.h"
@@ -32,6 +34,7 @@
#include "qemu/config-file.h"
#include "sysemu/sysemu.h"
#include "qemu/iov.h"
+#include "qemu/cutils.h"
/*#define TRAFFIC_DEBUG*/
/* Thanks to NetChip Technologies for donating this product ID.
@@ -653,7 +656,8 @@ typedef struct USBNetState {
static int is_rndis(USBNetState *s)
{
- return s->dev.config->bConfigurationValue == DEV_RNDIS_CONFIG_VALUE;
+ return s->dev.config ?
+ s->dev.config->bConfigurationValue == DEV_RNDIS_CONFIG_VALUE : 0;
}
static int ndis_query(USBNetState *s, uint32_t oid,
@@ -914,8 +918,9 @@ static int rndis_query_response(USBNetState *s,
bufoffs = le32_to_cpu(buf->InformationBufferOffset) + 8;
buflen = le32_to_cpu(buf->InformationBufferLength);
- if (bufoffs + buflen > length)
+ if (buflen > length || bufoffs >= length || bufoffs + buflen > length) {
return USB_RET_STALL;
+ }
infobuflen = ndis_query(s, le32_to_cpu(buf->OID),
bufoffs + (uint8_t *) buf, buflen, infobuf,
@@ -960,8 +965,9 @@ static int rndis_set_response(USBNetState *s,
bufoffs = le32_to_cpu(buf->InformationBufferOffset) + 8;
buflen = le32_to_cpu(buf->InformationBufferLength);
- if (bufoffs + buflen > length)
+ if (buflen > length || bufoffs >= length || bufoffs + buflen > length) {
return USB_RET_STALL;
+ }
ret = ndis_set(s, le32_to_cpu(buf->OID),
bufoffs + (uint8_t *) buf, buflen);
@@ -1211,8 +1217,9 @@ static void usb_net_handle_dataout(USBNetState *s, USBPacket *p)
if (le32_to_cpu(msg->MessageType) == RNDIS_PACKET_MSG) {
uint32_t offs = 8 + le32_to_cpu(msg->DataOffset);
uint32_t size = le32_to_cpu(msg->DataLength);
- if (offs + size <= len)
+ if (offs < len && size < len && offs + size <= len) {
qemu_send_packet(qemu_get_queue(s->nic), s->out_buf + offs, size);
+ }
}
s->out_ptr -= len;
memmove(s->out_buf, &s->out_buf[len], s->out_ptr);
diff --git a/qemu/hw/usb/dev-serial.c b/qemu/hw/usb/dev-serial.c
index a6a66008e..ba8538e60 100644
--- a/qemu/hw/usb/dev-serial.c
+++ b/qemu/hw/usb/dev-serial.c
@@ -8,7 +8,10 @@
* This code is licensed under the LGPL.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu-common.h"
+#include "qemu/cutils.h"
#include "qemu/error-report.h"
#include "hw/usb.h"
#include "hw/usb/desc.h"
diff --git a/qemu/hw/usb/dev-smartcard-reader.c b/qemu/hw/usb/dev-smartcard-reader.c
index 8952efffa..af4b85135 100644
--- a/qemu/hw/usb/dev-smartcard-reader.c
+++ b/qemu/hw/usb/dev-smartcard-reader.c
@@ -34,6 +34,8 @@
* Not sure which messages trigger this.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu-common.h"
#include "qemu/error-report.h"
#include "hw/usb.h"
diff --git a/qemu/hw/usb/dev-storage.c b/qemu/hw/usb/dev-storage.c
index 9a4e7dc0c..248a58045 100644
--- a/qemu/hw/usb/dev-storage.c
+++ b/qemu/hw/usb/dev-storage.c
@@ -7,6 +7,8 @@
* This code is licensed under the LGPL.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu-common.h"
#include "qemu/error-report.h"
#include "qemu/option.h"
@@ -20,6 +22,7 @@
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
#include "qapi/visitor.h"
+#include "qemu/cutils.h"
//#define DEBUG_MSD
@@ -613,20 +616,22 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp)
return;
}
- bdrv_add_key(blk_bs(blk), NULL, &err);
- if (err) {
- if (monitor_cur_is_qmp()) {
- error_propagate(errp, err);
- return;
- }
- error_free(err);
- err = NULL;
- if (cur_mon) {
- monitor_read_bdrv_key_start(cur_mon, blk_bs(blk),
- usb_msd_password_cb, s);
- s->dev.auto_attach = 0;
- } else {
- autostart = 0;
+ if (blk_bs(blk)) {
+ bdrv_add_key(blk_bs(blk), NULL, &err);
+ if (err) {
+ if (monitor_cur_is_qmp()) {
+ error_propagate(errp, err);
+ return;
+ }
+ error_free(err);
+ err = NULL;
+ if (cur_mon) {
+ monitor_read_bdrv_key_start(cur_mon, blk_bs(blk),
+ usb_msd_password_cb, s);
+ s->dev.auto_attach = 0;
+ } else {
+ autostart = 0;
+ }
}
}
@@ -778,24 +783,24 @@ static void usb_msd_class_initfn_storage(ObjectClass *klass, void *data)
dc->props = msd_properties;
}
-static void usb_msd_get_bootindex(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void usb_msd_get_bootindex(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
USBDevice *dev = USB_DEVICE(obj);
MSDState *s = USB_STORAGE_DEV(dev);
- visit_type_int32(v, &s->conf.bootindex, name, errp);
+ visit_type_int32(v, name, &s->conf.bootindex, errp);
}
-static void usb_msd_set_bootindex(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void usb_msd_set_bootindex(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
USBDevice *dev = USB_DEVICE(obj);
MSDState *s = USB_STORAGE_DEV(dev);
int32_t boot_index;
Error *local_err = NULL;
- visit_type_int32(v, &boot_index, name, &local_err);
+ visit_type_int32(v, name, &boot_index, &local_err);
if (local_err) {
goto out;
}
diff --git a/qemu/hw/usb/dev-uas.c b/qemu/hw/usb/dev-uas.c
index 38b26c586..0678b1b05 100644
--- a/qemu/hw/usb/dev-uas.c
+++ b/qemu/hw/usb/dev-uas.c
@@ -9,6 +9,7 @@
* See the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/option.h"
#include "qemu/config-file.h"
diff --git a/qemu/hw/usb/dev-wacom.c b/qemu/hw/usb/dev-wacom.c
index c2450e729..c4702dbba 100644
--- a/qemu/hw/usb/dev-wacom.c
+++ b/qemu/hw/usb/dev-wacom.c
@@ -25,6 +25,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "ui/console.h"
#include "hw/usb.h"
diff --git a/qemu/hw/usb/hcd-ehci-pci.c b/qemu/hw/usb/hcd-ehci-pci.c
index 7afa5f9d6..56577051e 100644
--- a/qemu/hw/usb/hcd-ehci-pci.c
+++ b/qemu/hw/usb/hcd-ehci-pci.c
@@ -15,6 +15,7 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/usb/hcd-ehci.h"
#include "qemu/range.h"
@@ -95,10 +96,8 @@ static void usb_ehci_pci_exit(PCIDevice *dev)
usb_ehci_unrealize(s, DEVICE(dev), NULL);
- if (s->irq) {
- g_free(s->irq);
- s->irq = NULL;
- }
+ g_free(s->irq);
+ s->irq = NULL;
}
static void usb_ehci_pci_reset(DeviceState *dev)
diff --git a/qemu/hw/usb/hcd-ehci-sysbus.c b/qemu/hw/usb/hcd-ehci-sysbus.c
index cd1cc142a..6c20604d0 100644
--- a/qemu/hw/usb/hcd-ehci-sysbus.c
+++ b/qemu/hw/usb/hcd-ehci-sysbus.c
@@ -15,6 +15,7 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/usb/hcd-ehci.h"
static const VMStateDescription vmstate_ehci_sysbus = {
diff --git a/qemu/hw/usb/hcd-ehci.c b/qemu/hw/usb/hcd-ehci.c
index 64a54c6e8..43a8f7abc 100644
--- a/qemu/hw/usb/hcd-ehci.c
+++ b/qemu/hw/usb/hcd-ehci.c
@@ -27,6 +27,8 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/usb/ehci-regs.h"
#include "hw/usb/hcd-ehci.h"
#include "trace.h"
@@ -726,7 +728,7 @@ static void ehci_detach(USBPort *port)
ehci_queues_rip_device(s, port->dev, 0);
ehci_queues_rip_device(s, port->dev, 1);
- *portsc &= ~(PORTSC_CONNECT|PORTSC_PED);
+ *portsc &= ~(PORTSC_CONNECT|PORTSC_PED|PORTSC_SUSPEND);
*portsc |= PORTSC_CSC;
ehci_raise_irq(s, USBSTS_PCD);
@@ -865,6 +867,7 @@ void ehci_reset(void *opaque)
s->usbsts = USBSTS_HALT;
s->usbsts_pending = 0;
s->usbsts_frindex = 0;
+ ehci_update_irq(s);
s->astate = EST_INACTIVE;
s->pstate = EST_INACTIVE;
@@ -893,6 +896,11 @@ static uint64_t ehci_caps_read(void *ptr, hwaddr addr,
return s->caps[addr];
}
+static void ehci_caps_write(void *ptr, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+}
+
static uint64_t ehci_opreg_read(void *ptr, hwaddr addr,
unsigned size)
{
@@ -1404,21 +1412,23 @@ static int ehci_process_itd(EHCIState *ehci,
if (itd->transact[i] & ITD_XACT_ACTIVE) {
pg = get_field(itd->transact[i], ITD_XACT_PGSEL);
off = itd->transact[i] & ITD_XACT_OFFSET_MASK;
- ptr1 = (itd->bufptr[pg] & ITD_BUFPTR_MASK);
- ptr2 = (itd->bufptr[pg+1] & ITD_BUFPTR_MASK);
len = get_field(itd->transact[i], ITD_XACT_LENGTH);
if (len > max * mult) {
len = max * mult;
}
-
- if (len > BUFF_SIZE) {
+ if (len > BUFF_SIZE || pg > 6) {
return -1;
}
+ ptr1 = (itd->bufptr[pg] & ITD_BUFPTR_MASK);
qemu_sglist_init(&ehci->isgl, ehci->device, 2, ehci->as);
if (off + len > 4096) {
/* transfer crosses page border */
+ if (pg == 6) {
+ return -1; /* avoid page pg + 1 */
+ }
+ ptr2 = (itd->bufptr[pg + 1] & ITD_BUFPTR_MASK);
uint32_t len2 = off + len - 4096;
uint32_t len1 = len - len2;
qemu_sglist_add(&ehci->isgl, ptr1 + off, len1);
@@ -2000,6 +2010,7 @@ static int ehci_state_writeback(EHCIQueue *q)
static void ehci_advance_state(EHCIState *ehci, int async)
{
EHCIQueue *q = NULL;
+ int itd_count = 0;
int again;
do {
@@ -2024,10 +2035,12 @@ static void ehci_advance_state(EHCIState *ehci, int async)
case EST_FETCHITD:
again = ehci_state_fetchitd(ehci, async);
+ itd_count++;
break;
case EST_FETCHSITD:
again = ehci_state_fetchsitd(ehci, async);
+ itd_count++;
break;
case EST_ADVANCEQUEUE:
@@ -2076,7 +2089,8 @@ static void ehci_advance_state(EHCIState *ehci, int async)
break;
}
- if (again < 0) {
+ if (again < 0 || itd_count > 16) {
+ /* TODO: notify guest (raise HSE irq?) */
fprintf(stderr, "processing error - resetting ehci HC\n");
ehci_reset(ehci);
again = 0;
@@ -2298,10 +2312,11 @@ static void ehci_frame_timer(void *opaque)
/* If we've raised int, we speed up the timer, so that we quickly
* notice any new packets queued up in response */
if (ehci->int_req_by_async && (ehci->usbsts & USBSTS_INT)) {
- expire_time = t_now + get_ticks_per_sec() / (FRAME_TIMER_FREQ * 4);
+ expire_time = t_now +
+ NANOSECONDS_PER_SECOND / (FRAME_TIMER_FREQ * 4);
ehci->int_req_by_async = false;
} else {
- expire_time = t_now + (get_ticks_per_sec()
+ expire_time = t_now + (NANOSECONDS_PER_SECOND
* (ehci->async_stepdown+1) / FRAME_TIMER_FREQ);
}
timer_mod(ehci->frame_timer, expire_time);
@@ -2310,6 +2325,7 @@ static void ehci_frame_timer(void *opaque)
static const MemoryRegionOps ehci_mmio_caps_ops = {
.read = ehci_caps_read,
+ .write = ehci_caps_write,
.valid.min_access_size = 1,
.valid.max_access_size = 4,
.impl.min_access_size = 1,
diff --git a/qemu/hw/usb/hcd-musb.c b/qemu/hw/usb/hcd-musb.c
index 61cc87894..27d9d0bd8 100644
--- a/qemu/hw/usb/hcd-musb.c
+++ b/qemu/hw/usb/hcd-musb.c
@@ -20,6 +20,7 @@
*
* Only host-mode and non-DMA accesses are currently supported.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/timer.h"
#include "hw/usb.h"
@@ -563,7 +564,7 @@ static void musb_schedule_cb(USBPort *port, USBPacket *packey)
ep->intv_timer[dir] = timer_new_ns(QEMU_CLOCK_VIRTUAL, musb_cb_tick, ep);
timer_mod(ep->intv_timer[dir], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
- muldiv64(timeout, get_ticks_per_sec(), 8000));
+ muldiv64(timeout, NANOSECONDS_PER_SECOND, 8000));
}
static int musb_timeout(int ttype, int speed, int val)
diff --git a/qemu/hw/usb/hcd-ohci.c b/qemu/hw/usb/hcd-ohci.c
index 7d6581806..ffab561cf 100644
--- a/qemu/hw/usb/hcd-ohci.c
+++ b/qemu/hw/usb/hcd-ohci.c
@@ -25,7 +25,9 @@
* o BIOS work to boot from USB storage
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
+#include "qapi/error.h"
#include "qemu/timer.h"
#include "hw/usb.h"
#include "hw/pci/pci.h"
@@ -439,15 +441,37 @@ static void ohci_stop_endpoints(OHCIState *ohci)
}
}
-/* Reset the controller */
-static void ohci_reset(void *opaque)
+static void ohci_roothub_reset(OHCIState *ohci)
{
- OHCIState *ohci = opaque;
OHCIPort *port;
int i;
ohci_bus_stop(ohci);
- ohci->ctl = 0;
+ ohci->rhdesc_a = OHCI_RHA_NPS | ohci->num_ports;
+ ohci->rhdesc_b = 0x0; /* Impl. specific */
+ ohci->rhstatus = 0;
+
+ for (i = 0; i < ohci->num_ports; i++) {
+ port = &ohci->rhport[i];
+ port->ctrl = 0;
+ if (port->port.dev && port->port.dev->attached) {
+ usb_port_reset(&port->port);
+ }
+ }
+ if (ohci->async_td) {
+ usb_cancel_packet(&ohci->usb_packet);
+ ohci->async_td = 0;
+ }
+ ohci_stop_endpoints(ohci);
+}
+
+/* Reset the controller */
+static void ohci_soft_reset(OHCIState *ohci)
+{
+ trace_usb_ohci_reset(ohci->name);
+
+ ohci_bus_stop(ohci);
+ ohci->ctl = (ohci->ctl & OHCI_CTL_IR) | OHCI_USB_SUSPEND;
ohci->old_ctl = 0;
ohci->status = 0;
ohci->intr_status = 0;
@@ -470,25 +494,13 @@ static void ohci_reset(void *opaque)
ohci->frame_number = 0;
ohci->pstart = 0;
ohci->lst = OHCI_LS_THRESH;
+}
- ohci->rhdesc_a = OHCI_RHA_NPS | ohci->num_ports;
- ohci->rhdesc_b = 0x0; /* Impl. specific */
- ohci->rhstatus = 0;
-
- for (i = 0; i < ohci->num_ports; i++)
- {
- port = &ohci->rhport[i];
- port->ctrl = 0;
- if (port->port.dev && port->port.dev->attached) {
- usb_port_reset(&port->port);
- }
- }
- if (ohci->async_td) {
- usb_cancel_packet(&ohci->usb_packet);
- ohci->async_td = 0;
- }
- ohci_stop_endpoints(ohci);
- trace_usb_ohci_reset(ohci->name);
+static void ohci_hard_reset(OHCIState *ohci)
+{
+ ohci_soft_reset(ohci);
+ ohci->ctl = 0;
+ ohci_roothub_reset(ohci);
}
/* Get an array of dwords from main memory */
@@ -1231,11 +1243,16 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion)
return active;
}
-/* Generate a SOF event, and set a timer for EOF */
-static void ohci_sof(OHCIState *ohci)
+/* set a timer for EOF */
+static void ohci_eof_timer(OHCIState *ohci)
{
ohci->sof_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
timer_mod(ohci->eof_timer, ohci->sof_time + usb_frame_time);
+}
+/* Set a timer for EOF and generate a SOF event */
+static void ohci_sof(OHCIState *ohci)
+{
+ ohci_eof_timer(ohci);
ohci_set_interrupt(ohci, OHCI_INTR_SF);
}
@@ -1331,19 +1348,14 @@ static void ohci_frame_boundary(void *opaque)
*/
static int ohci_bus_start(OHCIState *ohci)
{
- ohci->eof_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
- ohci_frame_boundary,
- ohci);
-
- if (ohci->eof_timer == NULL) {
- trace_usb_ohci_bus_eof_timer_failed(ohci->name);
- ohci_die(ohci);
- return 0;
- }
-
trace_usb_ohci_start(ohci->name);
- ohci_sof(ohci);
+ /* Delay the first SOF event by one frame time as
+ * linux driver is not ready to receive it and
+ * can meet some race conditions
+ */
+
+ ohci_eof_timer(ohci);
return 1;
}
@@ -1352,11 +1364,7 @@ static int ohci_bus_start(OHCIState *ohci)
static void ohci_bus_stop(OHCIState *ohci)
{
trace_usb_ohci_stop(ohci->name);
- if (ohci->eof_timer) {
- timer_del(ohci->eof_timer);
- timer_free(ohci->eof_timer);
- }
- ohci->eof_timer = NULL;
+ timer_del(ohci->eof_timer);
}
/* Sets a flag in a port status register but only set it if the port is
@@ -1436,12 +1444,15 @@ static void ohci_set_ctl(OHCIState *ohci, uint32_t val)
break;
case OHCI_USB_SUSPEND:
ohci_bus_stop(ohci);
+ /* clear pending SF otherwise linux driver loops in ohci_irq() */
+ ohci->intr_status &= ~OHCI_INTR_SF;
+ ohci_intr_update(ohci);
break;
case OHCI_USB_RESUME:
trace_usb_ohci_resume(ohci->name);
break;
case OHCI_USB_RESET:
- ohci_reset(ohci);
+ ohci_roothub_reset(ohci);
break;
}
}
@@ -1704,7 +1715,7 @@ static void ohci_mem_write(void *opaque,
ohci->status |= val;
if (ohci->status & OHCI_STATUS_HCR)
- ohci_reset(ohci);
+ ohci_soft_reset(ohci);
break;
case 3: /* HcInterruptStatus */
@@ -1783,7 +1794,7 @@ static void ohci_mem_write(void *opaque,
case 25: /* HcHReset */
ohci->hreset = val & ~OHCI_HRESET_FSBIR;
if (val & OHCI_HRESET_FSBIR)
- ohci_reset(ohci);
+ ohci_hard_reset(ohci);
break;
case 26: /* HcHInterruptEnable */
@@ -1839,12 +1850,12 @@ static void usb_ohci_init(OHCIState *ohci, DeviceState *dev,
if (usb_frame_time == 0) {
#ifdef OHCI_TIME_WARP
- usb_frame_time = get_ticks_per_sec();
- usb_bit_time = muldiv64(1, get_ticks_per_sec(), USB_HZ/1000);
+ usb_frame_time = NANOSECONDS_PER_SECOND;
+ usb_bit_time = NANOSECONDS_PER_SECOND / (USB_HZ / 1000);
#else
- usb_frame_time = muldiv64(1, get_ticks_per_sec(), 1000);
- if (get_ticks_per_sec() >= USB_HZ) {
- usb_bit_time = muldiv64(1, get_ticks_per_sec(), USB_HZ);
+ usb_frame_time = NANOSECONDS_PER_SECOND / 1000;
+ if (NANOSECONDS_PER_SECOND >= USB_HZ) {
+ usb_bit_time = NANOSECONDS_PER_SECOND / USB_HZ;
} else {
usb_bit_time = 1;
}
@@ -1883,6 +1894,9 @@ static void usb_ohci_init(OHCIState *ohci, DeviceState *dev,
usb_packet_init(&ohci->usb_packet);
ohci->async_td = 0;
+
+ ohci->eof_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ ohci_frame_boundary, ohci);
}
#define TYPE_PCI_OHCI "pci-ohci"
@@ -1952,6 +1966,9 @@ static void usb_ohci_exit(PCIDevice *dev)
if (!ohci->masterbus) {
usb_bus_release(&s->bus);
}
+
+ timer_del(s->eof_timer);
+ timer_free(s->eof_timer);
}
static void usb_ohci_reset_pci(DeviceState *d)
@@ -1960,7 +1977,7 @@ static void usb_ohci_reset_pci(DeviceState *d)
OHCIPCIState *ohci = PCI_OHCI(dev);
OHCIState *s = &ohci->state;
- ohci_reset(s);
+ ohci_hard_reset(s);
}
#define TYPE_SYSBUS_OHCI "sysbus-ohci"
@@ -1993,7 +2010,7 @@ static void usb_ohci_reset_sysbus(DeviceState *dev)
OHCISysBusState *s = SYSBUS_OHCI(dev);
OHCIState *ohci = &s->ohci;
- ohci_reset(ohci);
+ ohci_hard_reset(ohci);
}
static Property ohci_pci_properties[] = {
@@ -2017,23 +2034,13 @@ static bool ohci_eof_timer_needed(void *opaque)
{
OHCIState *ohci = opaque;
- return ohci->eof_timer != NULL;
-}
-
-static int ohci_eof_timer_pre_load(void *opaque)
-{
- OHCIState *ohci = opaque;
-
- ohci_bus_start(ohci);
-
- return 0;
+ return timer_pending(ohci->eof_timer);
}
static const VMStateDescription vmstate_ohci_eof_timer = {
.name = "ohci-core/eof-timer",
.version_id = 1,
.minimum_version_id = 1,
- .pre_load = ohci_eof_timer_pre_load,
.needed = ohci_eof_timer_needed,
.fields = (VMStateField[]) {
VMSTATE_TIMER_PTR(eof_timer, OHCIState),
diff --git a/qemu/hw/usb/hcd-uhci.c b/qemu/hw/usb/hcd-uhci.c
index 3f0ed6268..ca72a80f2 100644
--- a/qemu/hw/usb/hcd-uhci.c
+++ b/qemu/hw/usb/hcd-uhci.c
@@ -25,10 +25,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/usb.h"
#include "hw/usb/uhci-regs.h"
#include "hw/pci/pci.h"
+#include "qapi/error.h"
#include "qemu/timer.h"
#include "qemu/iov.h"
#include "sysemu/dma.h"
@@ -401,7 +403,7 @@ static int uhci_post_load(void *opaque, int version_id)
if (version_id < 2) {
s->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
- (get_ticks_per_sec() / FRAME_TIMER_FREQ);
+ (NANOSECONDS_PER_SECOND / FRAME_TIMER_FREQ);
}
return 0;
}
@@ -443,7 +445,7 @@ static void uhci_port_write(void *opaque, hwaddr addr,
/* start frame processing */
trace_usb_uhci_schedule_start();
s->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
- (get_ticks_per_sec() / FRAME_TIMER_FREQ);
+ (NANOSECONDS_PER_SECOND / FRAME_TIMER_FREQ);
timer_mod(s->frame_timer, s->expire_time);
s->status &= ~UHCI_STS_HCHALTED;
} else if (!(val & UHCI_CMD_RS)) {
@@ -772,8 +774,9 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
bool spd;
bool queuing = (q != NULL);
uint8_t pid = td->token & 0xff;
- UHCIAsync *async = uhci_async_find_td(s, td_addr);
+ UHCIAsync *async;
+ async = uhci_async_find_td(s, td_addr);
if (async) {
if (uhci_queue_verify(async->queue, qh_addr, td, td_addr, queuing)) {
assert(q == NULL || q == async->queue);
@@ -812,6 +815,19 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
return TD_RESULT_NEXT_QH;
}
+ switch (pid) {
+ case USB_TOKEN_OUT:
+ case USB_TOKEN_SETUP:
+ case USB_TOKEN_IN:
+ break;
+ default:
+ /* invalid pid : frame interrupted */
+ s->status |= UHCI_STS_HCPERR;
+ s->cmd &= ~UHCI_CMD_RS;
+ uhci_update_irq(s);
+ return TD_RESULT_STOP_FRAME;
+ }
+
if (async) {
if (queuing) {
/* we are busy filling the queue, we are not prepared
@@ -879,11 +895,7 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
break;
default:
- /* invalid pid : frame interrupted */
- uhci_async_free(async);
- s->status |= UHCI_STS_HCPERR;
- uhci_update_irq(s);
- return TD_RESULT_STOP_FRAME;
+ abort(); /* Never to execute */
}
if (async->packet.status == USB_RET_ASYNC) {
@@ -1119,7 +1131,7 @@ static void uhci_frame_timer(void *opaque)
UHCIState *s = opaque;
uint64_t t_now, t_last_run;
int i, frames;
- const uint64_t frame_t = get_ticks_per_sec() / FRAME_TIMER_FREQ;
+ const uint64_t frame_t = NANOSECONDS_PER_SECOND / FRAME_TIMER_FREQ;
s->completions_only = false;
qemu_bh_cancel(s->bh);
diff --git a/qemu/hw/usb/hcd-xhci.c b/qemu/hw/usb/hcd-xhci.c
index c673bed4c..bcde8a2f4 100644
--- a/qemu/hw/usb/hcd-xhci.c
+++ b/qemu/hw/usb/hcd-xhci.c
@@ -18,6 +18,7 @@
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "qemu/timer.h"
#include "hw/usb.h"
@@ -697,11 +698,13 @@ static inline void xhci_dma_write_u32s(XHCIState *xhci, dma_addr_t addr,
uint32_t *buf, size_t len)
{
int i;
- uint32_t tmp[len / sizeof(uint32_t)];
+ uint32_t tmp[5];
+ uint32_t n = len / sizeof(uint32_t);
assert((len % sizeof(uint32_t)) == 0);
+ assert(n <= ARRAY_SIZE(tmp));
- for (i = 0; i < (len / sizeof(uint32_t)); i++) {
+ for (i = 0; i < n; i++) {
tmp[i] = cpu_to_le32(buf[i]);
}
pci_dma_write(PCI_DEVICE(xhci), addr, tmp, len);
@@ -1453,9 +1456,7 @@ static int xhci_ep_nuke_one_xfer(XHCITransfer *t, TRBCCode report)
t->running_retry = 0;
killed = 1;
}
- if (t->trbs) {
- g_free(t->trbs);
- }
+ g_free(t->trbs);
t->trbs = NULL;
t->trb_count = t->trb_alloced = 0;
@@ -2190,7 +2191,7 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
xfer->trbs = NULL;
}
if (!xfer->trbs) {
- xfer->trbs = g_malloc(sizeof(XHCITRB) * length);
+ xfer->trbs = g_new(XHCITRB, length);
xfer->trb_alloced = length;
}
xfer->trb_count = length;
diff --git a/qemu/hw/usb/host-legacy.c b/qemu/hw/usb/host-legacy.c
index 422ed9a65..3b57e21b5 100644
--- a/qemu/hw/usb/host-legacy.c
+++ b/qemu/hw/usb/host-legacy.c
@@ -30,6 +30,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "hw/usb.h"
#include "hw/usb/host.h"
diff --git a/qemu/hw/usb/host-libusb.c b/qemu/hw/usb/host-libusb.c
index 11429f5e7..6458a9448 100644
--- a/qemu/hw/usb/host-libusb.c
+++ b/qemu/hw/usb/host-libusb.c
@@ -33,9 +33,11 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include <poll.h>
#include <libusb.h>
+#include "qapi/error.h"
#include "qemu-common.h"
#include "monitor/monitor.h"
#include "qemu/error-report.h"
@@ -451,6 +453,7 @@ static void usb_host_req_complete_iso(struct libusb_transfer *transfer)
}
if (xfer->ring->ep->pid == USB_TOKEN_IN) {
QTAILQ_INSERT_TAIL(&xfer->ring->copy, xfer, next);
+ usb_wakeup(xfer->ring->ep, 0);
} else {
QTAILQ_INSERT_TAIL(&xfer->ring->unused, xfer, next);
}
@@ -1239,7 +1242,7 @@ static void usb_host_handle_control(USBDevice *udev, USBPacket *p,
/* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices
* to work redirected to a not superspeed capable hcd */
- if (udev->speed == USB_SPEED_SUPER &&
+ if ((udev->speedmask & USB_SPEED_MASK_SUPER) &&
!(udev->port->speedmask & USB_SPEED_MASK_SUPER) &&
request == 0x8006 && value == 0x100 && index == 0) {
r->usb3ep0quirk = true;
@@ -1429,7 +1432,7 @@ static void usb_host_free_streams(USBDevice *udev, USBEndpoint **eps,
* still present in the first place. Attemping to contine where we
* left off is impossible.
*
- * What we are going to to to here is emulate a surprise removal of
+ * What we are going to do here is emulate a surprise removal of
* the usb device passed through, then kick host scan so the device
* will get re-attached (and re-initialized by the guest) in case it
* is still present.
diff --git a/qemu/hw/usb/host-stub.c b/qemu/hw/usb/host-stub.c
index 2eaaa8341..6ba65a1f6 100644
--- a/qemu/hw/usb/host-stub.c
+++ b/qemu/hw/usb/host-stub.c
@@ -30,6 +30,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "ui/console.h"
#include "hw/usb.h"
diff --git a/qemu/hw/usb/libhw.c b/qemu/hw/usb/libhw.c
index 8df11c461..73cdf0c97 100644
--- a/qemu/hw/usb/libhw.c
+++ b/qemu/hw/usb/libhw.c
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "hw/hw.h"
#include "hw/usb.h"
diff --git a/qemu/hw/usb/quirks.c b/qemu/hw/usb/quirks.c
index a761a9603..38a9c5634 100644
--- a/qemu/hw/usb/quirks.c
+++ b/qemu/hw/usb/quirks.c
@@ -12,6 +12,7 @@
* (at your option) any later version.
*/
+#include "qemu/osdep.h"
#include "quirks.h"
#include "hw/usb.h"
diff --git a/qemu/hw/usb/redirect.c b/qemu/hw/usb/redirect.c
index 25df25fd0..8d8054037 100644
--- a/qemu/hw/usb/redirect.c
+++ b/qemu/hw/usb/redirect.c
@@ -25,6 +25,8 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu-common.h"
#include "qemu/timer.h"
#include "sysemu/sysemu.h"
@@ -33,14 +35,14 @@
#include "qemu/iov.h"
#include "sysemu/char.h"
-#include <dirent.h>
-#include <sys/ioctl.h>
-#include <signal.h>
#include <usbredirparser.h>
#include <usbredirfilter.h>
#include "hw/usb.h"
+/* ERROR is defined below. Remove any previous definition. */
+#undef ERROR
+
#define MAX_ENDPOINTS 32
#define NO_INTERFACE_INFO 255 /* Valid interface_count always <= 32 */
#define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f))
@@ -324,7 +326,7 @@ static void packet_id_queue_add(struct PacketIdQueue *q, uint64_t id)
DPRINTF("adding packet id %"PRIu64" to %s queue\n", id, q->name);
- e = g_malloc0(sizeof(struct PacketIdQueueEntry));
+ e = g_new0(struct PacketIdQueueEntry, 1);
e->id = id;
QTAILQ_INSERT_TAIL(&q->head, e, next);
q->size++;
@@ -448,7 +450,7 @@ static USBPacket *usbredir_find_packet_by_id(USBRedirDevice *dev,
return p;
}
-static void bufp_alloc(USBRedirDevice *dev, uint8_t *data, uint16_t len,
+static int bufp_alloc(USBRedirDevice *dev, uint8_t *data, uint16_t len,
uint8_t status, uint8_t ep, void *free_on_destroy)
{
struct buf_packet *bufp;
@@ -465,12 +467,12 @@ static void bufp_alloc(USBRedirDevice *dev, uint8_t *data, uint16_t len,
if (dev->endpoint[EP2I(ep)].bufpq_size >
dev->endpoint[EP2I(ep)].bufpq_target_size) {
free(data);
- return;
+ return -1;
}
dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0;
}
- bufp = g_malloc(sizeof(struct buf_packet));
+ bufp = g_new(struct buf_packet, 1);
bufp->data = data;
bufp->len = len;
bufp->offset = 0;
@@ -478,6 +480,7 @@ static void bufp_alloc(USBRedirDevice *dev, uint8_t *data, uint16_t len,
bufp->free_on_destroy = free_on_destroy;
QTAILQ_INSERT_TAIL(&dev->endpoint[EP2I(ep)].bufpq, bufp, next);
dev->endpoint[EP2I(ep)].bufpq_size++;
+ return 0;
}
static void bufp_free(USBRedirDevice *dev, struct buf_packet *bufp,
@@ -2083,13 +2086,17 @@ static void usbredir_buffered_bulk_packet(void *priv, uint64_t id,
status = usb_redir_success;
free_on_destroy = NULL;
for (i = 0; i < data_len; i += len) {
+ int r;
if (len >= (data_len - i)) {
len = data_len - i;
status = buffered_bulk_packet->status;
free_on_destroy = data;
}
/* bufp_alloc also adds the packet to the ep queue */
- bufp_alloc(dev, data + i, len, status, ep, free_on_destroy);
+ r = bufp_alloc(dev, data + i, len, status, ep, free_on_destroy);
+ if (r) {
+ break;
+ }
}
if (dev->endpoint[EP2I(ep)].pending_async_packet) {
@@ -2236,7 +2243,7 @@ static int usbredir_get_bufpq(QEMUFile *f, void *priv, size_t unused)
endp->bufpq_size = qemu_get_be32(f);
for (i = 0; i < endp->bufpq_size; i++) {
- bufp = g_malloc(sizeof(struct buf_packet));
+ bufp = g_new(struct buf_packet, 1);
bufp->len = qemu_get_be32(f);
bufp->status = qemu_get_be32(f);
bufp->offset = 0;
diff --git a/qemu/hw/timer/tusb6010.c b/qemu/hw/usb/tusb6010.c
index 459c748e1..8f593a6fd 100644
--- a/qemu/hw/timer/tusb6010.c
+++ b/qemu/hw/usb/tusb6010.c
@@ -18,6 +18,7 @@
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/timer.h"
#include "hw/usb.h"
@@ -515,7 +516,7 @@ static void tusb_async_writew(void *opaque, hwaddr addr,
if (value & TUSB_DEV_OTG_TIMER_ENABLE)
timer_mod(s->otg_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
muldiv64(TUSB_DEV_OTG_TIMER_VAL(value),
- get_ticks_per_sec(), TUSB_DEVCLOCK));
+ NANOSECONDS_PER_SECOND, TUSB_DEVCLOCK));
else
timer_del(s->otg_timer);
break;
@@ -725,8 +726,8 @@ static void tusb6010_power(TUSBState *s, int on)
/* Pull the interrupt down after TUSB6010 comes up. */
s->intr_ok = 0;
tusb_intr_update(s);
- timer_mod(s->pwr_timer,
- qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + get_ticks_per_sec() / 2);
+ timer_mod(s->pwr_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ NANOSECONDS_PER_SECOND / 2);
}
}
diff --git a/qemu/hw/vfio/Makefile.objs b/qemu/hw/vfio/Makefile.objs
index d540c9d14..ceddbb8f9 100644
--- a/qemu/hw/vfio/Makefile.objs
+++ b/qemu/hw/vfio/Makefile.objs
@@ -1,6 +1,7 @@
ifeq ($(CONFIG_LINUX), y)
obj-$(CONFIG_SOFTMMU) += common.o
-obj-$(CONFIG_PCI) += pci.o
+obj-$(CONFIG_PCI) += pci.o pci-quirks.o
obj-$(CONFIG_SOFTMMU) += platform.o
obj-$(CONFIG_SOFTMMU) += calxeda-xgmac.o
+obj-$(CONFIG_SOFTMMU) += amd-xgbe.o
endif
diff --git a/qemu/hw/vfio/amd-xgbe.c b/qemu/hw/vfio/amd-xgbe.c
new file mode 100644
index 000000000..2c60310cf
--- /dev/null
+++ b/qemu/hw/vfio/amd-xgbe.c
@@ -0,0 +1,56 @@
+/*
+ * AMD XGBE VFIO device
+ *
+ * Copyright Linaro Limited, 2015
+ *
+ * Authors:
+ * Eric Auger <eric.auger@linaro.org>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "hw/vfio/vfio-amd-xgbe.h"
+
+static void amd_xgbe_realize(DeviceState *dev, Error **errp)
+{
+ VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(dev);
+ VFIOAmdXgbeDeviceClass *k = VFIO_AMD_XGBE_DEVICE_GET_CLASS(dev);
+
+ vdev->compat = g_strdup("amd,xgbe-seattle-v1a");
+
+ k->parent_realize(dev, errp);
+}
+
+static const VMStateDescription vfio_platform_amd_xgbe_vmstate = {
+ .name = TYPE_VFIO_AMD_XGBE,
+ .unmigratable = 1,
+};
+
+static void vfio_amd_xgbe_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VFIOAmdXgbeDeviceClass *vcxc =
+ VFIO_AMD_XGBE_DEVICE_CLASS(klass);
+ vcxc->parent_realize = dc->realize;
+ dc->realize = amd_xgbe_realize;
+ dc->desc = "VFIO AMD XGBE";
+ dc->vmsd = &vfio_platform_amd_xgbe_vmstate;
+}
+
+static const TypeInfo vfio_amd_xgbe_dev_info = {
+ .name = TYPE_VFIO_AMD_XGBE,
+ .parent = TYPE_VFIO_PLATFORM,
+ .instance_size = sizeof(VFIOAmdXgbeDevice),
+ .class_init = vfio_amd_xgbe_class_init,
+ .class_size = sizeof(VFIOAmdXgbeDeviceClass),
+};
+
+static void register_amd_xgbe_dev_type(void)
+{
+ type_register_static(&vfio_amd_xgbe_dev_info);
+}
+
+type_init(register_amd_xgbe_dev_type)
diff --git a/qemu/hw/vfio/calxeda-xgmac.c b/qemu/hw/vfio/calxeda-xgmac.c
index eb914f0d0..bb15d588e 100644
--- a/qemu/hw/vfio/calxeda-xgmac.c
+++ b/qemu/hw/vfio/calxeda-xgmac.c
@@ -11,6 +11,7 @@
*
*/
+#include "qemu/osdep.h"
#include "hw/vfio/vfio-calxeda-xgmac.h"
static void calxeda_xgmac_realize(DeviceState *dev, Error **errp)
diff --git a/qemu/hw/vfio/common.c b/qemu/hw/vfio/common.c
index 85ee9b005..f27db36fb 100644
--- a/qemu/hw/vfio/common.c
+++ b/qemu/hw/vfio/common.c
@@ -18,6 +18,7 @@
* Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@il.ibm.com)
*/
+#include "qemu/osdep.h"
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/vfio.h>
@@ -226,7 +227,7 @@ static int vfio_dma_map(VFIOContainer *container, hwaddr iova,
/*
* Try the mapping, if it fails with EBUSY, unmap the region and try
* again. This shouldn't be necessary, but we sometimes see it in
- * the the VGA ROM space.
+ * the VGA ROM space.
*/
if (ioctl(container->fd, VFIO_IOMMU_MAP_DMA, &map) == 0 ||
(errno == EBUSY && vfio_dma_unmap(container, iova, size) == 0 &&
@@ -312,13 +313,17 @@ out:
rcu_read_unlock();
}
+static hwaddr vfio_container_granularity(VFIOContainer *container)
+{
+ return (hwaddr)1 << ctz64(container->iova_pgsizes);
+}
+
static void vfio_listener_region_add(MemoryListener *listener,
MemoryRegionSection *section)
{
- VFIOContainer *container = container_of(listener, VFIOContainer,
- iommu_data.type1.listener);
+ VFIOContainer *container = container_of(listener, VFIOContainer, listener);
hwaddr iova, end;
- Int128 llend;
+ Int128 llend, llsize;
void *vaddr;
int ret;
@@ -344,14 +349,22 @@ static void vfio_listener_region_add(MemoryListener *listener,
if (int128_ge(int128_make64(iova), llend)) {
return;
}
+ end = int128_get64(int128_sub(llend, int128_one()));
+
+ if ((iova < container->min_iova) || (end > container->max_iova)) {
+ error_report("vfio: IOMMU container %p can't map guest IOVA region"
+ " 0x%"HWADDR_PRIx"..0x%"HWADDR_PRIx,
+ container, iova, end);
+ ret = -EFAULT;
+ goto fail;
+ }
memory_region_ref(section->mr);
if (memory_region_is_iommu(section->mr)) {
VFIOGuestIOMMU *giommu;
- trace_vfio_listener_region_add_iommu(iova,
- int128_get64(int128_sub(llend, int128_one())));
+ trace_vfio_listener_region_add_iommu(iova, end);
/*
* FIXME: We should do some checking to see if the
* capabilities of the host VFIO IOMMU are adequate to model
@@ -362,65 +375,60 @@ static void vfio_listener_region_add(MemoryListener *listener,
* would be the right place to wire that up (tell the KVM
* device emulation the VFIO iommu handles to use).
*/
- /*
- * This assumes that the guest IOMMU is empty of
- * mappings at this point.
- *
- * One way of doing this is:
- * 1. Avoid sharing IOMMUs between emulated devices or different
- * IOMMU groups.
- * 2. Implement VFIO_IOMMU_ENABLE in the host kernel to fail if
- * there are some mappings in IOMMU.
- *
- * VFIO on SPAPR does that. Other IOMMU models may do that different,
- * they must make sure there are no existing mappings or
- * loop through existing mappings to map them into VFIO.
- */
giommu = g_malloc0(sizeof(*giommu));
giommu->iommu = section->mr;
giommu->container = container;
giommu->n.notify = vfio_iommu_map_notify;
QLIST_INSERT_HEAD(&container->giommu_list, giommu, giommu_next);
+
memory_region_register_iommu_notifier(giommu->iommu, &giommu->n);
+ memory_region_iommu_replay(giommu->iommu, &giommu->n,
+ vfio_container_granularity(container),
+ false);
return;
}
/* Here we assume that memory_region_is_ram(section->mr)==true */
- end = int128_get64(llend);
vaddr = memory_region_get_ram_ptr(section->mr) +
section->offset_within_region +
(iova - section->offset_within_address_space);
- trace_vfio_listener_region_add_ram(iova, end - 1, vaddr);
+ trace_vfio_listener_region_add_ram(iova, end, vaddr);
+
+ llsize = int128_sub(llend, int128_make64(iova));
- ret = vfio_dma_map(container, iova, end - iova, vaddr, section->readonly);
+ ret = vfio_dma_map(container, iova, int128_get64(llsize),
+ vaddr, section->readonly);
if (ret) {
error_report("vfio_dma_map(%p, 0x%"HWADDR_PRIx", "
"0x%"HWADDR_PRIx", %p) = %d (%m)",
- container, iova, end - iova, vaddr, ret);
+ container, iova, int128_get64(llsize), vaddr, ret);
+ goto fail;
+ }
- /*
- * On the initfn path, store the first error in the container so we
- * can gracefully fail. Runtime, there's not much we can do other
- * than throw a hardware error.
- */
- if (!container->iommu_data.type1.initialized) {
- if (!container->iommu_data.type1.error) {
- container->iommu_data.type1.error = ret;
- }
- } else {
- hw_error("vfio: DMA mapping failed, unable to continue");
+ return;
+
+fail:
+ /*
+ * On the initfn path, store the first error in the container so we
+ * can gracefully fail. Runtime, there's not much we can do other
+ * than throw a hardware error.
+ */
+ if (!container->initialized) {
+ if (!container->error) {
+ container->error = ret;
}
+ } else {
+ hw_error("vfio: DMA mapping failed, unable to continue");
}
}
static void vfio_listener_region_del(MemoryListener *listener,
MemoryRegionSection *section)
{
- VFIOContainer *container = container_of(listener, VFIOContainer,
- iommu_data.type1.listener);
+ VFIOContainer *container = container_of(listener, VFIOContainer, listener);
hwaddr iova, end;
int ret;
@@ -485,49 +493,165 @@ static const MemoryListener vfio_memory_listener = {
static void vfio_listener_release(VFIOContainer *container)
{
- memory_listener_unregister(&container->iommu_data.type1.listener);
+ memory_listener_unregister(&container->listener);
}
-int vfio_mmap_region(Object *obj, VFIORegion *region,
- MemoryRegion *mem, MemoryRegion *submem,
- void **map, size_t size, off_t offset,
- const char *name)
+int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region,
+ int index, const char *name)
{
- int ret = 0;
- VFIODevice *vbasedev = region->vbasedev;
+ struct vfio_region_info *info;
+ int ret;
+
+ ret = vfio_get_region_info(vbasedev, index, &info);
+ if (ret) {
+ return ret;
+ }
- if (vbasedev->allow_mmap && size && region->flags &
- VFIO_REGION_INFO_FLAG_MMAP) {
- int prot = 0;
+ region->vbasedev = vbasedev;
+ region->flags = info->flags;
+ region->size = info->size;
+ region->fd_offset = info->offset;
+ region->nr = index;
- if (region->flags & VFIO_REGION_INFO_FLAG_READ) {
- prot |= PROT_READ;
+ if (region->size) {
+ region->mem = g_new0(MemoryRegion, 1);
+ memory_region_init_io(region->mem, obj, &vfio_region_ops,
+ region, name, region->size);
+
+ if (!vbasedev->no_mmap &&
+ region->flags & VFIO_REGION_INFO_FLAG_MMAP &&
+ !(region->size & ~qemu_real_host_page_mask)) {
+
+ region->nr_mmaps = 1;
+ region->mmaps = g_new0(VFIOMmap, region->nr_mmaps);
+
+ region->mmaps[0].offset = 0;
+ region->mmaps[0].size = region->size;
}
+ }
+
+ g_free(info);
+
+ trace_vfio_region_setup(vbasedev->name, index, name,
+ region->flags, region->fd_offset, region->size);
+ return 0;
+}
+
+int vfio_region_mmap(VFIORegion *region)
+{
+ int i, prot = 0;
+ char *name;
+
+ if (!region->mem) {
+ return 0;
+ }
- if (region->flags & VFIO_REGION_INFO_FLAG_WRITE) {
- prot |= PROT_WRITE;
+ prot |= region->flags & VFIO_REGION_INFO_FLAG_READ ? PROT_READ : 0;
+ prot |= region->flags & VFIO_REGION_INFO_FLAG_WRITE ? PROT_WRITE : 0;
+
+ for (i = 0; i < region->nr_mmaps; i++) {
+ region->mmaps[i].mmap = mmap(NULL, region->mmaps[i].size, prot,
+ MAP_SHARED, region->vbasedev->fd,
+ region->fd_offset +
+ region->mmaps[i].offset);
+ if (region->mmaps[i].mmap == MAP_FAILED) {
+ int ret = -errno;
+
+ trace_vfio_region_mmap_fault(memory_region_name(region->mem), i,
+ region->fd_offset +
+ region->mmaps[i].offset,
+ region->fd_offset +
+ region->mmaps[i].offset +
+ region->mmaps[i].size - 1, ret);
+
+ region->mmaps[i].mmap = NULL;
+
+ for (i--; i >= 0; i--) {
+ memory_region_del_subregion(region->mem, &region->mmaps[i].mem);
+ munmap(region->mmaps[i].mmap, region->mmaps[i].size);
+ object_unparent(OBJECT(&region->mmaps[i].mem));
+ region->mmaps[i].mmap = NULL;
+ }
+
+ return ret;
}
- *map = mmap(NULL, size, prot, MAP_SHARED,
- vbasedev->fd,
- region->fd_offset + offset);
- if (*map == MAP_FAILED) {
- *map = NULL;
- ret = -errno;
- goto empty_region;
+ name = g_strdup_printf("%s mmaps[%d]",
+ memory_region_name(region->mem), i);
+ memory_region_init_ram_ptr(&region->mmaps[i].mem,
+ memory_region_owner(region->mem),
+ name, region->mmaps[i].size,
+ region->mmaps[i].mmap);
+ g_free(name);
+ memory_region_set_skip_dump(&region->mmaps[i].mem);
+ memory_region_add_subregion(region->mem, region->mmaps[i].offset,
+ &region->mmaps[i].mem);
+
+ trace_vfio_region_mmap(memory_region_name(&region->mmaps[i].mem),
+ region->mmaps[i].offset,
+ region->mmaps[i].offset +
+ region->mmaps[i].size - 1);
+ }
+
+ return 0;
+}
+
+void vfio_region_exit(VFIORegion *region)
+{
+ int i;
+
+ if (!region->mem) {
+ return;
+ }
+
+ for (i = 0; i < region->nr_mmaps; i++) {
+ if (region->mmaps[i].mmap) {
+ memory_region_del_subregion(region->mem, &region->mmaps[i].mem);
}
+ }
- memory_region_init_ram_ptr(submem, obj, name, size, *map);
- memory_region_set_skip_dump(submem);
- } else {
-empty_region:
- /* Create a zero sized sub-region to make cleanup easy. */
- memory_region_init(submem, obj, name, 0);
+ trace_vfio_region_exit(region->vbasedev->name, region->nr);
+}
+
+void vfio_region_finalize(VFIORegion *region)
+{
+ int i;
+
+ if (!region->mem) {
+ return;
}
- memory_region_add_subregion(mem, offset, submem);
+ for (i = 0; i < region->nr_mmaps; i++) {
+ if (region->mmaps[i].mmap) {
+ munmap(region->mmaps[i].mmap, region->mmaps[i].size);
+ object_unparent(OBJECT(&region->mmaps[i].mem));
+ }
+ }
- return ret;
+ object_unparent(OBJECT(region->mem));
+
+ g_free(region->mem);
+ g_free(region->mmaps);
+
+ trace_vfio_region_finalize(region->vbasedev->name, region->nr);
+}
+
+void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled)
+{
+ int i;
+
+ if (!region->mem) {
+ return;
+ }
+
+ for (i = 0; i < region->nr_mmaps; i++) {
+ if (region->mmaps[i].mmap) {
+ memory_region_set_enabled(&region->mmaps[i].mem, enabled);
+ }
+ }
+
+ trace_vfio_region_mmaps_set_enabled(memory_region_name(region->mem),
+ enabled);
}
void vfio_reset_handler(void *opaque)
@@ -668,6 +792,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1_IOMMU) ||
ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1v2_IOMMU)) {
bool v2 = !!ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1v2_IOMMU);
+ struct vfio_iommu_type1_info info;
ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd);
if (ret) {
@@ -684,21 +809,27 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
goto free_container_exit;
}
- container->iommu_data.type1.listener = vfio_memory_listener;
- container->iommu_data.release = vfio_listener_release;
-
- memory_listener_register(&container->iommu_data.type1.listener,
- container->space->as);
-
- if (container->iommu_data.type1.error) {
- ret = container->iommu_data.type1.error;
- error_report("vfio: memory listener initialization failed for container");
- goto listener_release_exit;
+ /*
+ * FIXME: This assumes that a Type1 IOMMU can map any 64-bit
+ * IOVA whatsoever. That's not actually true, but the current
+ * kernel interface doesn't tell us what it can map, and the
+ * existing Type1 IOMMUs generally support any IOVA we're
+ * going to actually try in practice.
+ */
+ container->min_iova = 0;
+ container->max_iova = (hwaddr)-1;
+
+ /* Assume just 4K IOVA page size */
+ container->iova_pgsizes = 0x1000;
+ info.argsz = sizeof(info);
+ ret = ioctl(fd, VFIO_IOMMU_GET_INFO, &info);
+ /* Ignore errors */
+ if ((ret == 0) && (info.flags & VFIO_IOMMU_INFO_PGSIZES)) {
+ container->iova_pgsizes = info.iova_pgsizes;
}
-
- container->iommu_data.type1.initialized = true;
-
} else if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_IOMMU)) {
+ struct vfio_iommu_spapr_tce_info info;
+
ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd);
if (ret) {
error_report("vfio: failed to set group container: %m");
@@ -724,18 +855,41 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
goto free_container_exit;
}
- container->iommu_data.type1.listener = vfio_memory_listener;
- container->iommu_data.release = vfio_listener_release;
-
- memory_listener_register(&container->iommu_data.type1.listener,
- container->space->as);
+ /*
+ * This only considers the host IOMMU's 32-bit window. At
+ * some point we need to add support for the optional 64-bit
+ * window and dynamic windows
+ */
+ info.argsz = sizeof(info);
+ ret = ioctl(fd, VFIO_IOMMU_SPAPR_TCE_GET_INFO, &info);
+ if (ret) {
+ error_report("vfio: VFIO_IOMMU_SPAPR_TCE_GET_INFO failed: %m");
+ ret = -errno;
+ goto free_container_exit;
+ }
+ container->min_iova = info.dma32_window_start;
+ container->max_iova = container->min_iova + info.dma32_window_size - 1;
+ /* Assume just 4K IOVA pages for now */
+ container->iova_pgsizes = 0x1000;
} else {
error_report("vfio: No available IOMMU models");
ret = -EINVAL;
goto free_container_exit;
}
+ container->listener = vfio_memory_listener;
+
+ memory_listener_register(&container->listener, container->space->as);
+
+ if (container->error) {
+ ret = container->error;
+ error_report("vfio: memory listener initialization failed for container");
+ goto listener_release_exit;
+ }
+
+ container->initialized = true;
+
QLIST_INIT(&container->group_list);
QLIST_INSERT_HEAD(&space->containers, container, next);
@@ -774,9 +928,7 @@ static void vfio_disconnect_container(VFIOGroup *group)
VFIOAddressSpace *space = container->space;
VFIOGuestIOMMU *giommu, *tmp;
- if (container->iommu_data.release) {
- container->iommu_data.release(container);
- }
+ vfio_listener_release(container);
QLIST_REMOVE(container, next);
QLIST_FOREACH_SAFE(giommu, &container->giommu_list, giommu_next, tmp) {
@@ -926,47 +1078,115 @@ void vfio_put_base_device(VFIODevice *vbasedev)
close(vbasedev->fd);
}
-static int vfio_container_do_ioctl(AddressSpace *as, int32_t groupid,
- int req, void *param)
+int vfio_get_region_info(VFIODevice *vbasedev, int index,
+ struct vfio_region_info **info)
{
- VFIOGroup *group;
- VFIOContainer *container;
- int ret = -1;
+ size_t argsz = sizeof(struct vfio_region_info);
- group = vfio_get_group(groupid, as);
- if (!group) {
- error_report("vfio: group %d not registered", groupid);
- return ret;
+ *info = g_malloc0(argsz);
+
+ (*info)->index = index;
+ (*info)->argsz = argsz;
+
+ if (ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, *info)) {
+ g_free(*info);
+ return -errno;
}
- container = group->container;
- if (group->container) {
- ret = ioctl(container->fd, req, param);
- if (ret < 0) {
- error_report("vfio: failed to ioctl %d to container: ret=%d, %s",
- _IOC_NR(req) - VFIO_BASE, ret, strerror(errno));
- }
+ return 0;
+}
+
+/*
+ * Interfaces for IBM EEH (Enhanced Error Handling)
+ */
+static bool vfio_eeh_container_ok(VFIOContainer *container)
+{
+ /*
+ * As of 2016-03-04 (linux-4.5) the host kernel EEH/VFIO
+ * implementation is broken if there are multiple groups in a
+ * container. The hardware works in units of Partitionable
+ * Endpoints (== IOMMU groups) and the EEH operations naively
+ * iterate across all groups in the container, without any logic
+ * to make sure the groups have their state synchronized. For
+ * certain operations (ENABLE) that might be ok, until an error
+ * occurs, but for others (GET_STATE) it's clearly broken.
+ */
+
+ /*
+ * XXX Once fixed kernels exist, test for them here
+ */
+
+ if (QLIST_EMPTY(&container->group_list)) {
+ return false;
}
- vfio_put_group(group);
+ if (QLIST_NEXT(QLIST_FIRST(&container->group_list), container_next)) {
+ return false;
+ }
- return ret;
+ return true;
}
-int vfio_container_ioctl(AddressSpace *as, int32_t groupid,
- int req, void *param)
+static int vfio_eeh_container_op(VFIOContainer *container, uint32_t op)
{
- /* We allow only certain ioctls to the container */
- switch (req) {
- case VFIO_CHECK_EXTENSION:
- case VFIO_IOMMU_SPAPR_TCE_GET_INFO:
- case VFIO_EEH_PE_OP:
- break;
- default:
- /* Return an error on unknown requests */
- error_report("vfio: unsupported ioctl %X", req);
- return -1;
+ struct vfio_eeh_pe_op pe_op = {
+ .argsz = sizeof(pe_op),
+ .op = op,
+ };
+ int ret;
+
+ if (!vfio_eeh_container_ok(container)) {
+ error_report("vfio/eeh: EEH_PE_OP 0x%x: "
+ "kernel requires a container with exactly one group", op);
+ return -EPERM;
+ }
+
+ ret = ioctl(container->fd, VFIO_EEH_PE_OP, &pe_op);
+ if (ret < 0) {
+ error_report("vfio/eeh: EEH_PE_OP 0x%x failed: %m", op);
+ return -errno;
+ }
+
+ return 0;
+}
+
+static VFIOContainer *vfio_eeh_as_container(AddressSpace *as)
+{
+ VFIOAddressSpace *space = vfio_get_address_space(as);
+ VFIOContainer *container = NULL;
+
+ if (QLIST_EMPTY(&space->containers)) {
+ /* No containers to act on */
+ goto out;
}
- return vfio_container_do_ioctl(as, groupid, req, param);
+ container = QLIST_FIRST(&space->containers);
+
+ if (QLIST_NEXT(container, next)) {
+ /* We don't yet have logic to synchronize EEH state across
+ * multiple containers */
+ container = NULL;
+ goto out;
+ }
+
+out:
+ vfio_put_address_space(space);
+ return container;
+}
+
+bool vfio_eeh_as_ok(AddressSpace *as)
+{
+ VFIOContainer *container = vfio_eeh_as_container(as);
+
+ return (container != NULL) && vfio_eeh_container_ok(container);
+}
+
+int vfio_eeh_as_op(AddressSpace *as, uint32_t op)
+{
+ VFIOContainer *container = vfio_eeh_as_container(as);
+
+ if (!container) {
+ return -ENODEV;
+ }
+ return vfio_eeh_container_op(container, op);
}
diff --git a/qemu/hw/vfio/pci-quirks.c b/qemu/hw/vfio/pci-quirks.c
new file mode 100644
index 000000000..49ecf1172
--- /dev/null
+++ b/qemu/hw/vfio/pci-quirks.c
@@ -0,0 +1,1205 @@
+/*
+ * device quirks for PCI devices
+ *
+ * Copyright Red Hat, Inc. 2012-2015
+ *
+ * Authors:
+ * Alex Williamson <alex.williamson@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "pci.h"
+#include "trace.h"
+#include "qemu/range.h"
+
+/* Use uin32_t for vendor & device so PCI_ANY_ID expands and cannot match hw */
+static bool vfio_pci_is(VFIOPCIDevice *vdev, uint32_t vendor, uint32_t device)
+{
+ return (vendor == PCI_ANY_ID || vendor == vdev->vendor_id) &&
+ (device == PCI_ANY_ID || device == vdev->device_id);
+}
+
+static bool vfio_is_vga(VFIOPCIDevice *vdev)
+{
+ PCIDevice *pdev = &vdev->pdev;
+ uint16_t class = pci_get_word(pdev->config + PCI_CLASS_DEVICE);
+
+ return class == PCI_CLASS_DISPLAY_VGA;
+}
+
+/*
+ * List of device ids/vendor ids for which to disable
+ * option rom loading. This avoids the guest hangs during rom
+ * execution as noticed with the BCM 57810 card for lack of a
+ * more better way to handle such issues.
+ * The user can still override by specifying a romfile or
+ * rombar=1.
+ * Please see https://bugs.launchpad.net/qemu/+bug/1284874
+ * for an analysis of the 57810 card hang. When adding
+ * a new vendor id/device id combination below, please also add
+ * your card/environment details and information that could
+ * help in debugging to the bug tracking this issue
+ */
+static const struct {
+ uint32_t vendor;
+ uint32_t device;
+} romblacklist[] = {
+ { 0x14e4, 0x168e }, /* Broadcom BCM 57810 */
+};
+
+bool vfio_blacklist_opt_rom(VFIOPCIDevice *vdev)
+{
+ int i;
+
+ for (i = 0 ; i < ARRAY_SIZE(romblacklist); i++) {
+ if (vfio_pci_is(vdev, romblacklist[i].vendor, romblacklist[i].device)) {
+ trace_vfio_quirk_rom_blacklisted(vdev->vbasedev.name,
+ romblacklist[i].vendor,
+ romblacklist[i].device);
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ * Device specific region quirks (mostly backdoors to PCI config space)
+ */
+
+/*
+ * The generic window quirks operate on an address and data register,
+ * vfio_generic_window_address_quirk handles the address register and
+ * vfio_generic_window_data_quirk handles the data register. These ops
+ * pass reads and writes through to hardware until a value matching the
+ * stored address match/mask is written. When this occurs, the data
+ * register access emulated PCI config space for the device rather than
+ * passing through accesses. This enables devices where PCI config space
+ * is accessible behind a window register to maintain the virtualization
+ * provided through vfio.
+ */
+typedef struct VFIOConfigWindowMatch {
+ uint32_t match;
+ uint32_t mask;
+} VFIOConfigWindowMatch;
+
+typedef struct VFIOConfigWindowQuirk {
+ struct VFIOPCIDevice *vdev;
+
+ uint32_t address_val;
+
+ uint32_t address_offset;
+ uint32_t data_offset;
+
+ bool window_enabled;
+ uint8_t bar;
+
+ MemoryRegion *addr_mem;
+ MemoryRegion *data_mem;
+
+ uint32_t nr_matches;
+ VFIOConfigWindowMatch matches[];
+} VFIOConfigWindowQuirk;
+
+static uint64_t vfio_generic_window_quirk_address_read(void *opaque,
+ hwaddr addr,
+ unsigned size)
+{
+ VFIOConfigWindowQuirk *window = opaque;
+ VFIOPCIDevice *vdev = window->vdev;
+
+ return vfio_region_read(&vdev->bars[window->bar].region,
+ addr + window->address_offset, size);
+}
+
+static void vfio_generic_window_quirk_address_write(void *opaque, hwaddr addr,
+ uint64_t data,
+ unsigned size)
+{
+ VFIOConfigWindowQuirk *window = opaque;
+ VFIOPCIDevice *vdev = window->vdev;
+ int i;
+
+ window->window_enabled = false;
+
+ vfio_region_write(&vdev->bars[window->bar].region,
+ addr + window->address_offset, data, size);
+
+ for (i = 0; i < window->nr_matches; i++) {
+ if ((data & ~window->matches[i].mask) == window->matches[i].match) {
+ window->window_enabled = true;
+ window->address_val = data & window->matches[i].mask;
+ trace_vfio_quirk_generic_window_address_write(vdev->vbasedev.name,
+ memory_region_name(window->addr_mem), data);
+ break;
+ }
+ }
+}
+
+static const MemoryRegionOps vfio_generic_window_address_quirk = {
+ .read = vfio_generic_window_quirk_address_read,
+ .write = vfio_generic_window_quirk_address_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static uint64_t vfio_generic_window_quirk_data_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ VFIOConfigWindowQuirk *window = opaque;
+ VFIOPCIDevice *vdev = window->vdev;
+ uint64_t data;
+
+ /* Always read data reg, discard if window enabled */
+ data = vfio_region_read(&vdev->bars[window->bar].region,
+ addr + window->data_offset, size);
+
+ if (window->window_enabled) {
+ data = vfio_pci_read_config(&vdev->pdev, window->address_val, size);
+ trace_vfio_quirk_generic_window_data_read(vdev->vbasedev.name,
+ memory_region_name(window->data_mem), data);
+ }
+
+ return data;
+}
+
+static void vfio_generic_window_quirk_data_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ VFIOConfigWindowQuirk *window = opaque;
+ VFIOPCIDevice *vdev = window->vdev;
+
+ if (window->window_enabled) {
+ vfio_pci_write_config(&vdev->pdev, window->address_val, data, size);
+ trace_vfio_quirk_generic_window_data_write(vdev->vbasedev.name,
+ memory_region_name(window->data_mem), data);
+ return;
+ }
+
+ vfio_region_write(&vdev->bars[window->bar].region,
+ addr + window->data_offset, data, size);
+}
+
+static const MemoryRegionOps vfio_generic_window_data_quirk = {
+ .read = vfio_generic_window_quirk_data_read,
+ .write = vfio_generic_window_quirk_data_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+/*
+ * The generic mirror quirk handles devices which expose PCI config space
+ * through a region within a BAR. When enabled, reads and writes are
+ * redirected through to emulated PCI config space. XXX if PCI config space
+ * used memory regions, this could just be an alias.
+ */
+typedef struct VFIOConfigMirrorQuirk {
+ struct VFIOPCIDevice *vdev;
+ uint32_t offset;
+ uint8_t bar;
+ MemoryRegion *mem;
+} VFIOConfigMirrorQuirk;
+
+static uint64_t vfio_generic_quirk_mirror_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ VFIOConfigMirrorQuirk *mirror = opaque;
+ VFIOPCIDevice *vdev = mirror->vdev;
+ uint64_t data;
+
+ /* Read and discard in case the hardware cares */
+ (void)vfio_region_read(&vdev->bars[mirror->bar].region,
+ addr + mirror->offset, size);
+
+ data = vfio_pci_read_config(&vdev->pdev, addr, size);
+ trace_vfio_quirk_generic_mirror_read(vdev->vbasedev.name,
+ memory_region_name(mirror->mem),
+ addr, data);
+ return data;
+}
+
+static void vfio_generic_quirk_mirror_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ VFIOConfigMirrorQuirk *mirror = opaque;
+ VFIOPCIDevice *vdev = mirror->vdev;
+
+ vfio_pci_write_config(&vdev->pdev, addr, data, size);
+ trace_vfio_quirk_generic_mirror_write(vdev->vbasedev.name,
+ memory_region_name(mirror->mem),
+ addr, data);
+}
+
+static const MemoryRegionOps vfio_generic_mirror_quirk = {
+ .read = vfio_generic_quirk_mirror_read,
+ .write = vfio_generic_quirk_mirror_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+/* Is range1 fully contained within range2? */
+static bool vfio_range_contained(uint64_t first1, uint64_t len1,
+ uint64_t first2, uint64_t len2) {
+ return (first1 >= first2 && first1 + len1 <= first2 + len2);
+}
+
+#define PCI_VENDOR_ID_ATI 0x1002
+
+/*
+ * Radeon HD cards (HD5450 & HD7850) report the upper byte of the I/O port BAR
+ * through VGA register 0x3c3. On newer cards, the I/O port BAR is always
+ * BAR4 (older cards like the X550 used BAR1, but we don't care to support
+ * those). Note that on bare metal, a read of 0x3c3 doesn't always return the
+ * I/O port BAR address. Originally this was coded to return the virtual BAR
+ * address only if the physical register read returns the actual BAR address,
+ * but users have reported greater success if we return the virtual address
+ * unconditionally.
+ */
+static uint64_t vfio_ati_3c3_quirk_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ VFIOPCIDevice *vdev = opaque;
+ uint64_t data = vfio_pci_read_config(&vdev->pdev,
+ PCI_BASE_ADDRESS_4 + 1, size);
+
+ trace_vfio_quirk_ati_3c3_read(vdev->vbasedev.name, data);
+
+ return data;
+}
+
+static const MemoryRegionOps vfio_ati_3c3_quirk = {
+ .read = vfio_ati_3c3_quirk_read,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void vfio_vga_probe_ati_3c3_quirk(VFIOPCIDevice *vdev)
+{
+ VFIOQuirk *quirk;
+
+ /*
+ * As long as the BAR is >= 256 bytes it will be aligned such that the
+ * lower byte is always zero. Filter out anything else, if it exists.
+ */
+ if (!vfio_pci_is(vdev, PCI_VENDOR_ID_ATI, PCI_ANY_ID) ||
+ !vdev->bars[4].ioport || vdev->bars[4].region.size < 256) {
+ return;
+ }
+
+ quirk = g_malloc0(sizeof(*quirk));
+ quirk->mem = g_new0(MemoryRegion, 1);
+ quirk->nr_mem = 1;
+
+ memory_region_init_io(quirk->mem, OBJECT(vdev), &vfio_ati_3c3_quirk, vdev,
+ "vfio-ati-3c3-quirk", 1);
+ memory_region_add_subregion(&vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem,
+ 3 /* offset 3 bytes from 0x3c0 */, quirk->mem);
+
+ QLIST_INSERT_HEAD(&vdev->vga->region[QEMU_PCI_VGA_IO_HI].quirks,
+ quirk, next);
+
+ trace_vfio_quirk_ati_3c3_probe(vdev->vbasedev.name);
+}
+
+/*
+ * Newer ATI/AMD devices, including HD5450 and HD7850, have a mirror to PCI
+ * config space through MMIO BAR2 at offset 0x4000. Nothing seems to access
+ * the MMIO space directly, but a window to this space is provided through
+ * I/O port BAR4. Offset 0x0 is the address register and offset 0x4 is the
+ * data register. When the address is programmed to a range of 0x4000-0x4fff
+ * PCI configuration space is available. Experimentation seems to indicate
+ * that read-only may be provided by hardware.
+ */
+static void vfio_probe_ati_bar4_quirk(VFIOPCIDevice *vdev, int nr)
+{
+ VFIOQuirk *quirk;
+ VFIOConfigWindowQuirk *window;
+
+ /* This windows doesn't seem to be used except by legacy VGA code */
+ if (!vfio_pci_is(vdev, PCI_VENDOR_ID_ATI, PCI_ANY_ID) ||
+ !vdev->has_vga || nr != 4) {
+ return;
+ }
+
+ quirk = g_malloc0(sizeof(*quirk));
+ quirk->mem = g_new0(MemoryRegion, 2);
+ quirk->nr_mem = 2;
+ window = quirk->data = g_malloc0(sizeof(*window) +
+ sizeof(VFIOConfigWindowMatch));
+ window->vdev = vdev;
+ window->address_offset = 0;
+ window->data_offset = 4;
+ window->nr_matches = 1;
+ window->matches[0].match = 0x4000;
+ window->matches[0].mask = vdev->config_size - 1;
+ window->bar = nr;
+ window->addr_mem = &quirk->mem[0];
+ window->data_mem = &quirk->mem[1];
+
+ memory_region_init_io(window->addr_mem, OBJECT(vdev),
+ &vfio_generic_window_address_quirk, window,
+ "vfio-ati-bar4-window-address-quirk", 4);
+ memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
+ window->address_offset,
+ window->addr_mem, 1);
+
+ memory_region_init_io(window->data_mem, OBJECT(vdev),
+ &vfio_generic_window_data_quirk, window,
+ "vfio-ati-bar4-window-data-quirk", 4);
+ memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
+ window->data_offset,
+ window->data_mem, 1);
+
+ QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
+
+ trace_vfio_quirk_ati_bar4_probe(vdev->vbasedev.name);
+}
+
+/*
+ * Trap the BAR2 MMIO mirror to config space as well.
+ */
+static void vfio_probe_ati_bar2_quirk(VFIOPCIDevice *vdev, int nr)
+{
+ VFIOQuirk *quirk;
+ VFIOConfigMirrorQuirk *mirror;
+
+ /* Only enable on newer devices where BAR2 is 64bit */
+ if (!vfio_pci_is(vdev, PCI_VENDOR_ID_ATI, PCI_ANY_ID) ||
+ !vdev->has_vga || nr != 2 || !vdev->bars[2].mem64) {
+ return;
+ }
+
+ quirk = g_malloc0(sizeof(*quirk));
+ mirror = quirk->data = g_malloc0(sizeof(*mirror));
+ mirror->mem = quirk->mem = g_new0(MemoryRegion, 1);
+ quirk->nr_mem = 1;
+ mirror->vdev = vdev;
+ mirror->offset = 0x4000;
+ mirror->bar = nr;
+
+ memory_region_init_io(mirror->mem, OBJECT(vdev),
+ &vfio_generic_mirror_quirk, mirror,
+ "vfio-ati-bar2-4000-quirk", PCI_CONFIG_SPACE_SIZE);
+ memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
+ mirror->offset, mirror->mem, 1);
+
+ QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
+
+ trace_vfio_quirk_ati_bar2_probe(vdev->vbasedev.name);
+}
+
+/*
+ * Older ATI/AMD cards like the X550 have a similar window to that above.
+ * I/O port BAR1 provides a window to a mirror of PCI config space located
+ * in BAR2 at offset 0xf00. We don't care to support such older cards, but
+ * note it for future reference.
+ */
+
+#define PCI_VENDOR_ID_NVIDIA 0x10de
+
+/*
+ * Nvidia has several different methods to get to config space, the
+ * nouveu project has several of these documented here:
+ * https://github.com/pathscale/envytools/tree/master/hwdocs
+ *
+ * The first quirk is actually not documented in envytools and is found
+ * on 10de:01d1 (NVIDIA Corporation G72 [GeForce 7300 LE]). This is an
+ * NV46 chipset. The backdoor uses the legacy VGA I/O ports to access
+ * the mirror of PCI config space found at BAR0 offset 0x1800. The access
+ * sequence first writes 0x338 to I/O port 0x3d4. The target offset is
+ * then written to 0x3d0. Finally 0x538 is written for a read and 0x738
+ * is written for a write to 0x3d4. The BAR0 offset is then accessible
+ * through 0x3d0. This quirk doesn't seem to be necessary on newer cards
+ * that use the I/O port BAR5 window but it doesn't hurt to leave it.
+ */
+typedef enum {NONE = 0, SELECT, WINDOW, READ, WRITE} VFIONvidia3d0State;
+static const char *nv3d0_states[] = { "NONE", "SELECT",
+ "WINDOW", "READ", "WRITE" };
+
+typedef struct VFIONvidia3d0Quirk {
+ VFIOPCIDevice *vdev;
+ VFIONvidia3d0State state;
+ uint32_t offset;
+} VFIONvidia3d0Quirk;
+
+static uint64_t vfio_nvidia_3d4_quirk_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ VFIONvidia3d0Quirk *quirk = opaque;
+ VFIOPCIDevice *vdev = quirk->vdev;
+
+ quirk->state = NONE;
+
+ return vfio_vga_read(&vdev->vga->region[QEMU_PCI_VGA_IO_HI],
+ addr + 0x14, size);
+}
+
+static void vfio_nvidia_3d4_quirk_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ VFIONvidia3d0Quirk *quirk = opaque;
+ VFIOPCIDevice *vdev = quirk->vdev;
+ VFIONvidia3d0State old_state = quirk->state;
+
+ quirk->state = NONE;
+
+ switch (data) {
+ case 0x338:
+ if (old_state == NONE) {
+ quirk->state = SELECT;
+ trace_vfio_quirk_nvidia_3d0_state(vdev->vbasedev.name,
+ nv3d0_states[quirk->state]);
+ }
+ break;
+ case 0x538:
+ if (old_state == WINDOW) {
+ quirk->state = READ;
+ trace_vfio_quirk_nvidia_3d0_state(vdev->vbasedev.name,
+ nv3d0_states[quirk->state]);
+ }
+ break;
+ case 0x738:
+ if (old_state == WINDOW) {
+ quirk->state = WRITE;
+ trace_vfio_quirk_nvidia_3d0_state(vdev->vbasedev.name,
+ nv3d0_states[quirk->state]);
+ }
+ break;
+ }
+
+ vfio_vga_write(&vdev->vga->region[QEMU_PCI_VGA_IO_HI],
+ addr + 0x14, data, size);
+}
+
+static const MemoryRegionOps vfio_nvidia_3d4_quirk = {
+ .read = vfio_nvidia_3d4_quirk_read,
+ .write = vfio_nvidia_3d4_quirk_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static uint64_t vfio_nvidia_3d0_quirk_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ VFIONvidia3d0Quirk *quirk = opaque;
+ VFIOPCIDevice *vdev = quirk->vdev;
+ VFIONvidia3d0State old_state = quirk->state;
+ uint64_t data = vfio_vga_read(&vdev->vga->region[QEMU_PCI_VGA_IO_HI],
+ addr + 0x10, size);
+
+ quirk->state = NONE;
+
+ if (old_state == READ &&
+ (quirk->offset & ~(PCI_CONFIG_SPACE_SIZE - 1)) == 0x1800) {
+ uint8_t offset = quirk->offset & (PCI_CONFIG_SPACE_SIZE - 1);
+
+ data = vfio_pci_read_config(&vdev->pdev, offset, size);
+ trace_vfio_quirk_nvidia_3d0_read(vdev->vbasedev.name,
+ offset, size, data);
+ }
+
+ return data;
+}
+
+static void vfio_nvidia_3d0_quirk_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ VFIONvidia3d0Quirk *quirk = opaque;
+ VFIOPCIDevice *vdev = quirk->vdev;
+ VFIONvidia3d0State old_state = quirk->state;
+
+ quirk->state = NONE;
+
+ if (old_state == SELECT) {
+ quirk->offset = (uint32_t)data;
+ quirk->state = WINDOW;
+ trace_vfio_quirk_nvidia_3d0_state(vdev->vbasedev.name,
+ nv3d0_states[quirk->state]);
+ } else if (old_state == WRITE) {
+ if ((quirk->offset & ~(PCI_CONFIG_SPACE_SIZE - 1)) == 0x1800) {
+ uint8_t offset = quirk->offset & (PCI_CONFIG_SPACE_SIZE - 1);
+
+ vfio_pci_write_config(&vdev->pdev, offset, data, size);
+ trace_vfio_quirk_nvidia_3d0_write(vdev->vbasedev.name,
+ offset, data, size);
+ return;
+ }
+ }
+
+ vfio_vga_write(&vdev->vga->region[QEMU_PCI_VGA_IO_HI],
+ addr + 0x10, data, size);
+}
+
+static const MemoryRegionOps vfio_nvidia_3d0_quirk = {
+ .read = vfio_nvidia_3d0_quirk_read,
+ .write = vfio_nvidia_3d0_quirk_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void vfio_vga_probe_nvidia_3d0_quirk(VFIOPCIDevice *vdev)
+{
+ VFIOQuirk *quirk;
+ VFIONvidia3d0Quirk *data;
+
+ if (!vfio_pci_is(vdev, PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID) ||
+ !vdev->bars[1].region.size) {
+ return;
+ }
+
+ quirk = g_malloc0(sizeof(*quirk));
+ quirk->data = data = g_malloc0(sizeof(*data));
+ quirk->mem = g_new0(MemoryRegion, 2);
+ quirk->nr_mem = 2;
+ data->vdev = vdev;
+
+ memory_region_init_io(&quirk->mem[0], OBJECT(vdev), &vfio_nvidia_3d4_quirk,
+ data, "vfio-nvidia-3d4-quirk", 2);
+ memory_region_add_subregion(&vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem,
+ 0x14 /* 0x3c0 + 0x14 */, &quirk->mem[0]);
+
+ memory_region_init_io(&quirk->mem[1], OBJECT(vdev), &vfio_nvidia_3d0_quirk,
+ data, "vfio-nvidia-3d0-quirk", 2);
+ memory_region_add_subregion(&vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem,
+ 0x10 /* 0x3c0 + 0x10 */, &quirk->mem[1]);
+
+ QLIST_INSERT_HEAD(&vdev->vga->region[QEMU_PCI_VGA_IO_HI].quirks,
+ quirk, next);
+
+ trace_vfio_quirk_nvidia_3d0_probe(vdev->vbasedev.name);
+}
+
+/*
+ * The second quirk is documented in envytools. The I/O port BAR5 is just
+ * a set of address/data ports to the MMIO BARs. The BAR we care about is
+ * again BAR0. This backdoor is apparently a bit newer than the one above
+ * so we need to not only trap 256 bytes @0x1800, but all of PCI config
+ * space, including extended space is available at the 4k @0x88000.
+ */
+typedef struct VFIONvidiaBAR5Quirk {
+ uint32_t master;
+ uint32_t enable;
+ MemoryRegion *addr_mem;
+ MemoryRegion *data_mem;
+ bool enabled;
+ VFIOConfigWindowQuirk window; /* last for match data */
+} VFIONvidiaBAR5Quirk;
+
+static void vfio_nvidia_bar5_enable(VFIONvidiaBAR5Quirk *bar5)
+{
+ VFIOPCIDevice *vdev = bar5->window.vdev;
+
+ if (((bar5->master & bar5->enable) & 0x1) == bar5->enabled) {
+ return;
+ }
+
+ bar5->enabled = !bar5->enabled;
+ trace_vfio_quirk_nvidia_bar5_state(vdev->vbasedev.name,
+ bar5->enabled ? "Enable" : "Disable");
+ memory_region_set_enabled(bar5->addr_mem, bar5->enabled);
+ memory_region_set_enabled(bar5->data_mem, bar5->enabled);
+}
+
+static uint64_t vfio_nvidia_bar5_quirk_master_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ VFIONvidiaBAR5Quirk *bar5 = opaque;
+ VFIOPCIDevice *vdev = bar5->window.vdev;
+
+ return vfio_region_read(&vdev->bars[5].region, addr, size);
+}
+
+static void vfio_nvidia_bar5_quirk_master_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ VFIONvidiaBAR5Quirk *bar5 = opaque;
+ VFIOPCIDevice *vdev = bar5->window.vdev;
+
+ vfio_region_write(&vdev->bars[5].region, addr, data, size);
+
+ bar5->master = data;
+ vfio_nvidia_bar5_enable(bar5);
+}
+
+static const MemoryRegionOps vfio_nvidia_bar5_quirk_master = {
+ .read = vfio_nvidia_bar5_quirk_master_read,
+ .write = vfio_nvidia_bar5_quirk_master_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static uint64_t vfio_nvidia_bar5_quirk_enable_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ VFIONvidiaBAR5Quirk *bar5 = opaque;
+ VFIOPCIDevice *vdev = bar5->window.vdev;
+
+ return vfio_region_read(&vdev->bars[5].region, addr + 4, size);
+}
+
+static void vfio_nvidia_bar5_quirk_enable_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ VFIONvidiaBAR5Quirk *bar5 = opaque;
+ VFIOPCIDevice *vdev = bar5->window.vdev;
+
+ vfio_region_write(&vdev->bars[5].region, addr + 4, data, size);
+
+ bar5->enable = data;
+ vfio_nvidia_bar5_enable(bar5);
+}
+
+static const MemoryRegionOps vfio_nvidia_bar5_quirk_enable = {
+ .read = vfio_nvidia_bar5_quirk_enable_read,
+ .write = vfio_nvidia_bar5_quirk_enable_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void vfio_probe_nvidia_bar5_quirk(VFIOPCIDevice *vdev, int nr)
+{
+ VFIOQuirk *quirk;
+ VFIONvidiaBAR5Quirk *bar5;
+ VFIOConfigWindowQuirk *window;
+
+ if (!vfio_pci_is(vdev, PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID) ||
+ !vdev->has_vga || nr != 5) {
+ return;
+ }
+
+ quirk = g_malloc0(sizeof(*quirk));
+ quirk->mem = g_new0(MemoryRegion, 4);
+ quirk->nr_mem = 4;
+ bar5 = quirk->data = g_malloc0(sizeof(*bar5) +
+ (sizeof(VFIOConfigWindowMatch) * 2));
+ window = &bar5->window;
+
+ window->vdev = vdev;
+ window->address_offset = 0x8;
+ window->data_offset = 0xc;
+ window->nr_matches = 2;
+ window->matches[0].match = 0x1800;
+ window->matches[0].mask = PCI_CONFIG_SPACE_SIZE - 1;
+ window->matches[1].match = 0x88000;
+ window->matches[1].mask = vdev->config_size - 1;
+ window->bar = nr;
+ window->addr_mem = bar5->addr_mem = &quirk->mem[0];
+ window->data_mem = bar5->data_mem = &quirk->mem[1];
+
+ memory_region_init_io(window->addr_mem, OBJECT(vdev),
+ &vfio_generic_window_address_quirk, window,
+ "vfio-nvidia-bar5-window-address-quirk", 4);
+ memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
+ window->address_offset,
+ window->addr_mem, 1);
+ memory_region_set_enabled(window->addr_mem, false);
+
+ memory_region_init_io(window->data_mem, OBJECT(vdev),
+ &vfio_generic_window_data_quirk, window,
+ "vfio-nvidia-bar5-window-data-quirk", 4);
+ memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
+ window->data_offset,
+ window->data_mem, 1);
+ memory_region_set_enabled(window->data_mem, false);
+
+ memory_region_init_io(&quirk->mem[2], OBJECT(vdev),
+ &vfio_nvidia_bar5_quirk_master, bar5,
+ "vfio-nvidia-bar5-master-quirk", 4);
+ memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
+ 0, &quirk->mem[2], 1);
+
+ memory_region_init_io(&quirk->mem[3], OBJECT(vdev),
+ &vfio_nvidia_bar5_quirk_enable, bar5,
+ "vfio-nvidia-bar5-enable-quirk", 4);
+ memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
+ 4, &quirk->mem[3], 1);
+
+ QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
+
+ trace_vfio_quirk_nvidia_bar5_probe(vdev->vbasedev.name);
+}
+
+/*
+ * Finally, BAR0 itself. We want to redirect any accesses to either
+ * 0x1800 or 0x88000 through the PCI config space access functions.
+ */
+static void vfio_nvidia_quirk_mirror_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ VFIOConfigMirrorQuirk *mirror = opaque;
+ VFIOPCIDevice *vdev = mirror->vdev;
+ PCIDevice *pdev = &vdev->pdev;
+
+ vfio_generic_quirk_mirror_write(opaque, addr, data, size);
+
+ /*
+ * Nvidia seems to acknowledge MSI interrupts by writing 0xff to the
+ * MSI capability ID register. Both the ID and next register are
+ * read-only, so we allow writes covering either of those to real hw.
+ */
+ if ((pdev->cap_present & QEMU_PCI_CAP_MSI) &&
+ vfio_range_contained(addr, size, pdev->msi_cap, PCI_MSI_FLAGS)) {
+ vfio_region_write(&vdev->bars[mirror->bar].region,
+ addr + mirror->offset, data, size);
+ trace_vfio_quirk_nvidia_bar0_msi_ack(vdev->vbasedev.name);
+ }
+}
+
+static const MemoryRegionOps vfio_nvidia_mirror_quirk = {
+ .read = vfio_generic_quirk_mirror_read,
+ .write = vfio_nvidia_quirk_mirror_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void vfio_probe_nvidia_bar0_quirk(VFIOPCIDevice *vdev, int nr)
+{
+ VFIOQuirk *quirk;
+ VFIOConfigMirrorQuirk *mirror;
+
+ if (!vfio_pci_is(vdev, PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID) ||
+ !vfio_is_vga(vdev) || nr != 0) {
+ return;
+ }
+
+ quirk = g_malloc0(sizeof(*quirk));
+ mirror = quirk->data = g_malloc0(sizeof(*mirror));
+ mirror->mem = quirk->mem = g_new0(MemoryRegion, 1);
+ quirk->nr_mem = 1;
+ mirror->vdev = vdev;
+ mirror->offset = 0x88000;
+ mirror->bar = nr;
+
+ memory_region_init_io(mirror->mem, OBJECT(vdev),
+ &vfio_nvidia_mirror_quirk, mirror,
+ "vfio-nvidia-bar0-88000-mirror-quirk",
+ vdev->config_size);
+ memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
+ mirror->offset, mirror->mem, 1);
+
+ QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
+
+ /* The 0x1800 offset mirror only seems to get used by legacy VGA */
+ if (vdev->has_vga) {
+ quirk = g_malloc0(sizeof(*quirk));
+ mirror = quirk->data = g_malloc0(sizeof(*mirror));
+ mirror->mem = quirk->mem = g_new0(MemoryRegion, 1);
+ quirk->nr_mem = 1;
+ mirror->vdev = vdev;
+ mirror->offset = 0x1800;
+ mirror->bar = nr;
+
+ memory_region_init_io(mirror->mem, OBJECT(vdev),
+ &vfio_nvidia_mirror_quirk, mirror,
+ "vfio-nvidia-bar0-1800-mirror-quirk",
+ PCI_CONFIG_SPACE_SIZE);
+ memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
+ mirror->offset, mirror->mem, 1);
+
+ QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
+ }
+
+ trace_vfio_quirk_nvidia_bar0_probe(vdev->vbasedev.name);
+}
+
+/*
+ * TODO - Some Nvidia devices provide config access to their companion HDA
+ * device and even to their parent bridge via these config space mirrors.
+ * Add quirks for those regions.
+ */
+
+#define PCI_VENDOR_ID_REALTEK 0x10ec
+
+/*
+ * RTL8168 devices have a backdoor that can access the MSI-X table. At BAR2
+ * offset 0x70 there is a dword data register, offset 0x74 is a dword address
+ * register. According to the Linux r8169 driver, the MSI-X table is addressed
+ * when the "type" portion of the address register is set to 0x1. This appears
+ * to be bits 16:30. Bit 31 is both a write indicator and some sort of
+ * "address latched" indicator. Bits 12:15 are a mask field, which we can
+ * ignore because the MSI-X table should always be accessed as a dword (full
+ * mask). Bits 0:11 is offset within the type.
+ *
+ * Example trace:
+ *
+ * Read from MSI-X table offset 0
+ * vfio: vfio_bar_write(0000:05:00.0:BAR2+0x74, 0x1f000, 4) // store read addr
+ * vfio: vfio_bar_read(0000:05:00.0:BAR2+0x74, 4) = 0x8001f000 // latch
+ * vfio: vfio_bar_read(0000:05:00.0:BAR2+0x70, 4) = 0xfee00398 // read data
+ *
+ * Write 0xfee00000 to MSI-X table offset 0
+ * vfio: vfio_bar_write(0000:05:00.0:BAR2+0x70, 0xfee00000, 4) // write data
+ * vfio: vfio_bar_write(0000:05:00.0:BAR2+0x74, 0x8001f000, 4) // do write
+ * vfio: vfio_bar_read(0000:05:00.0:BAR2+0x74, 4) = 0x1f000 // complete
+ */
+typedef struct VFIOrtl8168Quirk {
+ VFIOPCIDevice *vdev;
+ uint32_t addr;
+ uint32_t data;
+ bool enabled;
+} VFIOrtl8168Quirk;
+
+static uint64_t vfio_rtl8168_quirk_address_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ VFIOrtl8168Quirk *rtl = opaque;
+ VFIOPCIDevice *vdev = rtl->vdev;
+ uint64_t data = vfio_region_read(&vdev->bars[2].region, addr + 0x74, size);
+
+ if (rtl->enabled) {
+ data = rtl->addr ^ 0x80000000U; /* latch/complete */
+ trace_vfio_quirk_rtl8168_fake_latch(vdev->vbasedev.name, data);
+ }
+
+ return data;
+}
+
+static void vfio_rtl8168_quirk_address_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ VFIOrtl8168Quirk *rtl = opaque;
+ VFIOPCIDevice *vdev = rtl->vdev;
+
+ rtl->enabled = false;
+
+ if ((data & 0x7fff0000) == 0x10000) { /* MSI-X table */
+ rtl->enabled = true;
+ rtl->addr = (uint32_t)data;
+
+ if (data & 0x80000000U) { /* Do write */
+ if (vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX) {
+ hwaddr offset = data & 0xfff;
+ uint64_t val = rtl->data;
+
+ trace_vfio_quirk_rtl8168_msix_write(vdev->vbasedev.name,
+ (uint16_t)offset, val);
+
+ /* Write to the proper guest MSI-X table instead */
+ memory_region_dispatch_write(&vdev->pdev.msix_table_mmio,
+ offset, val, size,
+ MEMTXATTRS_UNSPECIFIED);
+ }
+ return; /* Do not write guest MSI-X data to hardware */
+ }
+ }
+
+ vfio_region_write(&vdev->bars[2].region, addr + 0x74, data, size);
+}
+
+static const MemoryRegionOps vfio_rtl_address_quirk = {
+ .read = vfio_rtl8168_quirk_address_read,
+ .write = vfio_rtl8168_quirk_address_write,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false,
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static uint64_t vfio_rtl8168_quirk_data_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ VFIOrtl8168Quirk *rtl = opaque;
+ VFIOPCIDevice *vdev = rtl->vdev;
+ uint64_t data = vfio_region_read(&vdev->bars[2].region, addr + 0x74, size);
+
+ if (rtl->enabled && (vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX)) {
+ hwaddr offset = rtl->addr & 0xfff;
+ memory_region_dispatch_read(&vdev->pdev.msix_table_mmio, offset,
+ &data, size, MEMTXATTRS_UNSPECIFIED);
+ trace_vfio_quirk_rtl8168_msix_read(vdev->vbasedev.name, offset, data);
+ }
+
+ return data;
+}
+
+static void vfio_rtl8168_quirk_data_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ VFIOrtl8168Quirk *rtl = opaque;
+ VFIOPCIDevice *vdev = rtl->vdev;
+
+ rtl->data = (uint32_t)data;
+
+ vfio_region_write(&vdev->bars[2].region, addr + 0x70, data, size);
+}
+
+static const MemoryRegionOps vfio_rtl_data_quirk = {
+ .read = vfio_rtl8168_quirk_data_read,
+ .write = vfio_rtl8168_quirk_data_write,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false,
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void vfio_probe_rtl8168_bar2_quirk(VFIOPCIDevice *vdev, int nr)
+{
+ VFIOQuirk *quirk;
+ VFIOrtl8168Quirk *rtl;
+
+ if (!vfio_pci_is(vdev, PCI_VENDOR_ID_REALTEK, 0x8168) || nr != 2) {
+ return;
+ }
+
+ quirk = g_malloc0(sizeof(*quirk));
+ quirk->mem = g_new0(MemoryRegion, 2);
+ quirk->nr_mem = 2;
+ quirk->data = rtl = g_malloc0(sizeof(*rtl));
+ rtl->vdev = vdev;
+
+ memory_region_init_io(&quirk->mem[0], OBJECT(vdev),
+ &vfio_rtl_address_quirk, rtl,
+ "vfio-rtl8168-window-address-quirk", 4);
+ memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
+ 0x74, &quirk->mem[0], 1);
+
+ memory_region_init_io(&quirk->mem[1], OBJECT(vdev),
+ &vfio_rtl_data_quirk, rtl,
+ "vfio-rtl8168-window-data-quirk", 4);
+ memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
+ 0x70, &quirk->mem[1], 1);
+
+ QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
+
+ trace_vfio_quirk_rtl8168_probe(vdev->vbasedev.name);
+}
+
+/*
+ * Common quirk probe entry points.
+ */
+void vfio_vga_quirk_setup(VFIOPCIDevice *vdev)
+{
+ vfio_vga_probe_ati_3c3_quirk(vdev);
+ vfio_vga_probe_nvidia_3d0_quirk(vdev);
+}
+
+void vfio_vga_quirk_exit(VFIOPCIDevice *vdev)
+{
+ VFIOQuirk *quirk;
+ int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(vdev->vga->region); i++) {
+ QLIST_FOREACH(quirk, &vdev->vga->region[i].quirks, next) {
+ for (j = 0; j < quirk->nr_mem; j++) {
+ memory_region_del_subregion(&vdev->vga->region[i].mem,
+ &quirk->mem[j]);
+ }
+ }
+ }
+}
+
+void vfio_vga_quirk_finalize(VFIOPCIDevice *vdev)
+{
+ int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(vdev->vga->region); i++) {
+ while (!QLIST_EMPTY(&vdev->vga->region[i].quirks)) {
+ VFIOQuirk *quirk = QLIST_FIRST(&vdev->vga->region[i].quirks);
+ QLIST_REMOVE(quirk, next);
+ for (j = 0; j < quirk->nr_mem; j++) {
+ object_unparent(OBJECT(&quirk->mem[j]));
+ }
+ g_free(quirk->mem);
+ g_free(quirk->data);
+ g_free(quirk);
+ }
+ }
+}
+
+void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr)
+{
+ vfio_probe_ati_bar4_quirk(vdev, nr);
+ vfio_probe_ati_bar2_quirk(vdev, nr);
+ vfio_probe_nvidia_bar5_quirk(vdev, nr);
+ vfio_probe_nvidia_bar0_quirk(vdev, nr);
+ vfio_probe_rtl8168_bar2_quirk(vdev, nr);
+}
+
+void vfio_bar_quirk_exit(VFIOPCIDevice *vdev, int nr)
+{
+ VFIOBAR *bar = &vdev->bars[nr];
+ VFIOQuirk *quirk;
+ int i;
+
+ QLIST_FOREACH(quirk, &bar->quirks, next) {
+ for (i = 0; i < quirk->nr_mem; i++) {
+ memory_region_del_subregion(bar->region.mem, &quirk->mem[i]);
+ }
+ }
+}
+
+void vfio_bar_quirk_finalize(VFIOPCIDevice *vdev, int nr)
+{
+ VFIOBAR *bar = &vdev->bars[nr];
+ int i;
+
+ while (!QLIST_EMPTY(&bar->quirks)) {
+ VFIOQuirk *quirk = QLIST_FIRST(&bar->quirks);
+ QLIST_REMOVE(quirk, next);
+ for (i = 0; i < quirk->nr_mem; i++) {
+ object_unparent(OBJECT(&quirk->mem[i]));
+ }
+ g_free(quirk->mem);
+ g_free(quirk->data);
+ g_free(quirk);
+ }
+}
+
+/*
+ * Reset quirks
+ */
+
+/*
+ * AMD Radeon PCI config reset, based on Linux:
+ * drivers/gpu/drm/radeon/ci_smc.c:ci_is_smc_running()
+ * drivers/gpu/drm/radeon/radeon_device.c:radeon_pci_config_reset
+ * drivers/gpu/drm/radeon/ci_smc.c:ci_reset_smc()
+ * drivers/gpu/drm/radeon/ci_smc.c:ci_stop_smc_clock()
+ * IDs: include/drm/drm_pciids.h
+ * Registers: http://cgit.freedesktop.org/~agd5f/linux/commit/?id=4e2aa447f6f0
+ *
+ * Bonaire and Hawaii GPUs do not respond to a bus reset. This is a bug in the
+ * hardware that should be fixed on future ASICs. The symptom of this is that
+ * once the accerlated driver loads, Windows guests will bsod on subsequent
+ * attmpts to load the driver, such as after VM reset or shutdown/restart. To
+ * work around this, we do an AMD specific PCI config reset, followed by an SMC
+ * reset. The PCI config reset only works if SMC firmware is running, so we
+ * have a dependency on the state of the device as to whether this reset will
+ * be effective. There are still cases where we won't be able to kick the
+ * device into working, but this greatly improves the usability overall. The
+ * config reset magic is relatively common on AMD GPUs, but the setup and SMC
+ * poking is largely ASIC specific.
+ */
+static bool vfio_radeon_smc_is_running(VFIOPCIDevice *vdev)
+{
+ uint32_t clk, pc_c;
+
+ /*
+ * Registers 200h and 204h are index and data registers for accessing
+ * indirect configuration registers within the device.
+ */
+ vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000004, 4);
+ clk = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
+ vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000370, 4);
+ pc_c = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
+
+ return (!(clk & 1) && (0x20100 <= pc_c));
+}
+
+/*
+ * The scope of a config reset is controlled by a mode bit in the misc register
+ * and a fuse, exposed as a bit in another register. The fuse is the default
+ * (0 = GFX, 1 = whole GPU), the misc bit is a toggle, with the forumula
+ * scope = !(misc ^ fuse), where the resulting scope is defined the same as
+ * the fuse. A truth table therefore tells us that if misc == fuse, we need
+ * to flip the value of the bit in the misc register.
+ */
+static void vfio_radeon_set_gfx_only_reset(VFIOPCIDevice *vdev)
+{
+ uint32_t misc, fuse;
+ bool a, b;
+
+ vfio_region_write(&vdev->bars[5].region, 0x200, 0xc00c0000, 4);
+ fuse = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
+ b = fuse & 64;
+
+ vfio_region_write(&vdev->bars[5].region, 0x200, 0xc0000010, 4);
+ misc = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
+ a = misc & 2;
+
+ if (a == b) {
+ vfio_region_write(&vdev->bars[5].region, 0x204, misc ^ 2, 4);
+ vfio_region_read(&vdev->bars[5].region, 0x204, 4); /* flush */
+ }
+}
+
+static int vfio_radeon_reset(VFIOPCIDevice *vdev)
+{
+ PCIDevice *pdev = &vdev->pdev;
+ int i, ret = 0;
+ uint32_t data;
+
+ /* Defer to a kernel implemented reset */
+ if (vdev->vbasedev.reset_works) {
+ trace_vfio_quirk_ati_bonaire_reset_skipped(vdev->vbasedev.name);
+ return -ENODEV;
+ }
+
+ /* Enable only memory BAR access */
+ vfio_pci_write_config(pdev, PCI_COMMAND, PCI_COMMAND_MEMORY, 2);
+
+ /* Reset only works if SMC firmware is loaded and running */
+ if (!vfio_radeon_smc_is_running(vdev)) {
+ ret = -EINVAL;
+ trace_vfio_quirk_ati_bonaire_reset_no_smc(vdev->vbasedev.name);
+ goto out;
+ }
+
+ /* Make sure only the GFX function is reset */
+ vfio_radeon_set_gfx_only_reset(vdev);
+
+ /* AMD PCI config reset */
+ vfio_pci_write_config(pdev, 0x7c, 0x39d5e86b, 4);
+ usleep(100);
+
+ /* Read back the memory size to make sure we're out of reset */
+ for (i = 0; i < 100000; i++) {
+ if (vfio_region_read(&vdev->bars[5].region, 0x5428, 4) != 0xffffffff) {
+ goto reset_smc;
+ }
+ usleep(1);
+ }
+
+ trace_vfio_quirk_ati_bonaire_reset_timeout(vdev->vbasedev.name);
+
+reset_smc:
+ /* Reset SMC */
+ vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000000, 4);
+ data = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
+ data |= 1;
+ vfio_region_write(&vdev->bars[5].region, 0x204, data, 4);
+
+ /* Disable SMC clock */
+ vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000004, 4);
+ data = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
+ data |= 1;
+ vfio_region_write(&vdev->bars[5].region, 0x204, data, 4);
+
+ trace_vfio_quirk_ati_bonaire_reset_done(vdev->vbasedev.name);
+
+out:
+ /* Restore PCI command register */
+ vfio_pci_write_config(pdev, PCI_COMMAND, 0, 2);
+
+ return ret;
+}
+
+void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev)
+{
+ switch (vdev->vendor_id) {
+ case 0x1002:
+ switch (vdev->device_id) {
+ /* Bonaire */
+ case 0x6649: /* Bonaire [FirePro W5100] */
+ case 0x6650:
+ case 0x6651:
+ case 0x6658: /* Bonaire XTX [Radeon R7 260X] */
+ case 0x665c: /* Bonaire XT [Radeon HD 7790/8770 / R9 260 OEM] */
+ case 0x665d: /* Bonaire [Radeon R7 200 Series] */
+ /* Hawaii */
+ case 0x67A0: /* Hawaii XT GL [FirePro W9100] */
+ case 0x67A1: /* Hawaii PRO GL [FirePro W8100] */
+ case 0x67A2:
+ case 0x67A8:
+ case 0x67A9:
+ case 0x67AA:
+ case 0x67B0: /* Hawaii XT [Radeon R9 290X] */
+ case 0x67B1: /* Hawaii PRO [Radeon R9 290] */
+ case 0x67B8:
+ case 0x67B9:
+ case 0x67BA:
+ case 0x67BE:
+ vdev->resetfn = vfio_radeon_reset;
+ trace_vfio_quirk_ati_bonaire_reset(vdev->vbasedev.name);
+ break;
+ }
+ break;
+ }
+}
diff --git a/qemu/hw/vfio/pci.c b/qemu/hw/vfio/pci.c
index 4023d8e82..d091d8cf0 100644
--- a/qemu/hw/vfio/pci.c
+++ b/qemu/hw/vfio/pci.c
@@ -18,187 +18,24 @@
* Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@il.ibm.com)
*/
-#include <dirent.h>
+#include "qemu/osdep.h"
#include <linux/vfio.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include "config.h"
-#include "exec/address-spaces.h"
-#include "exec/memory.h"
#include "hw/pci/msi.h"
#include "hw/pci/msix.h"
-#include "hw/pci/pci.h"
-#include "qemu-common.h"
+#include "hw/pci/pci_bridge.h"
#include "qemu/error-report.h"
-#include "qemu/event_notifier.h"
-#include "qemu/queue.h"
#include "qemu/range.h"
#include "sysemu/kvm.h"
#include "sysemu/sysemu.h"
+#include "pci.h"
#include "trace.h"
-#include "hw/vfio/vfio.h"
-#include "hw/vfio/vfio-common.h"
-
-struct VFIOPCIDevice;
-
-typedef struct VFIOQuirk {
- MemoryRegion mem;
- struct VFIOPCIDevice *vdev;
- QLIST_ENTRY(VFIOQuirk) next;
- struct {
- uint32_t base_offset:TARGET_PAGE_BITS;
- uint32_t address_offset:TARGET_PAGE_BITS;
- uint32_t address_size:3;
- uint32_t bar:3;
-
- uint32_t address_match;
- uint32_t address_mask;
-
- uint32_t address_val:TARGET_PAGE_BITS;
- uint32_t data_offset:TARGET_PAGE_BITS;
- uint32_t data_size:3;
-
- uint8_t flags;
- uint8_t read_flags;
- uint8_t write_flags;
- } data;
-} VFIOQuirk;
-
-typedef struct VFIOBAR {
- VFIORegion region;
- bool ioport;
- bool mem64;
- QLIST_HEAD(, VFIOQuirk) quirks;
-} VFIOBAR;
-
-typedef struct VFIOVGARegion {
- MemoryRegion mem;
- off_t offset;
- int nr;
- QLIST_HEAD(, VFIOQuirk) quirks;
-} VFIOVGARegion;
-
-typedef struct VFIOVGA {
- off_t fd_offset;
- int fd;
- VFIOVGARegion region[QEMU_PCI_VGA_NUM_REGIONS];
-} VFIOVGA;
-
-typedef struct VFIOINTx {
- bool pending; /* interrupt pending */
- bool kvm_accel; /* set when QEMU bypass through KVM enabled */
- uint8_t pin; /* which pin to pull for qemu_set_irq */
- EventNotifier interrupt; /* eventfd triggered on interrupt */
- EventNotifier unmask; /* eventfd for unmask on QEMU bypass */
- PCIINTxRoute route; /* routing info for QEMU bypass */
- uint32_t mmap_timeout; /* delay to re-enable mmaps after interrupt */
- QEMUTimer *mmap_timer; /* enable mmaps after periods w/o interrupts */
-} VFIOINTx;
-
-typedef struct VFIOMSIVector {
- /*
- * Two interrupt paths are configured per vector. The first, is only used
- * for interrupts injected via QEMU. This is typically the non-accel path,
- * but may also be used when we want QEMU to handle masking and pending
- * bits. The KVM path bypasses QEMU and is therefore higher performance,
- * but requires masking at the device. virq is used to track the MSI route
- * through KVM, thus kvm_interrupt is only available when virq is set to a
- * valid (>= 0) value.
- */
- EventNotifier interrupt;
- EventNotifier kvm_interrupt;
- struct VFIOPCIDevice *vdev; /* back pointer to device */
- int virq;
- bool use;
-} VFIOMSIVector;
-
-enum {
- VFIO_INT_NONE = 0,
- VFIO_INT_INTx = 1,
- VFIO_INT_MSI = 2,
- VFIO_INT_MSIX = 3,
-};
-
-/* Cache of MSI-X setup plus extra mmap and memory region for split BAR map */
-typedef struct VFIOMSIXInfo {
- uint8_t table_bar;
- uint8_t pba_bar;
- uint16_t entries;
- uint32_t table_offset;
- uint32_t pba_offset;
- MemoryRegion mmap_mem;
- void *mmap;
-} VFIOMSIXInfo;
-
-typedef struct VFIOPCIDevice {
- PCIDevice pdev;
- VFIODevice vbasedev;
- VFIOINTx intx;
- unsigned int config_size;
- uint8_t *emulated_config_bits; /* QEMU emulated bits, little-endian */
- off_t config_offset; /* Offset of config space region within device fd */
- unsigned int rom_size;
- off_t rom_offset; /* Offset of ROM region within device fd */
- void *rom;
- int msi_cap_size;
- VFIOMSIVector *msi_vectors;
- VFIOMSIXInfo *msix;
- int nr_vectors; /* Number of MSI/MSIX vectors currently in use */
- int interrupt; /* Current interrupt type */
- VFIOBAR bars[PCI_NUM_REGIONS - 1]; /* No ROM */
- VFIOVGA vga; /* 0xa0000, 0x3b0, 0x3c0 */
- PCIHostDeviceAddress host;
- EventNotifier err_notifier;
- EventNotifier req_notifier;
- int (*resetfn)(struct VFIOPCIDevice *);
- uint32_t features;
-#define VFIO_FEATURE_ENABLE_VGA_BIT 0
-#define VFIO_FEATURE_ENABLE_VGA (1 << VFIO_FEATURE_ENABLE_VGA_BIT)
-#define VFIO_FEATURE_ENABLE_REQ_BIT 1
-#define VFIO_FEATURE_ENABLE_REQ (1 << VFIO_FEATURE_ENABLE_REQ_BIT)
- int32_t bootindex;
- uint8_t pm_cap;
- bool has_vga;
- bool pci_aer;
- bool req_enabled;
- bool has_flr;
- bool has_pm_reset;
- bool rom_read_failed;
-} VFIOPCIDevice;
-
-typedef struct VFIORomBlacklistEntry {
- uint16_t vendor_id;
- uint16_t device_id;
-} VFIORomBlacklistEntry;
-
-/*
- * List of device ids/vendor ids for which to disable
- * option rom loading. This avoids the guest hangs during rom
- * execution as noticed with the BCM 57810 card for lack of a
- * more better way to handle such issues.
- * The user can still override by specifying a romfile or
- * rombar=1.
- * Please see https://bugs.launchpad.net/qemu/+bug/1284874
- * for an analysis of the 57810 card hang. When adding
- * a new vendor id/device id combination below, please also add
- * your card/environment details and information that could
- * help in debugging to the bug tracking this issue
- */
-static const VFIORomBlacklistEntry romblacklist[] = {
- /* Broadcom BCM 57810 */
- { 0x14e4, 0x168e }
-};
#define MSIX_CAP_LENGTH 12
static void vfio_disable_interrupts(VFIOPCIDevice *vdev);
-static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len);
-static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr,
- uint32_t val, int len);
static void vfio_mmap_set_enabled(VFIOPCIDevice *vdev, bool enabled);
/*
@@ -248,7 +85,7 @@ static void vfio_intx_interrupt(void *opaque)
}
}
-static void vfio_eoi(VFIODevice *vbasedev)
+static void vfio_intx_eoi(VFIODevice *vbasedev)
{
VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev);
@@ -256,14 +93,14 @@ static void vfio_eoi(VFIODevice *vbasedev)
return;
}
- trace_vfio_eoi(vbasedev->name);
+ trace_vfio_intx_eoi(vbasedev->name);
vdev->intx.pending = false;
pci_irq_deassert(&vdev->pdev);
vfio_unmask_single_irqindex(vbasedev, VFIO_PCI_INTX_IRQ_INDEX);
}
-static void vfio_enable_intx_kvm(VFIOPCIDevice *vdev)
+static void vfio_intx_enable_kvm(VFIOPCIDevice *vdev)
{
#ifdef CONFIG_KVM
struct kvm_irqfd irqfd = {
@@ -275,7 +112,7 @@ static void vfio_enable_intx_kvm(VFIOPCIDevice *vdev)
int ret, argsz;
int32_t *pfd;
- if (!VFIO_ALLOW_KVM_INTX || !kvm_irqfds_enabled() ||
+ if (vdev->no_kvm_intx || !kvm_irqfds_enabled() ||
vdev->intx.route.mode != PCI_INTX_ENABLED ||
!kvm_resamplefds_enabled()) {
return;
@@ -325,7 +162,7 @@ static void vfio_enable_intx_kvm(VFIOPCIDevice *vdev)
vdev->intx.kvm_accel = true;
- trace_vfio_enable_intx_kvm(vdev->vbasedev.name);
+ trace_vfio_intx_enable_kvm(vdev->vbasedev.name);
return;
@@ -340,7 +177,7 @@ fail:
#endif
}
-static void vfio_disable_intx_kvm(VFIOPCIDevice *vdev)
+static void vfio_intx_disable_kvm(VFIOPCIDevice *vdev)
{
#ifdef CONFIG_KVM
struct kvm_irqfd irqfd = {
@@ -377,11 +214,11 @@ static void vfio_disable_intx_kvm(VFIOPCIDevice *vdev)
/* If we've missed an event, let it re-fire through QEMU */
vfio_unmask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX);
- trace_vfio_disable_intx_kvm(vdev->vbasedev.name);
+ trace_vfio_intx_disable_kvm(vdev->vbasedev.name);
#endif
}
-static void vfio_update_irq(PCIDevice *pdev)
+static void vfio_intx_update(PCIDevice *pdev)
{
VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev);
PCIINTxRoute route;
@@ -396,10 +233,10 @@ static void vfio_update_irq(PCIDevice *pdev)
return; /* Nothing changed */
}
- trace_vfio_update_irq(vdev->vbasedev.name,
- vdev->intx.route.irq, route.irq);
+ trace_vfio_intx_update(vdev->vbasedev.name,
+ vdev->intx.route.irq, route.irq);
- vfio_disable_intx_kvm(vdev);
+ vfio_intx_disable_kvm(vdev);
vdev->intx.route = route;
@@ -407,13 +244,13 @@ static void vfio_update_irq(PCIDevice *pdev)
return;
}
- vfio_enable_intx_kvm(vdev);
+ vfio_intx_enable_kvm(vdev);
/* Re-enable the interrupt in cased we missed an EOI */
- vfio_eoi(&vdev->vbasedev);
+ vfio_intx_eoi(&vdev->vbasedev);
}
-static int vfio_enable_intx(VFIOPCIDevice *vdev)
+static int vfio_intx_enable(VFIOPCIDevice *vdev)
{
uint8_t pin = vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1);
int ret, argsz;
@@ -468,21 +305,21 @@ static int vfio_enable_intx(VFIOPCIDevice *vdev)
return -errno;
}
- vfio_enable_intx_kvm(vdev);
+ vfio_intx_enable_kvm(vdev);
vdev->interrupt = VFIO_INT_INTx;
- trace_vfio_enable_intx(vdev->vbasedev.name);
+ trace_vfio_intx_enable(vdev->vbasedev.name);
return 0;
}
-static void vfio_disable_intx(VFIOPCIDevice *vdev)
+static void vfio_intx_disable(VFIOPCIDevice *vdev)
{
int fd;
timer_del(vdev->intx.mmap_timer);
- vfio_disable_intx_kvm(vdev);
+ vfio_intx_disable_kvm(vdev);
vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX);
vdev->intx.pending = false;
pci_irq_deassert(&vdev->pdev);
@@ -494,7 +331,7 @@ static void vfio_disable_intx(VFIOPCIDevice *vdev)
vdev->interrupt = VFIO_INT_NONE;
- trace_vfio_disable_intx(vdev->vbasedev.name);
+ trace_vfio_intx_disable(vdev->vbasedev.name);
}
/*
@@ -504,33 +341,35 @@ static void vfio_msi_interrupt(void *opaque)
{
VFIOMSIVector *vector = opaque;
VFIOPCIDevice *vdev = vector->vdev;
+ MSIMessage (*get_msg)(PCIDevice *dev, unsigned vector);
+ void (*notify)(PCIDevice *dev, unsigned vector);
+ MSIMessage msg;
int nr = vector - vdev->msi_vectors;
if (!event_notifier_test_and_clear(&vector->interrupt)) {
return;
}
-#ifdef DEBUG_VFIO
- MSIMessage msg;
-
if (vdev->interrupt == VFIO_INT_MSIX) {
- msg = msix_get_message(&vdev->pdev, nr);
+ get_msg = msix_get_message;
+ notify = msix_notify;
+
+ /* A masked vector firing needs to use the PBA, enable it */
+ if (msix_is_masked(&vdev->pdev, nr)) {
+ set_bit(nr, vdev->msix->pending);
+ memory_region_set_enabled(&vdev->pdev.msix_pba_mmio, true);
+ trace_vfio_msix_pba_enable(vdev->vbasedev.name);
+ }
} else if (vdev->interrupt == VFIO_INT_MSI) {
- msg = msi_get_message(&vdev->pdev, nr);
+ get_msg = msi_get_message;
+ notify = msi_notify;
} else {
abort();
}
+ msg = get_msg(&vdev->pdev, nr);
trace_vfio_msi_interrupt(vdev->vbasedev.name, nr, msg.address, msg.data);
-#endif
-
- if (vdev->interrupt == VFIO_INT_MSIX) {
- msix_notify(&vdev->pdev, nr);
- } else if (vdev->interrupt == VFIO_INT_MSI) {
- msi_notify(&vdev->pdev, nr);
- } else {
- error_report("vfio: MSI interrupt receieved, but not enabled?");
- }
+ notify(&vdev->pdev, nr);
}
static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix)
@@ -577,13 +416,12 @@ static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix)
return ret;
}
-static void vfio_add_kvm_msi_virq(VFIOMSIVector *vector, MSIMessage *msg,
- bool msix)
+static void vfio_add_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector,
+ MSIMessage *msg, bool msix)
{
int virq;
- if ((msix && !VFIO_ALLOW_KVM_MSIX) ||
- (!msix && !VFIO_ALLOW_KVM_MSI) || !msg) {
+ if ((msix && vdev->no_kvm_msix) || (!msix && vdev->no_kvm_msi) || !msg) {
return;
}
@@ -591,7 +429,7 @@ static void vfio_add_kvm_msi_virq(VFIOMSIVector *vector, MSIMessage *msg,
return;
}
- virq = kvm_irqchip_add_msi_route(kvm_state, *msg);
+ virq = kvm_irqchip_add_msi_route(kvm_state, *msg, &vdev->pdev);
if (virq < 0) {
event_notifier_cleanup(&vector->kvm_interrupt);
return;
@@ -616,9 +454,10 @@ static void vfio_remove_kvm_msi_virq(VFIOMSIVector *vector)
event_notifier_cleanup(&vector->kvm_interrupt);
}
-static void vfio_update_kvm_msi_virq(VFIOMSIVector *vector, MSIMessage msg)
+static void vfio_update_kvm_msi_virq(VFIOMSIVector *vector, MSIMessage msg,
+ PCIDevice *pdev)
{
- kvm_irqchip_update_msi_route(kvm_state, vector->virq, msg);
+ kvm_irqchip_update_msi_route(kvm_state, vector->virq, msg, pdev);
}
static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr,
@@ -653,10 +492,10 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr,
if (!msg) {
vfio_remove_kvm_msi_virq(vector);
} else {
- vfio_update_kvm_msi_virq(vector, *msg);
+ vfio_update_kvm_msi_virq(vector, *msg, pdev);
}
} else {
- vfio_add_kvm_msi_virq(vector, msg, true);
+ vfio_add_kvm_msi_virq(vdev, vector, msg, true);
}
/*
@@ -700,6 +539,14 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr,
}
}
+ /* Disable PBA emulation when nothing more is pending. */
+ clear_bit(nr, vdev->msix->pending);
+ if (find_first_bit(vdev->msix->pending,
+ vdev->nr_vectors) == vdev->nr_vectors) {
+ memory_region_set_enabled(&vdev->pdev.msix_pba_mmio, false);
+ trace_vfio_msix_pba_disable(vdev->vbasedev.name);
+ }
+
return 0;
}
@@ -748,11 +595,11 @@ static void vfio_msix_vector_release(PCIDevice *pdev, unsigned int nr)
}
}
-static void vfio_enable_msix(VFIOPCIDevice *vdev)
+static void vfio_msix_enable(VFIOPCIDevice *vdev)
{
vfio_disable_interrupts(vdev);
- vdev->msi_vectors = g_malloc0(vdev->msix->entries * sizeof(VFIOMSIVector));
+ vdev->msi_vectors = g_new0(VFIOMSIVector, vdev->msix->entries);
vdev->interrupt = VFIO_INT_MSIX;
@@ -777,10 +624,10 @@ static void vfio_enable_msix(VFIOPCIDevice *vdev)
error_report("vfio: msix_set_vector_notifiers failed");
}
- trace_vfio_enable_msix(vdev->vbasedev.name);
+ trace_vfio_msix_enable(vdev->vbasedev.name);
}
-static void vfio_enable_msi(VFIOPCIDevice *vdev)
+static void vfio_msi_enable(VFIOPCIDevice *vdev)
{
int ret, i;
@@ -788,7 +635,7 @@ static void vfio_enable_msi(VFIOPCIDevice *vdev)
vdev->nr_vectors = msi_nr_vectors_allocated(&vdev->pdev);
retry:
- vdev->msi_vectors = g_malloc0(vdev->nr_vectors * sizeof(VFIOMSIVector));
+ vdev->msi_vectors = g_new0(VFIOMSIVector, vdev->nr_vectors);
for (i = 0; i < vdev->nr_vectors; i++) {
VFIOMSIVector *vector = &vdev->msi_vectors[i];
@@ -809,7 +656,7 @@ retry:
* Attempt to enable route through KVM irqchip,
* default to userspace handling if unavailable.
*/
- vfio_add_kvm_msi_virq(vector, &msg, false);
+ vfio_add_kvm_msi_virq(vdev, vector, &msg, false);
}
/* Set interrupt type prior to possible interrupts */
@@ -853,10 +700,10 @@ retry:
return;
}
- trace_vfio_enable_msi(vdev->vbasedev.name, vdev->nr_vectors);
+ trace_vfio_msi_enable(vdev->vbasedev.name, vdev->nr_vectors);
}
-static void vfio_disable_msi_common(VFIOPCIDevice *vdev)
+static void vfio_msi_disable_common(VFIOPCIDevice *vdev)
{
int i;
@@ -877,10 +724,10 @@ static void vfio_disable_msi_common(VFIOPCIDevice *vdev)
vdev->nr_vectors = 0;
vdev->interrupt = VFIO_INT_NONE;
- vfio_enable_intx(vdev);
+ vfio_intx_enable(vdev);
}
-static void vfio_disable_msix(VFIOPCIDevice *vdev)
+static void vfio_msix_disable(VFIOPCIDevice *vdev)
{
int i;
@@ -901,17 +748,20 @@ static void vfio_disable_msix(VFIOPCIDevice *vdev)
vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX);
}
- vfio_disable_msi_common(vdev);
+ vfio_msi_disable_common(vdev);
+
+ memset(vdev->msix->pending, 0,
+ BITS_TO_LONGS(vdev->msix->entries) * sizeof(unsigned long));
- trace_vfio_disable_msix(vdev->vbasedev.name);
+ trace_vfio_msix_disable(vdev->vbasedev.name);
}
-static void vfio_disable_msi(VFIOPCIDevice *vdev)
+static void vfio_msi_disable(VFIOPCIDevice *vdev)
{
vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_MSI_IRQ_INDEX);
- vfio_disable_msi_common(vdev);
+ vfio_msi_disable_common(vdev);
- trace_vfio_disable_msi(vdev->vbasedev.name);
+ trace_vfio_msi_disable(vdev->vbasedev.name);
}
static void vfio_update_msi(VFIOPCIDevice *vdev)
@@ -927,31 +777,31 @@ static void vfio_update_msi(VFIOPCIDevice *vdev)
}
msg = msi_get_message(&vdev->pdev, i);
- vfio_update_kvm_msi_virq(vector, msg);
+ vfio_update_kvm_msi_virq(vector, msg, &vdev->pdev);
}
}
static void vfio_pci_load_rom(VFIOPCIDevice *vdev)
{
- struct vfio_region_info reg_info = {
- .argsz = sizeof(reg_info),
- .index = VFIO_PCI_ROM_REGION_INDEX
- };
+ struct vfio_region_info *reg_info;
uint64_t size;
off_t off = 0;
ssize_t bytes;
- if (ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_REGION_INFO, &reg_info)) {
+ if (vfio_get_region_info(&vdev->vbasedev,
+ VFIO_PCI_ROM_REGION_INDEX, &reg_info)) {
error_report("vfio: Error getting ROM info: %m");
return;
}
- trace_vfio_pci_load_rom(vdev->vbasedev.name, (unsigned long)reg_info.size,
- (unsigned long)reg_info.offset,
- (unsigned long)reg_info.flags);
+ trace_vfio_pci_load_rom(vdev->vbasedev.name, (unsigned long)reg_info->size,
+ (unsigned long)reg_info->offset,
+ (unsigned long)reg_info->flags);
+
+ vdev->rom_size = size = reg_info->size;
+ vdev->rom_offset = reg_info->offset;
- vdev->rom_size = size = reg_info.size;
- vdev->rom_offset = reg_info.offset;
+ g_free(reg_info);
if (!vdev->rom_size) {
vdev->rom_read_failed = true;
@@ -982,6 +832,36 @@ static void vfio_pci_load_rom(VFIOPCIDevice *vdev)
break;
}
}
+
+ /*
+ * Test the ROM signature against our device, if the vendor is correct
+ * but the device ID doesn't match, store the correct device ID and
+ * recompute the checksum. Intel IGD devices need this and are known
+ * to have bogus checksums so we can't simply adjust the checksum.
+ */
+ if (pci_get_word(vdev->rom) == 0xaa55 &&
+ pci_get_word(vdev->rom + 0x18) + 8 < vdev->rom_size &&
+ !memcmp(vdev->rom + pci_get_word(vdev->rom + 0x18), "PCIR", 4)) {
+ uint16_t vid, did;
+
+ vid = pci_get_word(vdev->rom + pci_get_word(vdev->rom + 0x18) + 4);
+ did = pci_get_word(vdev->rom + pci_get_word(vdev->rom + 0x18) + 6);
+
+ if (vid == vdev->vendor_id && did != vdev->device_id) {
+ int i;
+ uint8_t csum, *data = vdev->rom;
+
+ pci_set_word(vdev->rom + pci_get_word(vdev->rom + 0x18) + 6,
+ vdev->device_id);
+ data[6] = 0;
+
+ for (csum = 0, i = 0; i < vdev->rom_size; i++) {
+ csum += data[i];
+ }
+
+ data[6] = -csum;
+ }
+ }
}
static uint64_t vfio_rom_read(void *opaque, hwaddr addr, unsigned size)
@@ -1034,43 +914,19 @@ static const MemoryRegionOps vfio_rom_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
-static bool vfio_blacklist_opt_rom(VFIOPCIDevice *vdev)
-{
- PCIDevice *pdev = &vdev->pdev;
- uint16_t vendor_id, device_id;
- int count = 0;
-
- vendor_id = pci_get_word(pdev->config + PCI_VENDOR_ID);
- device_id = pci_get_word(pdev->config + PCI_DEVICE_ID);
-
- while (count < ARRAY_SIZE(romblacklist)) {
- if (romblacklist[count].vendor_id == vendor_id &&
- romblacklist[count].device_id == device_id) {
- return true;
- }
- count++;
- }
-
- return false;
-}
-
static void vfio_pci_size_rom(VFIOPCIDevice *vdev)
{
uint32_t orig, size = cpu_to_le32((uint32_t)PCI_ROM_ADDRESS_MASK);
off_t offset = vdev->config_offset + PCI_ROM_ADDRESS;
DeviceState *dev = DEVICE(vdev);
- char name[32];
+ char *name;
int fd = vdev->vbasedev.fd;
if (vdev->pdev.romfile || !vdev->pdev.rom_bar) {
/* Since pci handles romfile, just print a message and return */
if (vfio_blacklist_opt_rom(vdev) && vdev->pdev.romfile) {
- error_printf("Warning : Device at %04x:%02x:%02x.%x "
- "is known to cause system instability issues during "
- "option rom execution. "
- "Proceeding anyway since user specified romfile\n",
- vdev->host.domain, vdev->host.bus, vdev->host.slot,
- vdev->host.function);
+ error_printf("Warning : Device at %s is known to cause system instability issues during option rom execution. Proceeding anyway since user specified romfile\n",
+ vdev->vbasedev.name);
}
return;
}
@@ -1083,9 +939,7 @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev)
pwrite(fd, &size, 4, offset) != 4 ||
pread(fd, &size, 4, offset) != 4 ||
pwrite(fd, &orig, 4, offset) != 4) {
- error_report("%s(%04x:%02x:%02x.%x) failed: %m",
- __func__, vdev->host.domain, vdev->host.bus,
- vdev->host.slot, vdev->host.function);
+ error_report("%s(%s) failed: %m", __func__, vdev->vbasedev.name);
return;
}
@@ -1097,32 +951,22 @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev)
if (vfio_blacklist_opt_rom(vdev)) {
if (dev->opts && qemu_opt_get(dev->opts, "rombar")) {
- error_printf("Warning : Device at %04x:%02x:%02x.%x "
- "is known to cause system instability issues during "
- "option rom execution. "
- "Proceeding anyway since user specified non zero value for "
- "rombar\n",
- vdev->host.domain, vdev->host.bus, vdev->host.slot,
- vdev->host.function);
+ error_printf("Warning : Device at %s is known to cause system instability issues during option rom execution. Proceeding anyway since user specified non zero value for rombar\n",
+ vdev->vbasedev.name);
} else {
- error_printf("Warning : Rom loading for device at "
- "%04x:%02x:%02x.%x has been disabled due to "
- "system instability issues. "
- "Specify rombar=1 or romfile to force\n",
- vdev->host.domain, vdev->host.bus, vdev->host.slot,
- vdev->host.function);
+ error_printf("Warning : Rom loading for device at %s has been disabled due to system instability issues. Specify rombar=1 or romfile to force\n",
+ vdev->vbasedev.name);
return;
}
}
trace_vfio_pci_size_rom(vdev->vbasedev.name, size);
- snprintf(name, sizeof(name), "vfio[%04x:%02x:%02x.%x].rom",
- vdev->host.domain, vdev->host.bus, vdev->host.slot,
- vdev->host.function);
+ name = g_strdup_printf("vfio[%s].rom", vdev->vbasedev.name);
memory_region_init_io(&vdev->pdev.rom, OBJECT(vdev),
&vfio_rom_ops, vdev, name, size);
+ g_free(name);
pci_register_bar(&vdev->pdev, PCI_ROM_SLOT,
PCI_BASE_ADDRESS_SPACE_MEMORY, &vdev->pdev.rom);
@@ -1131,7 +975,7 @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev)
vdev->rom_read_failed = false;
}
-static void vfio_vga_write(void *opaque, hwaddr addr,
+void vfio_vga_write(void *opaque, hwaddr addr,
uint64_t data, unsigned size)
{
VFIOVGARegion *region = opaque;
@@ -1167,7 +1011,7 @@ static void vfio_vga_write(void *opaque, hwaddr addr,
trace_vfio_vga_write(region->offset + addr, data, size);
}
-static uint64_t vfio_vga_read(void *opaque, hwaddr addr, unsigned size)
+uint64_t vfio_vga_read(void *opaque, hwaddr addr, unsigned size)
{
VFIOVGARegion *region = opaque;
VFIOVGA *vga = container_of(region, VFIOVGA, region[region->nr]);
@@ -1213,858 +1057,9 @@ static const MemoryRegionOps vfio_vga_ops = {
};
/*
- * Device specific quirks
- */
-
-/* Is range1 fully contained within range2? */
-static bool vfio_range_contained(uint64_t first1, uint64_t len1,
- uint64_t first2, uint64_t len2) {
- return (first1 >= first2 && first1 + len1 <= first2 + len2);
-}
-
-static bool vfio_flags_enabled(uint8_t flags, uint8_t mask)
-{
- return (mask && (flags & mask) == mask);
-}
-
-static uint64_t vfio_generic_window_quirk_read(void *opaque,
- hwaddr addr, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIOPCIDevice *vdev = quirk->vdev;
- uint64_t data;
-
- if (vfio_flags_enabled(quirk->data.flags, quirk->data.read_flags) &&
- ranges_overlap(addr, size,
- quirk->data.data_offset, quirk->data.data_size)) {
- hwaddr offset = addr - quirk->data.data_offset;
-
- if (!vfio_range_contained(addr, size, quirk->data.data_offset,
- quirk->data.data_size)) {
- hw_error("%s: window data read not fully contained: %s",
- __func__, memory_region_name(&quirk->mem));
- }
-
- data = vfio_pci_read_config(&vdev->pdev,
- quirk->data.address_val + offset, size);
-
- trace_vfio_generic_window_quirk_read(memory_region_name(&quirk->mem),
- vdev->vbasedev.name,
- quirk->data.bar,
- addr, size, data);
- } else {
- data = vfio_region_read(&vdev->bars[quirk->data.bar].region,
- addr + quirk->data.base_offset, size);
- }
-
- return data;
-}
-
-static void vfio_generic_window_quirk_write(void *opaque, hwaddr addr,
- uint64_t data, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIOPCIDevice *vdev = quirk->vdev;
-
- if (ranges_overlap(addr, size,
- quirk->data.address_offset, quirk->data.address_size)) {
-
- if (addr != quirk->data.address_offset) {
- hw_error("%s: offset write into address window: %s",
- __func__, memory_region_name(&quirk->mem));
- }
-
- if ((data & ~quirk->data.address_mask) == quirk->data.address_match) {
- quirk->data.flags |= quirk->data.write_flags |
- quirk->data.read_flags;
- quirk->data.address_val = data & quirk->data.address_mask;
- } else {
- quirk->data.flags &= ~(quirk->data.write_flags |
- quirk->data.read_flags);
- }
- }
-
- if (vfio_flags_enabled(quirk->data.flags, quirk->data.write_flags) &&
- ranges_overlap(addr, size,
- quirk->data.data_offset, quirk->data.data_size)) {
- hwaddr offset = addr - quirk->data.data_offset;
-
- if (!vfio_range_contained(addr, size, quirk->data.data_offset,
- quirk->data.data_size)) {
- hw_error("%s: window data write not fully contained: %s",
- __func__, memory_region_name(&quirk->mem));
- }
-
- vfio_pci_write_config(&vdev->pdev,
- quirk->data.address_val + offset, data, size);
- trace_vfio_generic_window_quirk_write(memory_region_name(&quirk->mem),
- vdev->vbasedev.name,
- quirk->data.bar,
- addr, data, size);
- return;
- }
-
- vfio_region_write(&vdev->bars[quirk->data.bar].region,
- addr + quirk->data.base_offset, data, size);
-}
-
-static const MemoryRegionOps vfio_generic_window_quirk = {
- .read = vfio_generic_window_quirk_read,
- .write = vfio_generic_window_quirk_write,
- .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static uint64_t vfio_generic_quirk_read(void *opaque,
- hwaddr addr, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIOPCIDevice *vdev = quirk->vdev;
- hwaddr base = quirk->data.address_match & TARGET_PAGE_MASK;
- hwaddr offset = quirk->data.address_match & ~TARGET_PAGE_MASK;
- uint64_t data;
-
- if (vfio_flags_enabled(quirk->data.flags, quirk->data.read_flags) &&
- ranges_overlap(addr, size, offset, quirk->data.address_mask + 1)) {
- if (!vfio_range_contained(addr, size, offset,
- quirk->data.address_mask + 1)) {
- hw_error("%s: read not fully contained: %s",
- __func__, memory_region_name(&quirk->mem));
- }
-
- data = vfio_pci_read_config(&vdev->pdev, addr - offset, size);
-
- trace_vfio_generic_quirk_read(memory_region_name(&quirk->mem),
- vdev->vbasedev.name, quirk->data.bar,
- addr + base, size, data);
- } else {
- data = vfio_region_read(&vdev->bars[quirk->data.bar].region,
- addr + base, size);
- }
-
- return data;
-}
-
-static void vfio_generic_quirk_write(void *opaque, hwaddr addr,
- uint64_t data, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIOPCIDevice *vdev = quirk->vdev;
- hwaddr base = quirk->data.address_match & TARGET_PAGE_MASK;
- hwaddr offset = quirk->data.address_match & ~TARGET_PAGE_MASK;
-
- if (vfio_flags_enabled(quirk->data.flags, quirk->data.write_flags) &&
- ranges_overlap(addr, size, offset, quirk->data.address_mask + 1)) {
- if (!vfio_range_contained(addr, size, offset,
- quirk->data.address_mask + 1)) {
- hw_error("%s: write not fully contained: %s",
- __func__, memory_region_name(&quirk->mem));
- }
-
- vfio_pci_write_config(&vdev->pdev, addr - offset, data, size);
-
- trace_vfio_generic_quirk_write(memory_region_name(&quirk->mem),
- vdev->vbasedev.name, quirk->data.bar,
- addr + base, data, size);
- } else {
- vfio_region_write(&vdev->bars[quirk->data.bar].region,
- addr + base, data, size);
- }
-}
-
-static const MemoryRegionOps vfio_generic_quirk = {
- .read = vfio_generic_quirk_read,
- .write = vfio_generic_quirk_write,
- .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-#define PCI_VENDOR_ID_ATI 0x1002
-
-/*
- * Radeon HD cards (HD5450 & HD7850) report the upper byte of the I/O port BAR
- * through VGA register 0x3c3. On newer cards, the I/O port BAR is always
- * BAR4 (older cards like the X550 used BAR1, but we don't care to support
- * those). Note that on bare metal, a read of 0x3c3 doesn't always return the
- * I/O port BAR address. Originally this was coded to return the virtual BAR
- * address only if the physical register read returns the actual BAR address,
- * but users have reported greater success if we return the virtual address
- * unconditionally.
- */
-static uint64_t vfio_ati_3c3_quirk_read(void *opaque,
- hwaddr addr, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIOPCIDevice *vdev = quirk->vdev;
- uint64_t data = vfio_pci_read_config(&vdev->pdev,
- PCI_BASE_ADDRESS_0 + (4 * 4) + 1,
- size);
- trace_vfio_ati_3c3_quirk_read(data);
-
- return data;
-}
-
-static const MemoryRegionOps vfio_ati_3c3_quirk = {
- .read = vfio_ati_3c3_quirk_read,
- .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static void vfio_vga_probe_ati_3c3_quirk(VFIOPCIDevice *vdev)
-{
- PCIDevice *pdev = &vdev->pdev;
- VFIOQuirk *quirk;
-
- if (pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_ATI) {
- return;
- }
-
- /*
- * As long as the BAR is >= 256 bytes it will be aligned such that the
- * lower byte is always zero. Filter out anything else, if it exists.
- */
- if (!vdev->bars[4].ioport || vdev->bars[4].region.size < 256) {
- return;
- }
-
- quirk = g_malloc0(sizeof(*quirk));
- quirk->vdev = vdev;
-
- memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_ati_3c3_quirk, quirk,
- "vfio-ati-3c3-quirk", 1);
- memory_region_add_subregion(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem,
- 3 /* offset 3 bytes from 0x3c0 */, &quirk->mem);
-
- QLIST_INSERT_HEAD(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].quirks,
- quirk, next);
-
- trace_vfio_vga_probe_ati_3c3_quirk(vdev->vbasedev.name);
-}
-
-/*
- * Newer ATI/AMD devices, including HD5450 and HD7850, have a window to PCI
- * config space through MMIO BAR2 at offset 0x4000. Nothing seems to access
- * the MMIO space directly, but a window to this space is provided through
- * I/O port BAR4. Offset 0x0 is the address register and offset 0x4 is the
- * data register. When the address is programmed to a range of 0x4000-0x4fff
- * PCI configuration space is available. Experimentation seems to indicate
- * that only read-only access is provided, but we drop writes when the window
- * is enabled to config space nonetheless.
- */
-static void vfio_probe_ati_bar4_window_quirk(VFIOPCIDevice *vdev, int nr)
-{
- PCIDevice *pdev = &vdev->pdev;
- VFIOQuirk *quirk;
-
- if (!vdev->has_vga || nr != 4 ||
- pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_ATI) {
- return;
- }
-
- quirk = g_malloc0(sizeof(*quirk));
- quirk->vdev = vdev;
- quirk->data.address_size = 4;
- quirk->data.data_offset = 4;
- quirk->data.data_size = 4;
- quirk->data.address_match = 0x4000;
- quirk->data.address_mask = PCIE_CONFIG_SPACE_SIZE - 1;
- quirk->data.bar = nr;
- quirk->data.read_flags = quirk->data.write_flags = 1;
-
- memory_region_init_io(&quirk->mem, OBJECT(vdev),
- &vfio_generic_window_quirk, quirk,
- "vfio-ati-bar4-window-quirk", 8);
- memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
- quirk->data.base_offset, &quirk->mem, 1);
-
- QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
-
- trace_vfio_probe_ati_bar4_window_quirk(vdev->vbasedev.name);
-}
-
-#define PCI_VENDOR_ID_REALTEK 0x10ec
-
-/*
- * RTL8168 devices have a backdoor that can access the MSI-X table. At BAR2
- * offset 0x70 there is a dword data register, offset 0x74 is a dword address
- * register. According to the Linux r8169 driver, the MSI-X table is addressed
- * when the "type" portion of the address register is set to 0x1. This appears
- * to be bits 16:30. Bit 31 is both a write indicator and some sort of
- * "address latched" indicator. Bits 12:15 are a mask field, which we can
- * ignore because the MSI-X table should always be accessed as a dword (full
- * mask). Bits 0:11 is offset within the type.
- *
- * Example trace:
- *
- * Read from MSI-X table offset 0
- * vfio: vfio_bar_write(0000:05:00.0:BAR2+0x74, 0x1f000, 4) // store read addr
- * vfio: vfio_bar_read(0000:05:00.0:BAR2+0x74, 4) = 0x8001f000 // latch
- * vfio: vfio_bar_read(0000:05:00.0:BAR2+0x70, 4) = 0xfee00398 // read data
- *
- * Write 0xfee00000 to MSI-X table offset 0
- * vfio: vfio_bar_write(0000:05:00.0:BAR2+0x70, 0xfee00000, 4) // write data
- * vfio: vfio_bar_write(0000:05:00.0:BAR2+0x74, 0x8001f000, 4) // do write
- * vfio: vfio_bar_read(0000:05:00.0:BAR2+0x74, 4) = 0x1f000 // complete
- */
-
-static uint64_t vfio_rtl8168_window_quirk_read(void *opaque,
- hwaddr addr, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIOPCIDevice *vdev = quirk->vdev;
-
- switch (addr) {
- case 4: /* address */
- if (quirk->data.flags) {
- trace_vfio_rtl8168_window_quirk_read_fake(
- memory_region_name(&quirk->mem),
- vdev->vbasedev.name);
-
- return quirk->data.address_match ^ 0x80000000U;
- }
- break;
- case 0: /* data */
- if (quirk->data.flags) {
- uint64_t val;
-
- trace_vfio_rtl8168_window_quirk_read_table(
- memory_region_name(&quirk->mem),
- vdev->vbasedev.name);
-
- if (!(vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX)) {
- return 0;
- }
-
- memory_region_dispatch_read(&vdev->pdev.msix_table_mmio,
- (hwaddr)(quirk->data.address_match
- & 0xfff),
- &val,
- size,
- MEMTXATTRS_UNSPECIFIED);
- return val;
- }
- }
-
- trace_vfio_rtl8168_window_quirk_read_direct(memory_region_name(&quirk->mem),
- vdev->vbasedev.name);
-
- return vfio_region_read(&vdev->bars[quirk->data.bar].region,
- addr + 0x70, size);
-}
-
-static void vfio_rtl8168_window_quirk_write(void *opaque, hwaddr addr,
- uint64_t data, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIOPCIDevice *vdev = quirk->vdev;
-
- switch (addr) {
- case 4: /* address */
- if ((data & 0x7fff0000) == 0x10000) {
- if (data & 0x80000000U &&
- vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX) {
-
- trace_vfio_rtl8168_window_quirk_write_table(
- memory_region_name(&quirk->mem),
- vdev->vbasedev.name);
-
- memory_region_dispatch_write(&vdev->pdev.msix_table_mmio,
- (hwaddr)(data & 0xfff),
- (uint64_t)quirk->data.address_mask,
- size, MEMTXATTRS_UNSPECIFIED);
- }
-
- quirk->data.flags = 1;
- quirk->data.address_match = data;
-
- return;
- }
- quirk->data.flags = 0;
- break;
- case 0: /* data */
- quirk->data.address_mask = data;
- break;
- }
-
- trace_vfio_rtl8168_window_quirk_write_direct(
- memory_region_name(&quirk->mem),
- vdev->vbasedev.name);
-
- vfio_region_write(&vdev->bars[quirk->data.bar].region,
- addr + 0x70, data, size);
-}
-
-static const MemoryRegionOps vfio_rtl8168_window_quirk = {
- .read = vfio_rtl8168_window_quirk_read,
- .write = vfio_rtl8168_window_quirk_write,
- .valid = {
- .min_access_size = 4,
- .max_access_size = 4,
- .unaligned = false,
- },
- .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static void vfio_probe_rtl8168_bar2_window_quirk(VFIOPCIDevice *vdev, int nr)
-{
- PCIDevice *pdev = &vdev->pdev;
- VFIOQuirk *quirk;
-
- if (pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_REALTEK ||
- pci_get_word(pdev->config + PCI_DEVICE_ID) != 0x8168 || nr != 2) {
- return;
- }
-
- quirk = g_malloc0(sizeof(*quirk));
- quirk->vdev = vdev;
- quirk->data.bar = nr;
-
- memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_rtl8168_window_quirk,
- quirk, "vfio-rtl8168-window-quirk", 8);
- memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
- 0x70, &quirk->mem, 1);
-
- QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
-
- trace_vfio_probe_rtl8168_bar2_window_quirk(vdev->vbasedev.name);
-}
-/*
- * Trap the BAR2 MMIO window to config space as well.
- */
-static void vfio_probe_ati_bar2_4000_quirk(VFIOPCIDevice *vdev, int nr)
-{
- PCIDevice *pdev = &vdev->pdev;
- VFIOQuirk *quirk;
-
- /* Only enable on newer devices where BAR2 is 64bit */
- if (!vdev->has_vga || nr != 2 || !vdev->bars[2].mem64 ||
- pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_ATI) {
- return;
- }
-
- quirk = g_malloc0(sizeof(*quirk));
- quirk->vdev = vdev;
- quirk->data.flags = quirk->data.read_flags = quirk->data.write_flags = 1;
- quirk->data.address_match = 0x4000;
- quirk->data.address_mask = PCIE_CONFIG_SPACE_SIZE - 1;
- quirk->data.bar = nr;
-
- memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_generic_quirk, quirk,
- "vfio-ati-bar2-4000-quirk",
- TARGET_PAGE_ALIGN(quirk->data.address_mask + 1));
- memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
- quirk->data.address_match & TARGET_PAGE_MASK,
- &quirk->mem, 1);
-
- QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
-
- trace_vfio_probe_ati_bar2_4000_quirk(vdev->vbasedev.name);
-}
-
-/*
- * Older ATI/AMD cards like the X550 have a similar window to that above.
- * I/O port BAR1 provides a window to a mirror of PCI config space located
- * in BAR2 at offset 0xf00. We don't care to support such older cards, but
- * note it for future reference.
- */
-
-#define PCI_VENDOR_ID_NVIDIA 0x10de
-
-/*
- * Nvidia has several different methods to get to config space, the
- * nouveu project has several of these documented here:
- * https://github.com/pathscale/envytools/tree/master/hwdocs
- *
- * The first quirk is actually not documented in envytools and is found
- * on 10de:01d1 (NVIDIA Corporation G72 [GeForce 7300 LE]). This is an
- * NV46 chipset. The backdoor uses the legacy VGA I/O ports to access
- * the mirror of PCI config space found at BAR0 offset 0x1800. The access
- * sequence first writes 0x338 to I/O port 0x3d4. The target offset is
- * then written to 0x3d0. Finally 0x538 is written for a read and 0x738
- * is written for a write to 0x3d4. The BAR0 offset is then accessible
- * through 0x3d0. This quirk doesn't seem to be necessary on newer cards
- * that use the I/O port BAR5 window but it doesn't hurt to leave it.
- */
-enum {
- NV_3D0_NONE = 0,
- NV_3D0_SELECT,
- NV_3D0_WINDOW,
- NV_3D0_READ,
- NV_3D0_WRITE,
-};
-
-static uint64_t vfio_nvidia_3d0_quirk_read(void *opaque,
- hwaddr addr, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIOPCIDevice *vdev = quirk->vdev;
- PCIDevice *pdev = &vdev->pdev;
- uint64_t data = vfio_vga_read(&vdev->vga.region[QEMU_PCI_VGA_IO_HI],
- addr + quirk->data.base_offset, size);
-
- if (quirk->data.flags == NV_3D0_READ && addr == quirk->data.data_offset) {
- data = vfio_pci_read_config(pdev, quirk->data.address_val, size);
- trace_vfio_nvidia_3d0_quirk_read(size, data);
- }
-
- quirk->data.flags = NV_3D0_NONE;
-
- return data;
-}
-
-static void vfio_nvidia_3d0_quirk_write(void *opaque, hwaddr addr,
- uint64_t data, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIOPCIDevice *vdev = quirk->vdev;
- PCIDevice *pdev = &vdev->pdev;
-
- switch (quirk->data.flags) {
- case NV_3D0_NONE:
- if (addr == quirk->data.address_offset && data == 0x338) {
- quirk->data.flags = NV_3D0_SELECT;
- }
- break;
- case NV_3D0_SELECT:
- quirk->data.flags = NV_3D0_NONE;
- if (addr == quirk->data.data_offset &&
- (data & ~quirk->data.address_mask) == quirk->data.address_match) {
- quirk->data.flags = NV_3D0_WINDOW;
- quirk->data.address_val = data & quirk->data.address_mask;
- }
- break;
- case NV_3D0_WINDOW:
- quirk->data.flags = NV_3D0_NONE;
- if (addr == quirk->data.address_offset) {
- if (data == 0x538) {
- quirk->data.flags = NV_3D0_READ;
- } else if (data == 0x738) {
- quirk->data.flags = NV_3D0_WRITE;
- }
- }
- break;
- case NV_3D0_WRITE:
- quirk->data.flags = NV_3D0_NONE;
- if (addr == quirk->data.data_offset) {
- vfio_pci_write_config(pdev, quirk->data.address_val, data, size);
- trace_vfio_nvidia_3d0_quirk_write(data, size);
- return;
- }
- break;
- }
-
- vfio_vga_write(&vdev->vga.region[QEMU_PCI_VGA_IO_HI],
- addr + quirk->data.base_offset, data, size);
-}
-
-static const MemoryRegionOps vfio_nvidia_3d0_quirk = {
- .read = vfio_nvidia_3d0_quirk_read,
- .write = vfio_nvidia_3d0_quirk_write,
- .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static void vfio_vga_probe_nvidia_3d0_quirk(VFIOPCIDevice *vdev)
-{
- PCIDevice *pdev = &vdev->pdev;
- VFIOQuirk *quirk;
-
- if (pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_NVIDIA ||
- !vdev->bars[1].region.size) {
- return;
- }
-
- quirk = g_malloc0(sizeof(*quirk));
- quirk->vdev = vdev;
- quirk->data.base_offset = 0x10;
- quirk->data.address_offset = 4;
- quirk->data.address_size = 2;
- quirk->data.address_match = 0x1800;
- quirk->data.address_mask = PCI_CONFIG_SPACE_SIZE - 1;
- quirk->data.data_offset = 0;
- quirk->data.data_size = 4;
-
- memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_nvidia_3d0_quirk,
- quirk, "vfio-nvidia-3d0-quirk", 6);
- memory_region_add_subregion(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem,
- quirk->data.base_offset, &quirk->mem);
-
- QLIST_INSERT_HEAD(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].quirks,
- quirk, next);
-
- trace_vfio_vga_probe_nvidia_3d0_quirk(vdev->vbasedev.name);
-}
-
-/*
- * The second quirk is documented in envytools. The I/O port BAR5 is just
- * a set of address/data ports to the MMIO BARs. The BAR we care about is
- * again BAR0. This backdoor is apparently a bit newer than the one above
- * so we need to not only trap 256 bytes @0x1800, but all of PCI config
- * space, including extended space is available at the 4k @0x88000.
- */
-enum {
- NV_BAR5_ADDRESS = 0x1,
- NV_BAR5_ENABLE = 0x2,
- NV_BAR5_MASTER = 0x4,
- NV_BAR5_VALID = 0x7,
-};
-
-static void vfio_nvidia_bar5_window_quirk_write(void *opaque, hwaddr addr,
- uint64_t data, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
-
- switch (addr) {
- case 0x0:
- if (data & 0x1) {
- quirk->data.flags |= NV_BAR5_MASTER;
- } else {
- quirk->data.flags &= ~NV_BAR5_MASTER;
- }
- break;
- case 0x4:
- if (data & 0x1) {
- quirk->data.flags |= NV_BAR5_ENABLE;
- } else {
- quirk->data.flags &= ~NV_BAR5_ENABLE;
- }
- break;
- case 0x8:
- if (quirk->data.flags & NV_BAR5_MASTER) {
- if ((data & ~0xfff) == 0x88000) {
- quirk->data.flags |= NV_BAR5_ADDRESS;
- quirk->data.address_val = data & 0xfff;
- } else if ((data & ~0xff) == 0x1800) {
- quirk->data.flags |= NV_BAR5_ADDRESS;
- quirk->data.address_val = data & 0xff;
- } else {
- quirk->data.flags &= ~NV_BAR5_ADDRESS;
- }
- }
- break;
- }
-
- vfio_generic_window_quirk_write(opaque, addr, data, size);
-}
-
-static const MemoryRegionOps vfio_nvidia_bar5_window_quirk = {
- .read = vfio_generic_window_quirk_read,
- .write = vfio_nvidia_bar5_window_quirk_write,
- .valid.min_access_size = 4,
- .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static void vfio_probe_nvidia_bar5_window_quirk(VFIOPCIDevice *vdev, int nr)
-{
- PCIDevice *pdev = &vdev->pdev;
- VFIOQuirk *quirk;
-
- if (!vdev->has_vga || nr != 5 ||
- pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_NVIDIA) {
- return;
- }
-
- quirk = g_malloc0(sizeof(*quirk));
- quirk->vdev = vdev;
- quirk->data.read_flags = quirk->data.write_flags = NV_BAR5_VALID;
- quirk->data.address_offset = 0x8;
- quirk->data.address_size = 0; /* actually 4, but avoids generic code */
- quirk->data.data_offset = 0xc;
- quirk->data.data_size = 4;
- quirk->data.bar = nr;
-
- memory_region_init_io(&quirk->mem, OBJECT(vdev),
- &vfio_nvidia_bar5_window_quirk, quirk,
- "vfio-nvidia-bar5-window-quirk", 16);
- memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
- 0, &quirk->mem, 1);
-
- QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
-
- trace_vfio_probe_nvidia_bar5_window_quirk(vdev->vbasedev.name);
-}
-
-static void vfio_nvidia_88000_quirk_write(void *opaque, hwaddr addr,
- uint64_t data, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIOPCIDevice *vdev = quirk->vdev;
- PCIDevice *pdev = &vdev->pdev;
- hwaddr base = quirk->data.address_match & TARGET_PAGE_MASK;
-
- vfio_generic_quirk_write(opaque, addr, data, size);
-
- /*
- * Nvidia seems to acknowledge MSI interrupts by writing 0xff to the
- * MSI capability ID register. Both the ID and next register are
- * read-only, so we allow writes covering either of those to real hw.
- * NB - only fixed for the 0x88000 MMIO window.
- */
- if ((pdev->cap_present & QEMU_PCI_CAP_MSI) &&
- vfio_range_contained(addr, size, pdev->msi_cap, PCI_MSI_FLAGS)) {
- vfio_region_write(&vdev->bars[quirk->data.bar].region,
- addr + base, data, size);
- }
-}
-
-static const MemoryRegionOps vfio_nvidia_88000_quirk = {
- .read = vfio_generic_quirk_read,
- .write = vfio_nvidia_88000_quirk_write,
- .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-/*
- * Finally, BAR0 itself. We want to redirect any accesses to either
- * 0x1800 or 0x88000 through the PCI config space access functions.
- *
- * NB - quirk at a page granularity or else they don't seem to work when
- * BARs are mmap'd
- *
- * Here's offset 0x88000...
- */
-static void vfio_probe_nvidia_bar0_88000_quirk(VFIOPCIDevice *vdev, int nr)
-{
- PCIDevice *pdev = &vdev->pdev;
- VFIOQuirk *quirk;
- uint16_t vendor, class;
-
- vendor = pci_get_word(pdev->config + PCI_VENDOR_ID);
- class = pci_get_word(pdev->config + PCI_CLASS_DEVICE);
-
- if (nr != 0 || vendor != PCI_VENDOR_ID_NVIDIA ||
- class != PCI_CLASS_DISPLAY_VGA) {
- return;
- }
-
- quirk = g_malloc0(sizeof(*quirk));
- quirk->vdev = vdev;
- quirk->data.flags = quirk->data.read_flags = quirk->data.write_flags = 1;
- quirk->data.address_match = 0x88000;
- quirk->data.address_mask = PCIE_CONFIG_SPACE_SIZE - 1;
- quirk->data.bar = nr;
-
- memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_nvidia_88000_quirk,
- quirk, "vfio-nvidia-bar0-88000-quirk",
- TARGET_PAGE_ALIGN(quirk->data.address_mask + 1));
- memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
- quirk->data.address_match & TARGET_PAGE_MASK,
- &quirk->mem, 1);
-
- QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
-
- trace_vfio_probe_nvidia_bar0_88000_quirk(vdev->vbasedev.name);
-}
-
-/*
- * And here's the same for BAR0 offset 0x1800...
- */
-static void vfio_probe_nvidia_bar0_1800_quirk(VFIOPCIDevice *vdev, int nr)
-{
- PCIDevice *pdev = &vdev->pdev;
- VFIOQuirk *quirk;
-
- if (!vdev->has_vga || nr != 0 ||
- pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_NVIDIA) {
- return;
- }
-
- /* Log the chipset ID */
- trace_vfio_probe_nvidia_bar0_1800_quirk_id(
- (unsigned int)(vfio_region_read(&vdev->bars[0].region, 0, 4) >> 20)
- & 0xff);
-
- quirk = g_malloc0(sizeof(*quirk));
- quirk->vdev = vdev;
- quirk->data.flags = quirk->data.read_flags = quirk->data.write_flags = 1;
- quirk->data.address_match = 0x1800;
- quirk->data.address_mask = PCI_CONFIG_SPACE_SIZE - 1;
- quirk->data.bar = nr;
-
- memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_generic_quirk, quirk,
- "vfio-nvidia-bar0-1800-quirk",
- TARGET_PAGE_ALIGN(quirk->data.address_mask + 1));
- memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
- quirk->data.address_match & TARGET_PAGE_MASK,
- &quirk->mem, 1);
-
- QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
-
- trace_vfio_probe_nvidia_bar0_1800_quirk(vdev->vbasedev.name);
-}
-
-/*
- * TODO - Some Nvidia devices provide config access to their companion HDA
- * device and even to their parent bridge via these config space mirrors.
- * Add quirks for those regions.
- */
-
-/*
- * Common quirk probe entry points.
- */
-static void vfio_vga_quirk_setup(VFIOPCIDevice *vdev)
-{
- vfio_vga_probe_ati_3c3_quirk(vdev);
- vfio_vga_probe_nvidia_3d0_quirk(vdev);
-}
-
-static void vfio_vga_quirk_teardown(VFIOPCIDevice *vdev)
-{
- VFIOQuirk *quirk;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(vdev->vga.region); i++) {
- QLIST_FOREACH(quirk, &vdev->vga.region[i].quirks, next) {
- memory_region_del_subregion(&vdev->vga.region[i].mem, &quirk->mem);
- }
- }
-}
-
-static void vfio_vga_quirk_free(VFIOPCIDevice *vdev)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(vdev->vga.region); i++) {
- while (!QLIST_EMPTY(&vdev->vga.region[i].quirks)) {
- VFIOQuirk *quirk = QLIST_FIRST(&vdev->vga.region[i].quirks);
- object_unparent(OBJECT(&quirk->mem));
- QLIST_REMOVE(quirk, next);
- g_free(quirk);
- }
- }
-}
-
-static void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr)
-{
- vfio_probe_ati_bar4_window_quirk(vdev, nr);
- vfio_probe_ati_bar2_4000_quirk(vdev, nr);
- vfio_probe_nvidia_bar5_window_quirk(vdev, nr);
- vfio_probe_nvidia_bar0_88000_quirk(vdev, nr);
- vfio_probe_nvidia_bar0_1800_quirk(vdev, nr);
- vfio_probe_rtl8168_bar2_window_quirk(vdev, nr);
-}
-
-static void vfio_bar_quirk_teardown(VFIOPCIDevice *vdev, int nr)
-{
- VFIOBAR *bar = &vdev->bars[nr];
- VFIOQuirk *quirk;
-
- QLIST_FOREACH(quirk, &bar->quirks, next) {
- memory_region_del_subregion(&bar->region.mem, &quirk->mem);
- }
-}
-
-static void vfio_bar_quirk_free(VFIOPCIDevice *vdev, int nr)
-{
- VFIOBAR *bar = &vdev->bars[nr];
-
- while (!QLIST_EMPTY(&bar->quirks)) {
- VFIOQuirk *quirk = QLIST_FIRST(&bar->quirks);
- object_unparent(OBJECT(&quirk->mem));
- QLIST_REMOVE(quirk, next);
- g_free(quirk);
- }
-}
-
-/*
* PCI config space
*/
-static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len)
+uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len)
{
VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev);
uint32_t emu_bits = 0, emu_val = 0, phys_val = 0, val;
@@ -2082,9 +1077,8 @@ static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len)
ret = pread(vdev->vbasedev.fd, &phys_val, len,
vdev->config_offset + addr);
if (ret != len) {
- error_report("%s(%04x:%02x:%02x.%x, 0x%x, 0x%x) failed: %m",
- __func__, vdev->host.domain, vdev->host.bus,
- vdev->host.slot, vdev->host.function, addr, len);
+ error_report("%s(%s, 0x%x, 0x%x) failed: %m",
+ __func__, vdev->vbasedev.name, addr, len);
return -errno;
}
phys_val = le32_to_cpu(phys_val);
@@ -2097,8 +1091,8 @@ static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len)
return val;
}
-static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr,
- uint32_t val, int len)
+void vfio_pci_write_config(PCIDevice *pdev,
+ uint32_t addr, uint32_t val, int len)
{
VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev);
uint32_t val_le = cpu_to_le32(val);
@@ -2108,9 +1102,8 @@ static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr,
/* Write everything to VFIO, let it filter out what we can't write */
if (pwrite(vdev->vbasedev.fd, &val_le, len, vdev->config_offset + addr)
!= len) {
- error_report("%s(%04x:%02x:%02x.%x, 0x%x, 0x%x, 0x%x) failed: %m",
- __func__, vdev->host.domain, vdev->host.bus,
- vdev->host.slot, vdev->host.function, addr, val, len);
+ error_report("%s(%s, 0x%x, 0x%x, 0x%x) failed: %m",
+ __func__, vdev->vbasedev.name, addr, val, len);
}
/* MSI/MSI-X Enabling/Disabling */
@@ -2124,11 +1117,11 @@ static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr,
if (!was_enabled) {
if (is_enabled) {
- vfio_enable_msi(vdev);
+ vfio_msi_enable(vdev);
}
} else {
if (!is_enabled) {
- vfio_disable_msi(vdev);
+ vfio_msi_disable(vdev);
} else {
vfio_update_msi(vdev);
}
@@ -2142,9 +1135,9 @@ static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr,
is_enabled = msix_enabled(pdev);
if (!was_enabled && is_enabled) {
- vfio_enable_msix(vdev);
+ vfio_msix_enable(vdev);
} else if (was_enabled && !is_enabled) {
- vfio_disable_msix(vdev);
+ vfio_msix_disable(vdev);
}
} else {
/* Write everything to QEMU to keep emulated bits correct */
@@ -2163,17 +1156,17 @@ static void vfio_disable_interrupts(VFIOPCIDevice *vdev)
* disable MSI/X and then cleanup by disabling INTx.
*/
if (vdev->interrupt == VFIO_INT_MSIX) {
- vfio_disable_msix(vdev);
+ vfio_msix_disable(vdev);
} else if (vdev->interrupt == VFIO_INT_MSI) {
- vfio_disable_msi(vdev);
+ vfio_msi_disable(vdev);
}
if (vdev->interrupt == VFIO_INT_INTx) {
- vfio_disable_intx(vdev);
+ vfio_intx_disable(vdev);
}
}
-static int vfio_setup_msi(VFIOPCIDevice *vdev, int pos)
+static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos)
{
uint16_t ctrl;
bool msi_64bit, msi_maskbit;
@@ -2189,7 +1182,7 @@ static int vfio_setup_msi(VFIOPCIDevice *vdev, int pos)
msi_maskbit = !!(ctrl & PCI_MSI_FLAGS_MASKBIT);
entries = 1 << ((ctrl & PCI_MSI_FLAGS_QMASK) >> 1);
- trace_vfio_setup_msi(vdev->vbasedev.name, pos);
+ trace_vfio_msi_setup(vdev->vbasedev.name, pos);
ret = msi_init(&vdev->pdev, pos, entries, msi_64bit, msi_maskbit);
if (ret < 0) {
@@ -2204,6 +1197,74 @@ static int vfio_setup_msi(VFIOPCIDevice *vdev, int pos)
return 0;
}
+static void vfio_pci_fixup_msix_region(VFIOPCIDevice *vdev)
+{
+ off_t start, end;
+ VFIORegion *region = &vdev->bars[vdev->msix->table_bar].region;
+
+ /*
+ * We expect to find a single mmap covering the whole BAR, anything else
+ * means it's either unsupported or already setup.
+ */
+ if (region->nr_mmaps != 1 || region->mmaps[0].offset ||
+ region->size != region->mmaps[0].size) {
+ return;
+ }
+
+ /* MSI-X table start and end aligned to host page size */
+ start = vdev->msix->table_offset & qemu_real_host_page_mask;
+ end = REAL_HOST_PAGE_ALIGN((uint64_t)vdev->msix->table_offset +
+ (vdev->msix->entries * PCI_MSIX_ENTRY_SIZE));
+
+ /*
+ * Does the MSI-X table cover the beginning of the BAR? The whole BAR?
+ * NB - Host page size is necessarily a power of two and so is the PCI
+ * BAR (not counting EA yet), therefore if we have host page aligned
+ * @start and @end, then any remainder of the BAR before or after those
+ * must be at least host page sized and therefore mmap'able.
+ */
+ if (!start) {
+ if (end >= region->size) {
+ region->nr_mmaps = 0;
+ g_free(region->mmaps);
+ region->mmaps = NULL;
+ trace_vfio_msix_fixup(vdev->vbasedev.name,
+ vdev->msix->table_bar, 0, 0);
+ } else {
+ region->mmaps[0].offset = end;
+ region->mmaps[0].size = region->size - end;
+ trace_vfio_msix_fixup(vdev->vbasedev.name,
+ vdev->msix->table_bar, region->mmaps[0].offset,
+ region->mmaps[0].offset + region->mmaps[0].size);
+ }
+
+ /* Maybe it's aligned at the end of the BAR */
+ } else if (end >= region->size) {
+ region->mmaps[0].size = start;
+ trace_vfio_msix_fixup(vdev->vbasedev.name,
+ vdev->msix->table_bar, region->mmaps[0].offset,
+ region->mmaps[0].offset + region->mmaps[0].size);
+
+ /* Otherwise it must split the BAR */
+ } else {
+ region->nr_mmaps = 2;
+ region->mmaps = g_renew(VFIOMmap, region->mmaps, 2);
+
+ memcpy(&region->mmaps[1], &region->mmaps[0], sizeof(VFIOMmap));
+
+ region->mmaps[0].size = start;
+ trace_vfio_msix_fixup(vdev->vbasedev.name,
+ vdev->msix->table_bar, region->mmaps[0].offset,
+ region->mmaps[0].offset + region->mmaps[0].size);
+
+ region->mmaps[1].offset = end;
+ region->mmaps[1].size = region->size - end;
+ trace_vfio_msix_fixup(vdev->vbasedev.name,
+ vdev->msix->table_bar, region->mmaps[1].offset,
+ region->mmaps[1].offset + region->mmaps[1].size);
+ }
+}
+
/*
* We don't have any control over how pci_add_capability() inserts
* capabilities into the chain. In order to setup MSI-X we need a
@@ -2212,12 +1273,13 @@ static int vfio_setup_msi(VFIOPCIDevice *vdev, int pos)
* need to first look for where the MSI-X table lives. So we
* unfortunately split MSI-X setup across two functions.
*/
-static int vfio_early_setup_msix(VFIOPCIDevice *vdev)
+static int vfio_msix_early_setup(VFIOPCIDevice *vdev)
{
uint8_t pos;
uint16_t ctrl;
uint32_t table, pba;
int fd = vdev->vbasedev.fd;
+ VFIOMSIXInfo *msix;
pos = pci_find_capability(&vdev->pdev, PCI_CAP_ID_MSIX);
if (!pos) {
@@ -2225,7 +1287,7 @@ static int vfio_early_setup_msix(VFIOPCIDevice *vdev)
}
if (pread(fd, &ctrl, sizeof(ctrl),
- vdev->config_offset + pos + PCI_CAP_FLAGS) != sizeof(ctrl)) {
+ vdev->config_offset + pos + PCI_MSIX_FLAGS) != sizeof(ctrl)) {
return -errno;
}
@@ -2243,56 +1305,55 @@ static int vfio_early_setup_msix(VFIOPCIDevice *vdev)
table = le32_to_cpu(table);
pba = le32_to_cpu(pba);
- vdev->msix = g_malloc0(sizeof(*(vdev->msix)));
- vdev->msix->table_bar = table & PCI_MSIX_FLAGS_BIRMASK;
- vdev->msix->table_offset = table & ~PCI_MSIX_FLAGS_BIRMASK;
- vdev->msix->pba_bar = pba & PCI_MSIX_FLAGS_BIRMASK;
- vdev->msix->pba_offset = pba & ~PCI_MSIX_FLAGS_BIRMASK;
- vdev->msix->entries = (ctrl & PCI_MSIX_FLAGS_QSIZE) + 1;
+ msix = g_malloc0(sizeof(*msix));
+ msix->table_bar = table & PCI_MSIX_FLAGS_BIRMASK;
+ msix->table_offset = table & ~PCI_MSIX_FLAGS_BIRMASK;
+ msix->pba_bar = pba & PCI_MSIX_FLAGS_BIRMASK;
+ msix->pba_offset = pba & ~PCI_MSIX_FLAGS_BIRMASK;
+ msix->entries = (ctrl & PCI_MSIX_FLAGS_QSIZE) + 1;
/*
* Test the size of the pba_offset variable and catch if it extends outside
* of the specified BAR. If it is the case, we need to apply a hardware
* specific quirk if the device is known or we have a broken configuration.
*/
- if (vdev->msix->pba_offset >=
- vdev->bars[vdev->msix->pba_bar].region.size) {
-
- PCIDevice *pdev = &vdev->pdev;
- uint16_t vendor = pci_get_word(pdev->config + PCI_VENDOR_ID);
- uint16_t device = pci_get_word(pdev->config + PCI_DEVICE_ID);
-
+ if (msix->pba_offset >= vdev->bars[msix->pba_bar].region.size) {
/*
* Chelsio T5 Virtual Function devices are encoded as 0x58xx for T5
* adapters. The T5 hardware returns an incorrect value of 0x8000 for
* the VF PBA offset while the BAR itself is only 8k. The correct value
* is 0x1000, so we hard code that here.
*/
- if (vendor == PCI_VENDOR_ID_CHELSIO && (device & 0xff00) == 0x5800) {
- vdev->msix->pba_offset = 0x1000;
+ if (vdev->vendor_id == PCI_VENDOR_ID_CHELSIO &&
+ (vdev->device_id & 0xff00) == 0x5800) {
+ msix->pba_offset = 0x1000;
} else {
error_report("vfio: Hardware reports invalid configuration, "
"MSIX PBA outside of specified BAR");
+ g_free(msix);
return -EINVAL;
}
}
- trace_vfio_early_setup_msix(vdev->vbasedev.name, pos,
- vdev->msix->table_bar,
- vdev->msix->table_offset,
- vdev->msix->entries);
+ trace_vfio_msix_early_setup(vdev->vbasedev.name, pos, msix->table_bar,
+ msix->table_offset, msix->entries);
+ vdev->msix = msix;
+
+ vfio_pci_fixup_msix_region(vdev);
return 0;
}
-static int vfio_setup_msix(VFIOPCIDevice *vdev, int pos)
+static int vfio_msix_setup(VFIOPCIDevice *vdev, int pos)
{
int ret;
+ vdev->msix->pending = g_malloc0(BITS_TO_LONGS(vdev->msix->entries) *
+ sizeof(unsigned long));
ret = msix_init(&vdev->pdev, vdev->msix->entries,
- &vdev->bars[vdev->msix->table_bar].region.mem,
+ vdev->bars[vdev->msix->table_bar].region.mem,
vdev->msix->table_bar, vdev->msix->table_offset,
- &vdev->bars[vdev->msix->pba_bar].region.mem,
+ vdev->bars[vdev->msix->pba_bar].region.mem,
vdev->msix->pba_bar, vdev->msix->pba_offset, pos);
if (ret < 0) {
if (ret == -ENOTSUP) {
@@ -2302,6 +1363,24 @@ static int vfio_setup_msix(VFIOPCIDevice *vdev, int pos)
return ret;
}
+ /*
+ * The PCI spec suggests that devices provide additional alignment for
+ * MSI-X structures and avoid overlapping non-MSI-X related registers.
+ * For an assigned device, this hopefully means that emulation of MSI-X
+ * structures does not affect the performance of the device. If devices
+ * fail to provide that alignment, a significant performance penalty may
+ * result, for instance Mellanox MT27500 VFs:
+ * http://www.spinics.net/lists/kvm/msg125881.html
+ *
+ * The PBA is simply not that important for such a serious regression and
+ * most drivers do not appear to look at it. The solution for this is to
+ * disable the PBA MemoryRegion unless it's being used. We disable it
+ * here and only enable it if a masked vector fires through QEMU. As the
+ * vector-use notifier is called, which occurs on unmask, we test whether
+ * PBA emulation is needed and again disable if not.
+ */
+ memory_region_set_enabled(&vdev->pdev.msix_pba_mmio, false);
+
return 0;
}
@@ -2311,8 +1390,9 @@ static void vfio_teardown_msi(VFIOPCIDevice *vdev)
if (vdev->msix) {
msix_uninit(&vdev->pdev,
- &vdev->bars[vdev->msix->table_bar].region.mem,
- &vdev->bars[vdev->msix->pba_bar].region.mem);
+ vdev->bars[vdev->msix->table_bar].region.mem,
+ vdev->bars[vdev->msix->pba_bar].region.mem);
+ g_free(vdev->msix->pending);
}
}
@@ -2324,71 +1404,23 @@ static void vfio_mmap_set_enabled(VFIOPCIDevice *vdev, bool enabled)
int i;
for (i = 0; i < PCI_ROM_SLOT; i++) {
- VFIOBAR *bar = &vdev->bars[i];
-
- if (!bar->region.size) {
- continue;
- }
-
- memory_region_set_enabled(&bar->region.mmap_mem, enabled);
- if (vdev->msix && vdev->msix->table_bar == i) {
- memory_region_set_enabled(&vdev->msix->mmap_mem, enabled);
- }
+ vfio_region_mmaps_set_enabled(&vdev->bars[i].region, enabled);
}
}
-static void vfio_unregister_bar(VFIOPCIDevice *vdev, int nr)
+static void vfio_bar_setup(VFIOPCIDevice *vdev, int nr)
{
VFIOBAR *bar = &vdev->bars[nr];
- if (!bar->region.size) {
- return;
- }
-
- vfio_bar_quirk_teardown(vdev, nr);
-
- memory_region_del_subregion(&bar->region.mem, &bar->region.mmap_mem);
-
- if (vdev->msix && vdev->msix->table_bar == nr) {
- memory_region_del_subregion(&bar->region.mem, &vdev->msix->mmap_mem);
- }
-}
-
-static void vfio_unmap_bar(VFIOPCIDevice *vdev, int nr)
-{
- VFIOBAR *bar = &vdev->bars[nr];
-
- if (!bar->region.size) {
- return;
- }
-
- vfio_bar_quirk_free(vdev, nr);
-
- munmap(bar->region.mmap, memory_region_size(&bar->region.mmap_mem));
-
- if (vdev->msix && vdev->msix->table_bar == nr) {
- munmap(vdev->msix->mmap, memory_region_size(&vdev->msix->mmap_mem));
- }
-}
-
-static void vfio_map_bar(VFIOPCIDevice *vdev, int nr)
-{
- VFIOBAR *bar = &vdev->bars[nr];
- uint64_t size = bar->region.size;
- char name[64];
uint32_t pci_bar;
uint8_t type;
int ret;
/* Skip both unimplemented BARs and the upper half of 64bit BARS. */
- if (!size) {
+ if (!bar->region.size) {
return;
}
- snprintf(name, sizeof(name), "VFIO %04x:%02x:%02x.%x BAR %d",
- vdev->host.domain, vdev->host.bus, vdev->host.slot,
- vdev->host.function, nr);
-
/* Determine what type of BAR this is for registration */
ret = pread(vdev->vbasedev.fd, &pci_bar, sizeof(pci_bar),
vdev->config_offset + PCI_BASE_ADDRESS_0 + (4 * nr));
@@ -2403,102 +1435,78 @@ static void vfio_map_bar(VFIOPCIDevice *vdev, int nr)
type = pci_bar & (bar->ioport ? ~PCI_BASE_ADDRESS_IO_MASK :
~PCI_BASE_ADDRESS_MEM_MASK);
- /* A "slow" read/write mapping underlies all BARs */
- memory_region_init_io(&bar->region.mem, OBJECT(vdev), &vfio_region_ops,
- bar, name, size);
- pci_register_bar(&vdev->pdev, nr, type, &bar->region.mem);
-
- /*
- * We can't mmap areas overlapping the MSIX vector table, so we
- * potentially insert a direct-mapped subregion before and after it.
- */
- if (vdev->msix && vdev->msix->table_bar == nr) {
- size = vdev->msix->table_offset & qemu_real_host_page_mask;
- }
-
- strncat(name, " mmap", sizeof(name) - strlen(name) - 1);
- if (vfio_mmap_region(OBJECT(vdev), &bar->region, &bar->region.mem,
- &bar->region.mmap_mem, &bar->region.mmap,
- size, 0, name)) {
- error_report("%s unsupported. Performance may be slow", name);
- }
-
- if (vdev->msix && vdev->msix->table_bar == nr) {
- uint64_t start;
-
- start = REAL_HOST_PAGE_ALIGN((uint64_t)vdev->msix->table_offset +
- (vdev->msix->entries *
- PCI_MSIX_ENTRY_SIZE));
-
- size = start < bar->region.size ? bar->region.size - start : 0;
- strncat(name, " msix-hi", sizeof(name) - strlen(name) - 1);
- /* VFIOMSIXInfo contains another MemoryRegion for this mapping */
- if (vfio_mmap_region(OBJECT(vdev), &bar->region, &bar->region.mem,
- &vdev->msix->mmap_mem,
- &vdev->msix->mmap, size, start, name)) {
- error_report("%s unsupported. Performance may be slow", name);
- }
+ if (vfio_region_mmap(&bar->region)) {
+ error_report("Failed to mmap %s BAR %d. Performance may be slow",
+ vdev->vbasedev.name, nr);
}
vfio_bar_quirk_setup(vdev, nr);
+
+ pci_register_bar(&vdev->pdev, nr, type, bar->region.mem);
}
-static void vfio_map_bars(VFIOPCIDevice *vdev)
+static void vfio_bars_setup(VFIOPCIDevice *vdev)
{
int i;
for (i = 0; i < PCI_ROM_SLOT; i++) {
- vfio_map_bar(vdev, i);
+ vfio_bar_setup(vdev, i);
}
- if (vdev->has_vga) {
- memory_region_init_io(&vdev->vga.region[QEMU_PCI_VGA_MEM].mem,
+ if (vdev->vga) {
+ memory_region_init_io(&vdev->vga->region[QEMU_PCI_VGA_MEM].mem,
OBJECT(vdev), &vfio_vga_ops,
- &vdev->vga.region[QEMU_PCI_VGA_MEM],
+ &vdev->vga->region[QEMU_PCI_VGA_MEM],
"vfio-vga-mmio@0xa0000",
QEMU_PCI_VGA_MEM_SIZE);
- memory_region_init_io(&vdev->vga.region[QEMU_PCI_VGA_IO_LO].mem,
+ memory_region_init_io(&vdev->vga->region[QEMU_PCI_VGA_IO_LO].mem,
OBJECT(vdev), &vfio_vga_ops,
- &vdev->vga.region[QEMU_PCI_VGA_IO_LO],
+ &vdev->vga->region[QEMU_PCI_VGA_IO_LO],
"vfio-vga-io@0x3b0",
QEMU_PCI_VGA_IO_LO_SIZE);
- memory_region_init_io(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem,
+ memory_region_init_io(&vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem,
OBJECT(vdev), &vfio_vga_ops,
- &vdev->vga.region[QEMU_PCI_VGA_IO_HI],
+ &vdev->vga->region[QEMU_PCI_VGA_IO_HI],
"vfio-vga-io@0x3c0",
QEMU_PCI_VGA_IO_HI_SIZE);
- pci_register_vga(&vdev->pdev, &vdev->vga.region[QEMU_PCI_VGA_MEM].mem,
- &vdev->vga.region[QEMU_PCI_VGA_IO_LO].mem,
- &vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem);
+ pci_register_vga(&vdev->pdev, &vdev->vga->region[QEMU_PCI_VGA_MEM].mem,
+ &vdev->vga->region[QEMU_PCI_VGA_IO_LO].mem,
+ &vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem);
vfio_vga_quirk_setup(vdev);
}
}
-static void vfio_unregister_bars(VFIOPCIDevice *vdev)
+static void vfio_bars_exit(VFIOPCIDevice *vdev)
{
int i;
for (i = 0; i < PCI_ROM_SLOT; i++) {
- vfio_unregister_bar(vdev, i);
+ vfio_bar_quirk_exit(vdev, i);
+ vfio_region_exit(&vdev->bars[i].region);
}
- if (vdev->has_vga) {
- vfio_vga_quirk_teardown(vdev);
+ if (vdev->vga) {
pci_unregister_vga(&vdev->pdev);
+ vfio_vga_quirk_exit(vdev);
}
}
-static void vfio_unmap_bars(VFIOPCIDevice *vdev)
+static void vfio_bars_finalize(VFIOPCIDevice *vdev)
{
int i;
for (i = 0; i < PCI_ROM_SLOT; i++) {
- vfio_unmap_bar(vdev, i);
+ vfio_bar_quirk_finalize(vdev, i);
+ vfio_region_finalize(&vdev->bars[i].region);
}
- if (vdev->has_vga) {
- vfio_vga_quirk_free(vdev);
+ if (vdev->vga) {
+ vfio_vga_quirk_finalize(vdev);
+ for (i = 0; i < ARRAY_SIZE(vdev->vga->region); i++) {
+ object_unparent(OBJECT(&vdev->vga->region[i].mem));
+ }
+ g_free(vdev->vga);
}
}
@@ -2507,10 +1515,11 @@ static void vfio_unmap_bars(VFIOPCIDevice *vdev)
*/
static uint8_t vfio_std_cap_max_size(PCIDevice *pdev, uint8_t pos)
{
- uint8_t tmp, next = 0xff;
+ uint8_t tmp;
+ uint16_t next = PCI_CONFIG_SPACE_SIZE;
for (tmp = pdev->config[PCI_CAPABILITY_LIST]; tmp;
- tmp = pdev->config[tmp + 1]) {
+ tmp = pdev->config[tmp + PCI_CAP_LIST_NEXT]) {
if (tmp > pos && tmp < next) {
next = tmp;
}
@@ -2563,13 +1572,38 @@ static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size)
}
if (!pci_bus_is_express(vdev->pdev.bus)) {
+ PCIBus *bus = vdev->pdev.bus;
+ PCIDevice *bridge;
+
/*
- * Use express capability as-is on PCI bus. It doesn't make much
- * sense to even expose, but some drivers (ex. tg3) depend on it
- * and guests don't seem to be particular about it. We'll need
- * to revist this or force express devices to express buses if we
- * ever expose an IOMMU to the guest.
+ * Traditionally PCI device assignment exposes the PCIe capability
+ * as-is on non-express buses. The reason being that some drivers
+ * simply assume that it's there, for example tg3. However when
+ * we're running on a native PCIe machine type, like Q35, we need
+ * to hide the PCIe capability. The reason for this is twofold;
+ * first Windows guests get a Code 10 error when the PCIe capability
+ * is exposed in this configuration. Therefore express devices won't
+ * work at all unless they're attached to express buses in the VM.
+ * Second, a native PCIe machine introduces the possibility of fine
+ * granularity IOMMUs supporting both translation and isolation.
+ * Guest code to discover the IOMMU visibility of a device, such as
+ * IOMMU grouping code on Linux, is very aware of device types and
+ * valid transitions between bus types. An express device on a non-
+ * express bus is not a valid combination on bare metal systems.
+ *
+ * Drivers that require a PCIe capability to make the device
+ * functional are simply going to need to have their devices placed
+ * on a PCIe bus in the VM.
*/
+ while (!pci_bus_is_root(bus)) {
+ bridge = pci_bridge_get_device(bus);
+ bus = bridge->bus;
+ }
+
+ if (pci_bus_is_express(bus)) {
+ return 0;
+ }
+
} else if (pci_bus_is_root(vdev->pdev.bus)) {
/*
* On a Root Complex bus Endpoints become Root Complex Integrated
@@ -2674,7 +1708,7 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos)
int ret;
cap_id = pdev->config[pos];
- next = pdev->config[pos + 1];
+ next = pdev->config[pos + PCI_CAP_LIST_NEXT];
/*
* If it becomes important to configure capabilities to their actual
@@ -2688,7 +1722,7 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos)
* pci_add_capability always inserts the new capability at the head
* of the chain. Therefore to end up with a chain that matches the
* physical device, we insert from the end by making this recursive.
- * This is also why we pre-caclulate size above as cached config space
+ * This is also why we pre-calculate size above as cached config space
* will be changed as we unwind the stack.
*/
if (next) {
@@ -2704,18 +1738,18 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos)
}
/* Use emulated next pointer to allow dropping caps */
- pci_set_byte(vdev->emulated_config_bits + pos + 1, 0xff);
+ pci_set_byte(vdev->emulated_config_bits + pos + PCI_CAP_LIST_NEXT, 0xff);
switch (cap_id) {
case PCI_CAP_ID_MSI:
- ret = vfio_setup_msi(vdev, pos);
+ ret = vfio_msi_setup(vdev, pos);
break;
case PCI_CAP_ID_EXP:
vfio_check_pcie_flr(vdev, pos);
ret = vfio_setup_pcie_cap(vdev, pos, size);
break;
case PCI_CAP_ID_MSIX:
- ret = vfio_setup_msix(vdev, pos);
+ ret = vfio_msix_setup(vdev, pos);
break;
case PCI_CAP_ID_PM:
vfio_check_pm_reset(vdev, pos);
@@ -2732,9 +1766,8 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos)
}
if (ret < 0) {
- error_report("vfio: %04x:%02x:%02x.%x Error adding PCI capability "
- "0x%x[0x%x]@0x%x: %d", vdev->host.domain,
- vdev->host.bus, vdev->host.slot, vdev->host.function,
+ error_report("vfio: %s Error adding PCI capability "
+ "0x%x[0x%x]@0x%x: %d", vdev->vbasedev.name,
cap_id, size, pos, ret);
return ret;
}
@@ -2793,14 +1826,17 @@ static void vfio_pci_pre_reset(VFIOPCIDevice *vdev)
static void vfio_pci_post_reset(VFIOPCIDevice *vdev)
{
- vfio_enable_intx(vdev);
+ vfio_intx_enable(vdev);
}
-static bool vfio_pci_host_match(PCIHostDeviceAddress *host1,
- PCIHostDeviceAddress *host2)
+static bool vfio_pci_host_match(PCIHostDeviceAddress *addr, const char *name)
{
- return (host1->domain == host2->domain && host1->bus == host2->bus &&
- host1->slot == host2->slot && host1->function == host2->function);
+ char tmp[13];
+
+ sprintf(tmp, "%04x:%02x:%02x.%1x", addr->domain,
+ addr->bus, addr->slot, addr->function);
+
+ return (strcmp(tmp, name) == 0);
}
static int vfio_pci_hot_reset(VFIOPCIDevice *vdev, bool single)
@@ -2825,9 +1861,8 @@ static int vfio_pci_hot_reset(VFIOPCIDevice *vdev, bool single)
if (ret && errno != ENOSPC) {
ret = -errno;
if (!vdev->has_pm_reset) {
- error_report("vfio: Cannot reset device %04x:%02x:%02x.%x, "
- "no available reset mechanism.", vdev->host.domain,
- vdev->host.bus, vdev->host.slot, vdev->host.function);
+ error_report("vfio: Cannot reset device %s, "
+ "no available reset mechanism.", vdev->vbasedev.name);
}
goto out_single;
}
@@ -2860,7 +1895,7 @@ static int vfio_pci_hot_reset(VFIOPCIDevice *vdev, bool single)
trace_vfio_pci_hot_reset_dep_devices(host.domain,
host.bus, host.slot, host.function, devices[i].group_id);
- if (vfio_pci_host_match(&host, &vdev->host)) {
+ if (vfio_pci_host_match(&host, vdev->vbasedev.name)) {
continue;
}
@@ -2886,7 +1921,7 @@ static int vfio_pci_hot_reset(VFIOPCIDevice *vdev, bool single)
continue;
}
tmp = container_of(vbasedev_iter, VFIOPCIDevice, vbasedev);
- if (vfio_pci_host_match(&host, &tmp->host)) {
+ if (vfio_pci_host_match(&host, tmp->vbasedev.name)) {
if (single) {
ret = -EINVAL;
goto out_single;
@@ -2948,7 +1983,7 @@ out:
host.slot = PCI_SLOT(devices[i].devfn);
host.function = PCI_FUNC(devices[i].devfn);
- if (vfio_pci_host_match(&host, &vdev->host)) {
+ if (vfio_pci_host_match(&host, vdev->vbasedev.name)) {
continue;
}
@@ -2967,7 +2002,7 @@ out:
continue;
}
tmp = container_of(vbasedev_iter, VFIOPCIDevice, vbasedev);
- if (vfio_pci_host_match(&host, &tmp->host)) {
+ if (vfio_pci_host_match(&host, tmp->vbasedev.name)) {
vfio_pci_post_reset(tmp);
break;
}
@@ -3017,13 +2052,59 @@ static void vfio_pci_compute_needs_reset(VFIODevice *vbasedev)
static VFIODeviceOps vfio_pci_ops = {
.vfio_compute_needs_reset = vfio_pci_compute_needs_reset,
.vfio_hot_reset_multi = vfio_pci_hot_reset_multi,
- .vfio_eoi = vfio_eoi,
+ .vfio_eoi = vfio_intx_eoi,
};
+int vfio_populate_vga(VFIOPCIDevice *vdev)
+{
+ VFIODevice *vbasedev = &vdev->vbasedev;
+ struct vfio_region_info *reg_info;
+ int ret;
+
+ if (vbasedev->num_regions > VFIO_PCI_VGA_REGION_INDEX) {
+ ret = vfio_get_region_info(vbasedev,
+ VFIO_PCI_VGA_REGION_INDEX, &reg_info);
+ if (ret) {
+ return ret;
+ }
+
+ if (!(reg_info->flags & VFIO_REGION_INFO_FLAG_READ) ||
+ !(reg_info->flags & VFIO_REGION_INFO_FLAG_WRITE) ||
+ reg_info->size < 0xbffff + 1) {
+ error_report("vfio: Unexpected VGA info, flags 0x%lx, size 0x%lx",
+ (unsigned long)reg_info->flags,
+ (unsigned long)reg_info->size);
+ g_free(reg_info);
+ return -EINVAL;
+ }
+
+ vdev->vga = g_new0(VFIOVGA, 1);
+
+ vdev->vga->fd_offset = reg_info->offset;
+ vdev->vga->fd = vdev->vbasedev.fd;
+
+ g_free(reg_info);
+
+ vdev->vga->region[QEMU_PCI_VGA_MEM].offset = QEMU_PCI_VGA_MEM_BASE;
+ vdev->vga->region[QEMU_PCI_VGA_MEM].nr = QEMU_PCI_VGA_MEM;
+ QLIST_INIT(&vdev->vga->region[QEMU_PCI_VGA_MEM].quirks);
+
+ vdev->vga->region[QEMU_PCI_VGA_IO_LO].offset = QEMU_PCI_VGA_IO_LO_BASE;
+ vdev->vga->region[QEMU_PCI_VGA_IO_LO].nr = QEMU_PCI_VGA_IO_LO;
+ QLIST_INIT(&vdev->vga->region[QEMU_PCI_VGA_IO_LO].quirks);
+
+ vdev->vga->region[QEMU_PCI_VGA_IO_HI].offset = QEMU_PCI_VGA_IO_HI_BASE;
+ vdev->vga->region[QEMU_PCI_VGA_IO_HI].nr = QEMU_PCI_VGA_IO_HI;
+ QLIST_INIT(&vdev->vga->region[QEMU_PCI_VGA_IO_HI].quirks);
+ }
+
+ return 0;
+}
+
static int vfio_populate_device(VFIOPCIDevice *vdev)
{
VFIODevice *vbasedev = &vdev->vbasedev;
- struct vfio_region_info reg_info = { .argsz = sizeof(reg_info) };
+ struct vfio_region_info *reg_info;
struct vfio_irq_info irq_info = { .argsz = sizeof(irq_info) };
int i, ret = -1;
@@ -3045,85 +2126,47 @@ static int vfio_populate_device(VFIOPCIDevice *vdev)
}
for (i = VFIO_PCI_BAR0_REGION_INDEX; i < VFIO_PCI_ROM_REGION_INDEX; i++) {
- reg_info.index = i;
+ char *name = g_strdup_printf("%s BAR %d", vbasedev->name, i);
+
+ ret = vfio_region_setup(OBJECT(vdev), vbasedev,
+ &vdev->bars[i].region, i, name);
+ g_free(name);
- ret = ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, &reg_info);
if (ret) {
error_report("vfio: Error getting region %d info: %m", i);
goto error;
}
- trace_vfio_populate_device_region(vbasedev->name, i,
- (unsigned long)reg_info.size,
- (unsigned long)reg_info.offset,
- (unsigned long)reg_info.flags);
-
- vdev->bars[i].region.vbasedev = vbasedev;
- vdev->bars[i].region.flags = reg_info.flags;
- vdev->bars[i].region.size = reg_info.size;
- vdev->bars[i].region.fd_offset = reg_info.offset;
- vdev->bars[i].region.nr = i;
QLIST_INIT(&vdev->bars[i].quirks);
}
- reg_info.index = VFIO_PCI_CONFIG_REGION_INDEX;
-
- ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_REGION_INFO, &reg_info);
+ ret = vfio_get_region_info(vbasedev,
+ VFIO_PCI_CONFIG_REGION_INDEX, &reg_info);
if (ret) {
error_report("vfio: Error getting config info: %m");
goto error;
}
trace_vfio_populate_device_config(vdev->vbasedev.name,
- (unsigned long)reg_info.size,
- (unsigned long)reg_info.offset,
- (unsigned long)reg_info.flags);
+ (unsigned long)reg_info->size,
+ (unsigned long)reg_info->offset,
+ (unsigned long)reg_info->flags);
- vdev->config_size = reg_info.size;
+ vdev->config_size = reg_info->size;
if (vdev->config_size == PCI_CONFIG_SPACE_SIZE) {
vdev->pdev.cap_present &= ~QEMU_PCI_CAP_EXPRESS;
}
- vdev->config_offset = reg_info.offset;
+ vdev->config_offset = reg_info->offset;
- if ((vdev->features & VFIO_FEATURE_ENABLE_VGA) &&
- vbasedev->num_regions > VFIO_PCI_VGA_REGION_INDEX) {
- struct vfio_region_info vga_info = {
- .argsz = sizeof(vga_info),
- .index = VFIO_PCI_VGA_REGION_INDEX,
- };
+ g_free(reg_info);
- ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_REGION_INFO, &vga_info);
+ if (vdev->features & VFIO_FEATURE_ENABLE_VGA) {
+ ret = vfio_populate_vga(vdev);
if (ret) {
error_report(
"vfio: Device does not support requested feature x-vga");
goto error;
}
-
- if (!(vga_info.flags & VFIO_REGION_INFO_FLAG_READ) ||
- !(vga_info.flags & VFIO_REGION_INFO_FLAG_WRITE) ||
- vga_info.size < 0xbffff + 1) {
- error_report("vfio: Unexpected VGA info, flags 0x%lx, size 0x%lx",
- (unsigned long)vga_info.flags,
- (unsigned long)vga_info.size);
- goto error;
- }
-
- vdev->vga.fd_offset = vga_info.offset;
- vdev->vga.fd = vdev->vbasedev.fd;
-
- vdev->vga.region[QEMU_PCI_VGA_MEM].offset = QEMU_PCI_VGA_MEM_BASE;
- vdev->vga.region[QEMU_PCI_VGA_MEM].nr = QEMU_PCI_VGA_MEM;
- QLIST_INIT(&vdev->vga.region[QEMU_PCI_VGA_MEM].quirks);
-
- vdev->vga.region[QEMU_PCI_VGA_IO_LO].offset = QEMU_PCI_VGA_IO_LO_BASE;
- vdev->vga.region[QEMU_PCI_VGA_IO_LO].nr = QEMU_PCI_VGA_IO_LO;
- QLIST_INIT(&vdev->vga.region[QEMU_PCI_VGA_IO_LO].quirks);
-
- vdev->vga.region[QEMU_PCI_VGA_IO_HI].offset = QEMU_PCI_VGA_IO_HI_BASE;
- vdev->vga.region[QEMU_PCI_VGA_IO_HI].nr = QEMU_PCI_VGA_IO_HI;
- QLIST_INIT(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].quirks);
-
- vdev->has_vga = true;
}
irq_info.index = VFIO_PCI_ERR_IRQ_INDEX;
@@ -3148,11 +2191,8 @@ error:
static void vfio_put_device(VFIOPCIDevice *vdev)
{
g_free(vdev->vbasedev.name);
- if (vdev->msix) {
- object_unparent(OBJECT(&vdev->msix->mmap_mem));
- g_free(vdev->msix);
- vdev->msix = NULL;
- }
+ g_free(vdev->msix);
+
vfio_put_base_device(&vdev->vbasedev);
}
@@ -3173,10 +2213,7 @@ static void vfio_err_notifier_handler(void *opaque)
* guest to contain the error.
*/
- error_report("%s(%04x:%02x:%02x.%x) Unrecoverable error detected. "
- "Please collect any data possible and then kill the guest",
- __func__, vdev->host.domain, vdev->host.bus,
- vdev->host.slot, vdev->host.function);
+ error_report("%s(%s) Unrecoverable error detected. Please collect any data possible and then kill the guest", __func__, vdev->vbasedev.name);
vm_stop(RUN_STATE_INTERNAL_ERROR);
}
@@ -3352,203 +2389,48 @@ static void vfio_unregister_req_notifier(VFIOPCIDevice *vdev)
vdev->req_enabled = false;
}
-/*
- * AMD Radeon PCI config reset, based on Linux:
- * drivers/gpu/drm/radeon/ci_smc.c:ci_is_smc_running()
- * drivers/gpu/drm/radeon/radeon_device.c:radeon_pci_config_reset
- * drivers/gpu/drm/radeon/ci_smc.c:ci_reset_smc()
- * drivers/gpu/drm/radeon/ci_smc.c:ci_stop_smc_clock()
- * IDs: include/drm/drm_pciids.h
- * Registers: http://cgit.freedesktop.org/~agd5f/linux/commit/?id=4e2aa447f6f0
- *
- * Bonaire and Hawaii GPUs do not respond to a bus reset. This is a bug in the
- * hardware that should be fixed on future ASICs. The symptom of this is that
- * once the accerlated driver loads, Windows guests will bsod on subsequent
- * attmpts to load the driver, such as after VM reset or shutdown/restart. To
- * work around this, we do an AMD specific PCI config reset, followed by an SMC
- * reset. The PCI config reset only works if SMC firmware is running, so we
- * have a dependency on the state of the device as to whether this reset will
- * be effective. There are still cases where we won't be able to kick the
- * device into working, but this greatly improves the usability overall. The
- * config reset magic is relatively common on AMD GPUs, but the setup and SMC
- * poking is largely ASIC specific.
- */
-static bool vfio_radeon_smc_is_running(VFIOPCIDevice *vdev)
-{
- uint32_t clk, pc_c;
-
- /*
- * Registers 200h and 204h are index and data registers for acessing
- * indirect configuration registers within the device.
- */
- vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000004, 4);
- clk = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
- vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000370, 4);
- pc_c = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
-
- return (!(clk & 1) && (0x20100 <= pc_c));
-}
-
-/*
- * The scope of a config reset is controlled by a mode bit in the misc register
- * and a fuse, exposed as a bit in another register. The fuse is the default
- * (0 = GFX, 1 = whole GPU), the misc bit is a toggle, with the forumula
- * scope = !(misc ^ fuse), where the resulting scope is defined the same as
- * the fuse. A truth table therefore tells us that if misc == fuse, we need
- * to flip the value of the bit in the misc register.
- */
-static void vfio_radeon_set_gfx_only_reset(VFIOPCIDevice *vdev)
-{
- uint32_t misc, fuse;
- bool a, b;
-
- vfio_region_write(&vdev->bars[5].region, 0x200, 0xc00c0000, 4);
- fuse = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
- b = fuse & 64;
-
- vfio_region_write(&vdev->bars[5].region, 0x200, 0xc0000010, 4);
- misc = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
- a = misc & 2;
-
- if (a == b) {
- vfio_region_write(&vdev->bars[5].region, 0x204, misc ^ 2, 4);
- vfio_region_read(&vdev->bars[5].region, 0x204, 4); /* flush */
- }
-}
-
-static int vfio_radeon_reset(VFIOPCIDevice *vdev)
-{
- PCIDevice *pdev = &vdev->pdev;
- int i, ret = 0;
- uint32_t data;
-
- /* Defer to a kernel implemented reset */
- if (vdev->vbasedev.reset_works) {
- return -ENODEV;
- }
-
- /* Enable only memory BAR access */
- vfio_pci_write_config(pdev, PCI_COMMAND, PCI_COMMAND_MEMORY, 2);
-
- /* Reset only works if SMC firmware is loaded and running */
- if (!vfio_radeon_smc_is_running(vdev)) {
- ret = -EINVAL;
- goto out;
- }
-
- /* Make sure only the GFX function is reset */
- vfio_radeon_set_gfx_only_reset(vdev);
-
- /* AMD PCI config reset */
- vfio_pci_write_config(pdev, 0x7c, 0x39d5e86b, 4);
- usleep(100);
-
- /* Read back the memory size to make sure we're out of reset */
- for (i = 0; i < 100000; i++) {
- if (vfio_region_read(&vdev->bars[5].region, 0x5428, 4) != 0xffffffff) {
- break;
- }
- usleep(1);
- }
-
- /* Reset SMC */
- vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000000, 4);
- data = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
- data |= 1;
- vfio_region_write(&vdev->bars[5].region, 0x204, data, 4);
-
- /* Disable SMC clock */
- vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000004, 4);
- data = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
- data |= 1;
- vfio_region_write(&vdev->bars[5].region, 0x204, data, 4);
-
-out:
- /* Restore PCI command register */
- vfio_pci_write_config(pdev, PCI_COMMAND, 0, 2);
-
- return ret;
-}
-
-static void vfio_setup_resetfn(VFIOPCIDevice *vdev)
-{
- PCIDevice *pdev = &vdev->pdev;
- uint16_t vendor, device;
-
- vendor = pci_get_word(pdev->config + PCI_VENDOR_ID);
- device = pci_get_word(pdev->config + PCI_DEVICE_ID);
-
- switch (vendor) {
- case 0x1002:
- switch (device) {
- /* Bonaire */
- case 0x6649: /* Bonaire [FirePro W5100] */
- case 0x6650:
- case 0x6651:
- case 0x6658: /* Bonaire XTX [Radeon R7 260X] */
- case 0x665c: /* Bonaire XT [Radeon HD 7790/8770 / R9 260 OEM] */
- case 0x665d: /* Bonaire [Radeon R7 200 Series] */
- /* Hawaii */
- case 0x67A0: /* Hawaii XT GL [FirePro W9100] */
- case 0x67A1: /* Hawaii PRO GL [FirePro W8100] */
- case 0x67A2:
- case 0x67A8:
- case 0x67A9:
- case 0x67AA:
- case 0x67B0: /* Hawaii XT [Radeon R9 290X] */
- case 0x67B1: /* Hawaii PRO [Radeon R9 290] */
- case 0x67B8:
- case 0x67B9:
- case 0x67BA:
- case 0x67BE:
- vdev->resetfn = vfio_radeon_reset;
- break;
- }
- break;
- }
-}
-
static int vfio_initfn(PCIDevice *pdev)
{
VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev);
VFIODevice *vbasedev_iter;
VFIOGroup *group;
- char path[PATH_MAX], iommu_group_path[PATH_MAX], *group_name;
+ char *tmp, group_path[PATH_MAX], *group_name;
ssize_t len;
struct stat st;
int groupid;
int ret;
- /* Check that the host device exists */
- snprintf(path, sizeof(path),
- "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/",
- vdev->host.domain, vdev->host.bus, vdev->host.slot,
- vdev->host.function);
- if (stat(path, &st) < 0) {
- error_report("vfio: error: no such host device: %s", path);
+ if (!vdev->vbasedev.sysfsdev) {
+ vdev->vbasedev.sysfsdev =
+ g_strdup_printf("/sys/bus/pci/devices/%04x:%02x:%02x.%01x",
+ vdev->host.domain, vdev->host.bus,
+ vdev->host.slot, vdev->host.function);
+ }
+
+ if (stat(vdev->vbasedev.sysfsdev, &st) < 0) {
+ error_report("vfio: error: no such host device: %s",
+ vdev->vbasedev.sysfsdev);
return -errno;
}
+ vdev->vbasedev.name = g_strdup(basename(vdev->vbasedev.sysfsdev));
vdev->vbasedev.ops = &vfio_pci_ops;
-
vdev->vbasedev.type = VFIO_DEVICE_TYPE_PCI;
- vdev->vbasedev.name = g_strdup_printf("%04x:%02x:%02x.%01x",
- vdev->host.domain, vdev->host.bus,
- vdev->host.slot, vdev->host.function);
- strncat(path, "iommu_group", sizeof(path) - strlen(path) - 1);
+ tmp = g_strdup_printf("%s/iommu_group", vdev->vbasedev.sysfsdev);
+ len = readlink(tmp, group_path, sizeof(group_path));
+ g_free(tmp);
- len = readlink(path, iommu_group_path, sizeof(path));
- if (len <= 0 || len >= sizeof(path)) {
+ if (len <= 0 || len >= sizeof(group_path)) {
error_report("vfio: error no iommu_group for device");
return len < 0 ? -errno : -ENAMETOOLONG;
}
- iommu_group_path[len] = 0;
- group_name = basename(iommu_group_path);
+ group_path[len] = 0;
+ group_name = basename(group_path);
if (sscanf(group_name, "%d", &groupid) != 1) {
- error_report("vfio: error reading %s: %m", path);
+ error_report("vfio: error reading %s: %m", group_path);
return -errno;
}
@@ -3560,21 +2442,18 @@ static int vfio_initfn(PCIDevice *pdev)
return -ENOENT;
}
- snprintf(path, sizeof(path), "%04x:%02x:%02x.%01x",
- vdev->host.domain, vdev->host.bus, vdev->host.slot,
- vdev->host.function);
-
QLIST_FOREACH(vbasedev_iter, &group->device_list, next) {
if (strcmp(vbasedev_iter->name, vdev->vbasedev.name) == 0) {
- error_report("vfio: error: device %s is already attached", path);
+ error_report("vfio: error: device %s is already attached",
+ vdev->vbasedev.name);
vfio_put_group(group);
return -EBUSY;
}
}
- ret = vfio_get_device(group, path, &vdev->vbasedev);
+ ret = vfio_get_device(group, vdev->vbasedev.name, &vdev->vbasedev);
if (ret) {
- error_report("vfio: failed to get device %s", path);
+ error_report("vfio: failed to get device %s", vdev->vbasedev.name);
vfio_put_group(group);
return ret;
}
@@ -3600,6 +2479,54 @@ static int vfio_initfn(PCIDevice *pdev)
/* QEMU can choose to expose the ROM or not */
memset(vdev->emulated_config_bits + PCI_ROM_ADDRESS, 0xff, 4);
+ /*
+ * The PCI spec reserves vendor ID 0xffff as an invalid value. The
+ * device ID is managed by the vendor and need only be a 16-bit value.
+ * Allow any 16-bit value for subsystem so they can be hidden or changed.
+ */
+ if (vdev->vendor_id != PCI_ANY_ID) {
+ if (vdev->vendor_id >= 0xffff) {
+ error_report("vfio: Invalid PCI vendor ID provided");
+ return -EINVAL;
+ }
+ vfio_add_emulated_word(vdev, PCI_VENDOR_ID, vdev->vendor_id, ~0);
+ trace_vfio_pci_emulated_vendor_id(vdev->vbasedev.name, vdev->vendor_id);
+ } else {
+ vdev->vendor_id = pci_get_word(pdev->config + PCI_VENDOR_ID);
+ }
+
+ if (vdev->device_id != PCI_ANY_ID) {
+ if (vdev->device_id > 0xffff) {
+ error_report("vfio: Invalid PCI device ID provided");
+ return -EINVAL;
+ }
+ vfio_add_emulated_word(vdev, PCI_DEVICE_ID, vdev->device_id, ~0);
+ trace_vfio_pci_emulated_device_id(vdev->vbasedev.name, vdev->device_id);
+ } else {
+ vdev->device_id = pci_get_word(pdev->config + PCI_DEVICE_ID);
+ }
+
+ if (vdev->sub_vendor_id != PCI_ANY_ID) {
+ if (vdev->sub_vendor_id > 0xffff) {
+ error_report("vfio: Invalid PCI subsystem vendor ID provided");
+ return -EINVAL;
+ }
+ vfio_add_emulated_word(vdev, PCI_SUBSYSTEM_VENDOR_ID,
+ vdev->sub_vendor_id, ~0);
+ trace_vfio_pci_emulated_sub_vendor_id(vdev->vbasedev.name,
+ vdev->sub_vendor_id);
+ }
+
+ if (vdev->sub_device_id != PCI_ANY_ID) {
+ if (vdev->sub_device_id > 0xffff) {
+ error_report("vfio: Invalid PCI subsystem device ID provided");
+ return -EINVAL;
+ }
+ vfio_add_emulated_word(vdev, PCI_SUBSYSTEM_ID, vdev->sub_device_id, ~0);
+ trace_vfio_pci_emulated_sub_device_id(vdev->vbasedev.name,
+ vdev->sub_device_id);
+ }
+
/* QEMU can change multi-function devices to single function, or reverse */
vdev->emulated_config_bits[PCI_HEADER_TYPE] =
PCI_HEADER_TYPE_MULTI_FUNCTION;
@@ -3621,12 +2548,12 @@ static int vfio_initfn(PCIDevice *pdev)
vfio_pci_size_rom(vdev);
- ret = vfio_early_setup_msix(vdev);
+ ret = vfio_msix_early_setup(vdev);
if (ret) {
return ret;
}
- vfio_map_bars(vdev);
+ vfio_bars_setup(vdev);
ret = vfio_add_capabilities(vdev);
if (ret) {
@@ -3647,8 +2574,8 @@ static int vfio_initfn(PCIDevice *pdev)
if (vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1)) {
vdev->intx.mmap_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
vfio_intx_mmap_enable, vdev);
- pci_device_set_intx_routing_notifier(&vdev->pdev, vfio_update_irq);
- ret = vfio_enable_intx(vdev);
+ pci_device_set_intx_routing_notifier(&vdev->pdev, vfio_intx_update);
+ ret = vfio_intx_enable(vdev);
if (ret) {
goto out_teardown;
}
@@ -3656,14 +2583,14 @@ static int vfio_initfn(PCIDevice *pdev)
vfio_register_err_notifier(vdev);
vfio_register_req_notifier(vdev);
- vfio_setup_resetfn(vdev);
+ vfio_setup_resetfn_quirk(vdev);
return 0;
out_teardown:
pci_device_set_intx_routing_notifier(&vdev->pdev, NULL);
vfio_teardown_msi(vdev);
- vfio_unregister_bars(vdev);
+ vfio_bars_exit(vdev);
return ret;
}
@@ -3673,7 +2600,7 @@ static void vfio_instance_finalize(Object *obj)
VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pci_dev);
VFIOGroup *group = vdev->vbasedev.group;
- vfio_unmap_bars(vdev);
+ vfio_bars_finalize(vdev);
g_free(vdev->emulated_config_bits);
g_free(vdev->rom);
vfio_put_device(vdev);
@@ -3692,7 +2619,7 @@ static void vfio_exitfn(PCIDevice *pdev)
timer_free(vdev->intx.mmap_timer);
}
vfio_teardown_msi(vdev);
- vfio_unregister_bars(vdev);
+ vfio_bars_exit(vdev);
}
static void vfio_pci_reset(DeviceState *dev)
@@ -3743,13 +2670,23 @@ static void vfio_instance_init(Object *obj)
static Property vfio_pci_dev_properties[] = {
DEFINE_PROP_PCI_HOST_DEVADDR("host", VFIOPCIDevice, host),
+ DEFINE_PROP_STRING("sysfsdev", VFIOPCIDevice, vbasedev.sysfsdev),
DEFINE_PROP_UINT32("x-intx-mmap-timeout-ms", VFIOPCIDevice,
intx.mmap_timeout, 1100),
DEFINE_PROP_BIT("x-vga", VFIOPCIDevice, features,
VFIO_FEATURE_ENABLE_VGA_BIT, false),
DEFINE_PROP_BIT("x-req", VFIOPCIDevice, features,
VFIO_FEATURE_ENABLE_REQ_BIT, true),
- DEFINE_PROP_BOOL("x-mmap", VFIOPCIDevice, vbasedev.allow_mmap, true),
+ DEFINE_PROP_BOOL("x-no-mmap", VFIOPCIDevice, vbasedev.no_mmap, false),
+ DEFINE_PROP_BOOL("x-no-kvm-intx", VFIOPCIDevice, no_kvm_intx, false),
+ DEFINE_PROP_BOOL("x-no-kvm-msi", VFIOPCIDevice, no_kvm_msi, false),
+ DEFINE_PROP_BOOL("x-no-kvm-msix", VFIOPCIDevice, no_kvm_msix, false),
+ DEFINE_PROP_UINT32("x-pci-vendor-id", VFIOPCIDevice, vendor_id, PCI_ANY_ID),
+ DEFINE_PROP_UINT32("x-pci-device-id", VFIOPCIDevice, device_id, PCI_ANY_ID),
+ DEFINE_PROP_UINT32("x-pci-sub-vendor-id", VFIOPCIDevice,
+ sub_vendor_id, PCI_ANY_ID),
+ DEFINE_PROP_UINT32("x-pci-sub-device-id", VFIOPCIDevice,
+ sub_device_id, PCI_ANY_ID),
/*
* TODO - support passed fds... is this necessary?
* DEFINE_PROP_STRING("vfiofd", VFIOPCIDevice, vfiofd_name),
diff --git a/qemu/hw/vfio/pci.h b/qemu/hw/vfio/pci.h
new file mode 100644
index 000000000..3976f6854
--- /dev/null
+++ b/qemu/hw/vfio/pci.h
@@ -0,0 +1,162 @@
+/*
+ * vfio based device assignment support - PCI devices
+ *
+ * Copyright Red Hat, Inc. 2012-2015
+ *
+ * Authors:
+ * Alex Williamson <alex.williamson@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+#ifndef HW_VFIO_VFIO_PCI_H
+#define HW_VFIO_VFIO_PCI_H
+
+#include "qemu-common.h"
+#include "exec/memory.h"
+#include "hw/pci/pci.h"
+#include "hw/vfio/vfio-common.h"
+#include "qemu/event_notifier.h"
+#include "qemu/queue.h"
+#include "qemu/timer.h"
+
+#define PCI_ANY_ID (~0)
+
+struct VFIOPCIDevice;
+
+typedef struct VFIOQuirk {
+ QLIST_ENTRY(VFIOQuirk) next;
+ void *data;
+ int nr_mem;
+ MemoryRegion *mem;
+} VFIOQuirk;
+
+typedef struct VFIOBAR {
+ VFIORegion region;
+ bool ioport;
+ bool mem64;
+ QLIST_HEAD(, VFIOQuirk) quirks;
+} VFIOBAR;
+
+typedef struct VFIOVGARegion {
+ MemoryRegion mem;
+ off_t offset;
+ int nr;
+ QLIST_HEAD(, VFIOQuirk) quirks;
+} VFIOVGARegion;
+
+typedef struct VFIOVGA {
+ off_t fd_offset;
+ int fd;
+ VFIOVGARegion region[QEMU_PCI_VGA_NUM_REGIONS];
+} VFIOVGA;
+
+typedef struct VFIOINTx {
+ bool pending; /* interrupt pending */
+ bool kvm_accel; /* set when QEMU bypass through KVM enabled */
+ uint8_t pin; /* which pin to pull for qemu_set_irq */
+ EventNotifier interrupt; /* eventfd triggered on interrupt */
+ EventNotifier unmask; /* eventfd for unmask on QEMU bypass */
+ PCIINTxRoute route; /* routing info for QEMU bypass */
+ uint32_t mmap_timeout; /* delay to re-enable mmaps after interrupt */
+ QEMUTimer *mmap_timer; /* enable mmaps after periods w/o interrupts */
+} VFIOINTx;
+
+typedef struct VFIOMSIVector {
+ /*
+ * Two interrupt paths are configured per vector. The first, is only used
+ * for interrupts injected via QEMU. This is typically the non-accel path,
+ * but may also be used when we want QEMU to handle masking and pending
+ * bits. The KVM path bypasses QEMU and is therefore higher performance,
+ * but requires masking at the device. virq is used to track the MSI route
+ * through KVM, thus kvm_interrupt is only available when virq is set to a
+ * valid (>= 0) value.
+ */
+ EventNotifier interrupt;
+ EventNotifier kvm_interrupt;
+ struct VFIOPCIDevice *vdev; /* back pointer to device */
+ int virq;
+ bool use;
+} VFIOMSIVector;
+
+enum {
+ VFIO_INT_NONE = 0,
+ VFIO_INT_INTx = 1,
+ VFIO_INT_MSI = 2,
+ VFIO_INT_MSIX = 3,
+};
+
+/* Cache of MSI-X setup plus extra mmap and memory region for split BAR map */
+typedef struct VFIOMSIXInfo {
+ uint8_t table_bar;
+ uint8_t pba_bar;
+ uint16_t entries;
+ uint32_t table_offset;
+ uint32_t pba_offset;
+ MemoryRegion mmap_mem;
+ void *mmap;
+ unsigned long *pending;
+} VFIOMSIXInfo;
+
+typedef struct VFIOPCIDevice {
+ PCIDevice pdev;
+ VFIODevice vbasedev;
+ VFIOINTx intx;
+ unsigned int config_size;
+ uint8_t *emulated_config_bits; /* QEMU emulated bits, little-endian */
+ off_t config_offset; /* Offset of config space region within device fd */
+ unsigned int rom_size;
+ off_t rom_offset; /* Offset of ROM region within device fd */
+ void *rom;
+ int msi_cap_size;
+ VFIOMSIVector *msi_vectors;
+ VFIOMSIXInfo *msix;
+ int nr_vectors; /* Number of MSI/MSIX vectors currently in use */
+ int interrupt; /* Current interrupt type */
+ VFIOBAR bars[PCI_NUM_REGIONS - 1]; /* No ROM */
+ VFIOVGA *vga; /* 0xa0000, 0x3b0, 0x3c0 */
+ PCIHostDeviceAddress host;
+ EventNotifier err_notifier;
+ EventNotifier req_notifier;
+ int (*resetfn)(struct VFIOPCIDevice *);
+ uint32_t vendor_id;
+ uint32_t device_id;
+ uint32_t sub_vendor_id;
+ uint32_t sub_device_id;
+ uint32_t features;
+#define VFIO_FEATURE_ENABLE_VGA_BIT 0
+#define VFIO_FEATURE_ENABLE_VGA (1 << VFIO_FEATURE_ENABLE_VGA_BIT)
+#define VFIO_FEATURE_ENABLE_REQ_BIT 1
+#define VFIO_FEATURE_ENABLE_REQ (1 << VFIO_FEATURE_ENABLE_REQ_BIT)
+ int32_t bootindex;
+ uint8_t pm_cap;
+ bool has_vga;
+ bool pci_aer;
+ bool req_enabled;
+ bool has_flr;
+ bool has_pm_reset;
+ bool rom_read_failed;
+ bool no_kvm_intx;
+ bool no_kvm_msi;
+ bool no_kvm_msix;
+} VFIOPCIDevice;
+
+uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len);
+void vfio_pci_write_config(PCIDevice *pdev,
+ uint32_t addr, uint32_t val, int len);
+
+uint64_t vfio_vga_read(void *opaque, hwaddr addr, unsigned size);
+void vfio_vga_write(void *opaque, hwaddr addr, uint64_t data, unsigned size);
+
+bool vfio_blacklist_opt_rom(VFIOPCIDevice *vdev);
+void vfio_vga_quirk_setup(VFIOPCIDevice *vdev);
+void vfio_vga_quirk_exit(VFIOPCIDevice *vdev);
+void vfio_vga_quirk_finalize(VFIOPCIDevice *vdev);
+void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr);
+void vfio_bar_quirk_exit(VFIOPCIDevice *vdev, int nr);
+void vfio_bar_quirk_finalize(VFIOPCIDevice *vdev, int nr);
+void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev);
+
+int vfio_populate_vga(VFIOPCIDevice *vdev);
+
+#endif /* HW_VFIO_VFIO_PCI_H */
diff --git a/qemu/hw/vfio/platform.c b/qemu/hw/vfio/platform.c
index 60365d127..1798a00a3 100644
--- a/qemu/hw/vfio/platform.c
+++ b/qemu/hw/vfio/platform.c
@@ -14,6 +14,8 @@
* Copyright Red Hat, Inc. 2012
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include <sys/ioctl.h>
#include <linux/vfio.h>
@@ -32,6 +34,11 @@
* Functions used whatever the injection method
*/
+static inline bool vfio_irq_is_automasked(VFIOINTp *intp)
+{
+ return intp->flags & VFIO_IRQ_INFO_AUTOMASKED;
+}
+
/**
* vfio_init_intp - allocate, initialize the IRQ struct pointer
* and add it into the list of IRQs
@@ -57,18 +64,25 @@ static VFIOINTp *vfio_init_intp(VFIODevice *vbasedev,
sysbus_init_irq(sbdev, &intp->qemuirq);
/* Get an eventfd for trigger */
- ret = event_notifier_init(&intp->interrupt, 0);
+ intp->interrupt = g_malloc0(sizeof(EventNotifier));
+ ret = event_notifier_init(intp->interrupt, 0);
if (ret) {
+ g_free(intp->interrupt);
g_free(intp);
error_report("vfio: Error: trigger event_notifier_init failed ");
return NULL;
}
- /* Get an eventfd for resample/unmask */
- ret = event_notifier_init(&intp->unmask, 0);
- if (ret) {
- g_free(intp);
- error_report("vfio: Error: resamplefd event_notifier_init failed");
- return NULL;
+ if (vfio_irq_is_automasked(intp)) {
+ /* Get an eventfd for resample/unmask */
+ intp->unmask = g_malloc0(sizeof(EventNotifier));
+ ret = event_notifier_init(intp->unmask, 0);
+ if (ret) {
+ g_free(intp->interrupt);
+ g_free(intp->unmask);
+ g_free(intp);
+ error_report("vfio: Error: resamplefd event_notifier_init failed");
+ return NULL;
+ }
}
QLIST_INSERT_HEAD(&vdev->intp_list, intp, next);
@@ -100,7 +114,7 @@ static int vfio_set_trigger_eventfd(VFIOINTp *intp,
irq_set->start = 0;
irq_set->count = 1;
pfd = (int32_t *)&irq_set->data;
- *pfd = event_notifier_get_fd(&intp->interrupt);
+ *pfd = event_notifier_get_fd(intp->interrupt);
qemu_set_fd_handler(*pfd, (IOHandler *)handler, NULL, intp);
ret = ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irq_set);
g_free(irq_set);
@@ -130,12 +144,8 @@ static void vfio_mmap_set_enabled(VFIOPlatformDevice *vdev, bool enabled)
{
int i;
- trace_vfio_platform_mmap_set_enabled(enabled);
-
for (i = 0; i < vdev->vbasedev.num_regions; i++) {
- VFIORegion *region = vdev->regions[i];
-
- memory_region_set_enabled(&region->mmap_mem, enabled);
+ vfio_region_mmaps_set_enabled(vdev->regions[i], enabled);
}
}
@@ -182,7 +192,7 @@ static void vfio_intp_mmap_enable(void *opaque)
static void vfio_intp_inject_pending_lockheld(VFIOINTp *intp)
{
trace_vfio_platform_intp_inject_pending_lockheld(intp->pin,
- event_notifier_get_fd(&intp->interrupt));
+ event_notifier_get_fd(intp->interrupt));
intp->state = VFIO_IRQ_ACTIVE;
@@ -224,18 +234,18 @@ static void vfio_intp_interrupt(VFIOINTp *intp)
trace_vfio_intp_interrupt_set_pending(intp->pin);
QSIMPLEQ_INSERT_TAIL(&vdev->pending_intp_queue,
intp, pqnext);
- ret = event_notifier_test_and_clear(&intp->interrupt);
+ ret = event_notifier_test_and_clear(intp->interrupt);
qemu_mutex_unlock(&vdev->intp_mutex);
return;
}
trace_vfio_platform_intp_interrupt(intp->pin,
- event_notifier_get_fd(&intp->interrupt));
+ event_notifier_get_fd(intp->interrupt));
- ret = event_notifier_test_and_clear(&intp->interrupt);
+ ret = event_notifier_test_and_clear(intp->interrupt);
if (!ret) {
- error_report("Error when clearing fd=%d (ret = %d)\n",
- event_notifier_get_fd(&intp->interrupt), ret);
+ error_report("Error when clearing fd=%d (ret = %d)",
+ event_notifier_get_fd(intp->interrupt), ret);
}
intp->state = VFIO_IRQ_ACTIVE;
@@ -283,13 +293,13 @@ static void vfio_platform_eoi(VFIODevice *vbasedev)
QLIST_FOREACH(intp, &vdev->intp_list, next) {
if (intp->state == VFIO_IRQ_ACTIVE) {
trace_vfio_platform_eoi(intp->pin,
- event_notifier_get_fd(&intp->interrupt));
+ event_notifier_get_fd(intp->interrupt));
intp->state = VFIO_IRQ_INACTIVE;
/* deassert the virtual IRQ */
qemu_set_irq(intp->qemuirq, 0);
- if (intp->flags & VFIO_IRQ_INFO_AUTOMASKED) {
+ if (vfio_irq_is_automasked(intp)) {
/* unmasks the physical level-sensitive IRQ */
vfio_unmask_single_irqindex(vbasedev, intp->pin);
}
@@ -310,18 +320,29 @@ static void vfio_platform_eoi(VFIODevice *vbasedev)
/**
* vfio_start_eventfd_injection - starts the virtual IRQ injection using
* user-side handled eventfds
- * @intp: the IRQ struct pointer
+ * @sbdev: the sysbus device handle
+ * @irq: the qemu irq handle
*/
-static int vfio_start_eventfd_injection(VFIOINTp *intp)
+static void vfio_start_eventfd_injection(SysBusDevice *sbdev, qemu_irq irq)
{
int ret;
+ VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev);
+ VFIOINTp *intp;
+
+ QLIST_FOREACH(intp, &vdev->intp_list, next) {
+ if (intp->qemuirq == irq) {
+ break;
+ }
+ }
+ assert(intp);
ret = vfio_set_trigger_eventfd(intp, vfio_intp_interrupt);
if (ret) {
- error_report("vfio: Error: Failed to pass IRQ fd to the driver: %m");
+ error_report("vfio: failed to start eventfd signaling for IRQ %d: %m",
+ intp->pin);
+ abort();
}
- return ret;
}
/*
@@ -349,7 +370,7 @@ static int vfio_set_resample_eventfd(VFIOINTp *intp)
irq_set->start = 0;
irq_set->count = 1;
pfd = (int32_t *)&irq_set->data;
- *pfd = event_notifier_get_fd(&intp->unmask);
+ *pfd = event_notifier_get_fd(intp->unmask);
qemu_set_fd_handler(*pfd, NULL, NULL, NULL);
ret = ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irq_set);
g_free(irq_set);
@@ -359,6 +380,15 @@ static int vfio_set_resample_eventfd(VFIOINTp *intp)
return ret;
}
+/**
+ * vfio_start_irqfd_injection - starts the virtual IRQ injection using
+ * irqfd
+ *
+ * @sbdev: the sysbus device handle
+ * @irq: the qemu irq handle
+ *
+ * In case the irqfd setup fails, we fallback to userspace handled eventfd
+ */
static void vfio_start_irqfd_injection(SysBusDevice *sbdev, qemu_irq irq)
{
VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev);
@@ -366,7 +396,7 @@ static void vfio_start_irqfd_injection(SysBusDevice *sbdev, qemu_irq irq)
if (!kvm_irqfds_enabled() || !kvm_resamplefds_enabled() ||
!vdev->irqfd_allowed) {
- return;
+ goto fail_irqfd;
}
QLIST_FOREACH(intp, &vdev->intp_list, next) {
@@ -376,39 +406,36 @@ static void vfio_start_irqfd_injection(SysBusDevice *sbdev, qemu_irq irq)
}
assert(intp);
- /* Get to a known interrupt state */
- qemu_set_fd_handler(event_notifier_get_fd(&intp->interrupt),
- NULL, NULL, vdev);
-
- vfio_mask_single_irqindex(&vdev->vbasedev, intp->pin);
- qemu_set_irq(intp->qemuirq, 0);
-
- if (kvm_irqchip_add_irqfd_notifier(kvm_state, &intp->interrupt,
- &intp->unmask, irq) < 0) {
+ if (kvm_irqchip_add_irqfd_notifier(kvm_state, intp->interrupt,
+ intp->unmask, irq) < 0) {
goto fail_irqfd;
}
if (vfio_set_trigger_eventfd(intp, NULL) < 0) {
goto fail_vfio;
}
- if (vfio_set_resample_eventfd(intp) < 0) {
- goto fail_vfio;
+ if (vfio_irq_is_automasked(intp)) {
+ if (vfio_set_resample_eventfd(intp) < 0) {
+ goto fail_vfio;
+ }
+ trace_vfio_platform_start_level_irqfd_injection(intp->pin,
+ event_notifier_get_fd(intp->interrupt),
+ event_notifier_get_fd(intp->unmask));
+ } else {
+ trace_vfio_platform_start_edge_irqfd_injection(intp->pin,
+ event_notifier_get_fd(intp->interrupt));
}
- /* Let's resume injection with irqfd setup */
- vfio_unmask_single_irqindex(&vdev->vbasedev, intp->pin);
-
intp->kvm_accel = true;
- trace_vfio_platform_start_irqfd_injection(intp->pin,
- event_notifier_get_fd(&intp->interrupt),
- event_notifier_get_fd(&intp->unmask));
return;
fail_vfio:
- kvm_irqchip_remove_irqfd_notifier(kvm_state, &intp->interrupt, irq);
+ kvm_irqchip_remove_irqfd_notifier(kvm_state, intp->interrupt, irq);
+ error_report("vfio: failed to start eventfd signaling for IRQ %d: %m",
+ intp->pin);
+ abort();
fail_irqfd:
- vfio_start_eventfd_injection(intp);
- vfio_unmask_single_irqindex(&vdev->vbasedev, intp->pin);
+ vfio_start_eventfd_injection(sbdev, irq);
return;
}
@@ -446,28 +473,16 @@ static int vfio_populate_device(VFIODevice *vbasedev)
vdev->regions = g_new0(VFIORegion *, vbasedev->num_regions);
for (i = 0; i < vbasedev->num_regions; i++) {
- struct vfio_region_info reg_info = { .argsz = sizeof(reg_info) };
- VFIORegion *ptr;
+ char *name = g_strdup_printf("VFIO %s region %d\n", vbasedev->name, i);
- vdev->regions[i] = g_malloc0(sizeof(VFIORegion));
- ptr = vdev->regions[i];
- reg_info.index = i;
- ret = ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, &reg_info);
+ vdev->regions[i] = g_new0(VFIORegion, 1);
+ ret = vfio_region_setup(OBJECT(vdev), vbasedev,
+ vdev->regions[i], i, name);
+ g_free(name);
if (ret) {
error_report("vfio: Error getting region %d info: %m", i);
goto reg_error;
}
- ptr->flags = reg_info.flags;
- ptr->size = reg_info.size;
- ptr->fd_offset = reg_info.offset;
- ptr->nr = i;
- ptr->vbasedev = vbasedev;
-
- trace_vfio_platform_populate_regions(ptr->nr,
- (unsigned long)ptr->flags,
- (unsigned long)ptr->size,
- ptr->vbasedev->fd,
- (unsigned long)ptr->fd_offset);
}
vdev->mmap_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
@@ -504,6 +519,9 @@ irq_err:
}
reg_error:
for (i = 0; i < vbasedev->num_regions; i++) {
+ if (vdev->regions[i]) {
+ vfio_region_finalize(vdev->regions[i]);
+ }
g_free(vdev->regions[i]);
}
g_free(vdev->regions);
@@ -530,38 +548,45 @@ static int vfio_base_device_init(VFIODevice *vbasedev)
{
VFIOGroup *group;
VFIODevice *vbasedev_iter;
- char path[PATH_MAX], iommu_group_path[PATH_MAX], *group_name;
+ char *tmp, group_path[PATH_MAX], *group_name;
ssize_t len;
struct stat st;
int groupid;
int ret;
- /* name must be set prior to the call */
- if (!vbasedev->name || strchr(vbasedev->name, '/')) {
- return -EINVAL;
- }
+ /* @sysfsdev takes precedence over @host */
+ if (vbasedev->sysfsdev) {
+ g_free(vbasedev->name);
+ vbasedev->name = g_strdup(basename(vbasedev->sysfsdev));
+ } else {
+ if (!vbasedev->name || strchr(vbasedev->name, '/')) {
+ return -EINVAL;
+ }
- /* Check that the host device exists */
- g_snprintf(path, sizeof(path), "/sys/bus/platform/devices/%s/",
- vbasedev->name);
+ vbasedev->sysfsdev = g_strdup_printf("/sys/bus/platform/devices/%s",
+ vbasedev->name);
+ }
- if (stat(path, &st) < 0) {
- error_report("vfio: error: no such host device: %s", path);
+ if (stat(vbasedev->sysfsdev, &st) < 0) {
+ error_report("vfio: error: no such host device: %s",
+ vbasedev->sysfsdev);
return -errno;
}
- g_strlcat(path, "iommu_group", sizeof(path));
- len = readlink(path, iommu_group_path, sizeof(iommu_group_path));
- if (len < 0 || len >= sizeof(iommu_group_path)) {
+ tmp = g_strdup_printf("%s/iommu_group", vbasedev->sysfsdev);
+ len = readlink(tmp, group_path, sizeof(group_path));
+ g_free(tmp);
+
+ if (len < 0 || len >= sizeof(group_path)) {
error_report("vfio: error no iommu_group for device");
return len < 0 ? -errno : -ENAMETOOLONG;
}
- iommu_group_path[len] = 0;
- group_name = basename(iommu_group_path);
+ group_path[len] = 0;
+ group_name = basename(group_path);
if (sscanf(group_name, "%d", &groupid) != 1) {
- error_report("vfio: error reading %s: %m", path);
+ error_report("vfio: error reading %s: %m", group_path);
return -errno;
}
@@ -573,25 +598,24 @@ static int vfio_base_device_init(VFIODevice *vbasedev)
return -ENOENT;
}
- g_snprintf(path, sizeof(path), "%s", vbasedev->name);
-
QLIST_FOREACH(vbasedev_iter, &group->device_list, next) {
if (strcmp(vbasedev_iter->name, vbasedev->name) == 0) {
- error_report("vfio: error: device %s is already attached", path);
+ error_report("vfio: error: device %s is already attached",
+ vbasedev->name);
vfio_put_group(group);
return -EBUSY;
}
}
- ret = vfio_get_device(group, path, vbasedev);
+ ret = vfio_get_device(group, vbasedev->name, vbasedev);
if (ret) {
- error_report("vfio: failed to get device %s", path);
+ error_report("vfio: failed to get device %s", vbasedev->name);
vfio_put_group(group);
return ret;
}
ret = vfio_populate_device(vbasedev);
if (ret) {
- error_report("vfio: failed to populate device %s", path);
+ error_report("vfio: failed to populate device %s", vbasedev->name);
vfio_put_group(group);
}
@@ -599,41 +623,6 @@ static int vfio_base_device_init(VFIODevice *vbasedev)
}
/**
- * vfio_map_region - initialize the 2 memory regions for a given
- * MMIO region index
- * @vdev: the VFIO platform device handle
- * @nr: the index of the region
- *
- * Init the top memory region and the mmapped memory region beneath
- * VFIOPlatformDevice is used since VFIODevice is not a QOM Object
- * and could not be passed to memory region functions
-*/
-static void vfio_map_region(VFIOPlatformDevice *vdev, int nr)
-{
- VFIORegion *region = vdev->regions[nr];
- uint64_t size = region->size;
- char name[64];
-
- if (!size) {
- return;
- }
-
- g_snprintf(name, sizeof(name), "VFIO %s region %d",
- vdev->vbasedev.name, nr);
-
- /* A "slow" read/write mapping underlies all regions */
- memory_region_init_io(&region->mem, OBJECT(vdev), &vfio_region_ops,
- region, name, size);
-
- g_strlcat(name, " mmap", sizeof(name));
-
- if (vfio_mmap_region(OBJECT(vdev), region, &region->mem,
- &region->mmap_mem, &region->mmap, size, 0, name)) {
- error_report("%s unsupported. Performance may be slow", name);
- }
-}
-
-/**
* vfio_platform_realize - the device realize function
* @dev: device state pointer
* @errp: error
@@ -646,13 +635,14 @@ static void vfio_platform_realize(DeviceState *dev, Error **errp)
VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(dev);
SysBusDevice *sbdev = SYS_BUS_DEVICE(dev);
VFIODevice *vbasedev = &vdev->vbasedev;
- VFIOINTp *intp;
int i, ret;
vbasedev->type = VFIO_DEVICE_TYPE_PLATFORM;
vbasedev->ops = &vfio_platform_ops;
- trace_vfio_platform_realize(vbasedev->name, vdev->compat);
+ trace_vfio_platform_realize(vbasedev->sysfsdev ?
+ vbasedev->sysfsdev : vbasedev->name,
+ vdev->compat);
ret = vfio_base_device_init(vbasedev);
if (ret) {
@@ -662,12 +652,11 @@ static void vfio_platform_realize(DeviceState *dev, Error **errp)
}
for (i = 0; i < vbasedev->num_regions; i++) {
- vfio_map_region(vdev, i);
- sysbus_init_mmio(sbdev, &vdev->regions[i]->mem);
- }
-
- QLIST_FOREACH(intp, &vdev->intp_list, next) {
- vfio_start_eventfd_injection(intp);
+ if (vfio_region_mmap(vdev->regions[i])) {
+ error_report("%s mmap unsupported. Performance may be slow",
+ memory_region_name(vdev->regions[i]->mem));
+ }
+ sysbus_init_mmio(sbdev, vdev->regions[i]->mem);
}
}
@@ -678,7 +667,8 @@ static const VMStateDescription vfio_platform_vmstate = {
static Property vfio_platform_dev_properties[] = {
DEFINE_PROP_STRING("host", VFIOPlatformDevice, vbasedev.name),
- DEFINE_PROP_BOOL("x-mmap", VFIOPlatformDevice, vbasedev.allow_mmap, true),
+ DEFINE_PROP_STRING("sysfsdev", VFIOPlatformDevice, vbasedev.sysfsdev),
+ DEFINE_PROP_BOOL("x-no-mmap", VFIOPlatformDevice, vbasedev.no_mmap, false),
DEFINE_PROP_UINT32("mmap-timeout-ms", VFIOPlatformDevice,
mmap_timeout, 1100),
DEFINE_PROP_BOOL("x-irqfd", VFIOPlatformDevice, irqfd_allowed, true),
diff --git a/qemu/hw/virtio/Makefile.objs b/qemu/hw/virtio/Makefile.objs
index 19b224a44..3e2b175da 100644
--- a/qemu/hw/virtio/Makefile.objs
+++ b/qemu/hw/virtio/Makefile.objs
@@ -2,7 +2,6 @@ common-obj-y += virtio-rng.o
common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
common-obj-y += virtio-bus.o
common-obj-y += virtio-mmio.o
-obj-$(CONFIG_VIRTIO) += dataplane/
obj-y += virtio.o virtio-balloon.o
obj-$(CONFIG_LINUX) += vhost.o vhost-backend.o vhost-user.o
diff --git a/qemu/hw/virtio/dataplane/Makefile.objs b/qemu/hw/virtio/dataplane/Makefile.objs
deleted file mode 100644
index 753a9cab4..000000000
--- a/qemu/hw/virtio/dataplane/Makefile.objs
+++ /dev/null
@@ -1 +0,0 @@
-obj-y += vring.o
diff --git a/qemu/hw/virtio/dataplane/vring.c b/qemu/hw/virtio/dataplane/vring.c
deleted file mode 100644
index 07fd69c69..000000000
--- a/qemu/hw/virtio/dataplane/vring.c
+++ /dev/null
@@ -1,453 +0,0 @@
-/* Copyright 2012 Red Hat, Inc.
- * Copyright IBM, Corp. 2012
- *
- * Based on Linux 2.6.39 vhost code:
- * Copyright (C) 2009 Red Hat, Inc.
- * Copyright (C) 2006 Rusty Russell IBM Corporation
- *
- * Author: Michael S. Tsirkin <mst@redhat.com>
- * Stefan Hajnoczi <stefanha@redhat.com>
- *
- * Inspiration, some code, and most witty comments come from
- * Documentation/virtual/lguest/lguest.c, by Rusty Russell
- *
- * This work is licensed under the terms of the GNU GPL, version 2.
- */
-
-#include "trace.h"
-#include "hw/hw.h"
-#include "exec/memory.h"
-#include "exec/address-spaces.h"
-#include "hw/virtio/virtio-access.h"
-#include "hw/virtio/dataplane/vring.h"
-#include "hw/virtio/dataplane/vring-accessors.h"
-#include "qemu/error-report.h"
-
-/* vring_map can be coupled with vring_unmap or (if you still have the
- * value returned in *mr) memory_region_unref.
- */
-static void *vring_map(MemoryRegion **mr, hwaddr phys, hwaddr len,
- bool is_write)
-{
- MemoryRegionSection section = memory_region_find(get_system_memory(), phys, len);
-
- if (!section.mr || int128_get64(section.size) < len) {
- goto out;
- }
- if (is_write && section.readonly) {
- goto out;
- }
- if (!memory_region_is_ram(section.mr)) {
- goto out;
- }
-
- /* Ignore regions with dirty logging, we cannot mark them dirty */
- if (memory_region_get_dirty_log_mask(section.mr)) {
- goto out;
- }
-
- *mr = section.mr;
- return memory_region_get_ram_ptr(section.mr) + section.offset_within_region;
-
-out:
- memory_region_unref(section.mr);
- *mr = NULL;
- return NULL;
-}
-
-static void vring_unmap(void *buffer, bool is_write)
-{
- ram_addr_t addr;
- MemoryRegion *mr;
-
- mr = qemu_ram_addr_from_host(buffer, &addr);
- memory_region_unref(mr);
-}
-
-/* Map the guest's vring to host memory */
-bool vring_setup(Vring *vring, VirtIODevice *vdev, int n)
-{
- hwaddr vring_addr = virtio_queue_get_ring_addr(vdev, n);
- hwaddr vring_size = virtio_queue_get_ring_size(vdev, n);
- void *vring_ptr;
-
- vring->broken = false;
-
- vring_ptr = vring_map(&vring->mr, vring_addr, vring_size, true);
- if (!vring_ptr) {
- error_report("Failed to map vring "
- "addr %#" HWADDR_PRIx " size %" HWADDR_PRIu,
- vring_addr, vring_size);
- vring->broken = true;
- return false;
- }
-
- vring_init(&vring->vr, virtio_queue_get_num(vdev, n), vring_ptr, 4096);
-
- vring->last_avail_idx = virtio_queue_get_last_avail_idx(vdev, n);
- vring->last_used_idx = vring_get_used_idx(vdev, vring);
- vring->signalled_used = 0;
- vring->signalled_used_valid = false;
-
- trace_vring_setup(virtio_queue_get_ring_addr(vdev, n),
- vring->vr.desc, vring->vr.avail, vring->vr.used);
- return true;
-}
-
-void vring_teardown(Vring *vring, VirtIODevice *vdev, int n)
-{
- virtio_queue_set_last_avail_idx(vdev, n, vring->last_avail_idx);
- virtio_queue_invalidate_signalled_used(vdev, n);
-
- memory_region_unref(vring->mr);
-}
-
-/* Disable guest->host notifies */
-void vring_disable_notification(VirtIODevice *vdev, Vring *vring)
-{
- if (!virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
- vring_set_used_flags(vdev, vring, VRING_USED_F_NO_NOTIFY);
- }
-}
-
-/* Enable guest->host notifies
- *
- * Return true if the vring is empty, false if there are more requests.
- */
-bool vring_enable_notification(VirtIODevice *vdev, Vring *vring)
-{
- if (virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
- vring_avail_event(&vring->vr) = vring->vr.avail->idx;
- } else {
- vring_clear_used_flags(vdev, vring, VRING_USED_F_NO_NOTIFY);
- }
- smp_mb(); /* ensure update is seen before reading avail_idx */
- return !vring_more_avail(vdev, vring);
-}
-
-/* This is stolen from linux/drivers/vhost/vhost.c:vhost_notify() */
-bool vring_should_notify(VirtIODevice *vdev, Vring *vring)
-{
- uint16_t old, new;
- bool v;
- /* Flush out used index updates. This is paired
- * with the barrier that the Guest executes when enabling
- * interrupts. */
- smp_mb();
-
- if (virtio_has_feature(vdev, VIRTIO_F_NOTIFY_ON_EMPTY) &&
- unlikely(!vring_more_avail(vdev, vring))) {
- return true;
- }
-
- if (!virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
- return !(vring_get_avail_flags(vdev, vring) &
- VRING_AVAIL_F_NO_INTERRUPT);
- }
- old = vring->signalled_used;
- v = vring->signalled_used_valid;
- new = vring->signalled_used = vring->last_used_idx;
- vring->signalled_used_valid = true;
-
- if (unlikely(!v)) {
- return true;
- }
-
- return vring_need_event(virtio_tswap16(vdev, vring_used_event(&vring->vr)),
- new, old);
-}
-
-
-static int get_desc(Vring *vring, VirtQueueElement *elem,
- struct vring_desc *desc)
-{
- unsigned *num;
- struct iovec *iov;
- hwaddr *addr;
- MemoryRegion *mr;
-
- if (desc->flags & VRING_DESC_F_WRITE) {
- num = &elem->in_num;
- iov = &elem->in_sg[*num];
- addr = &elem->in_addr[*num];
- } else {
- num = &elem->out_num;
- iov = &elem->out_sg[*num];
- addr = &elem->out_addr[*num];
-
- /* If it's an output descriptor, they're all supposed
- * to come before any input descriptors. */
- if (unlikely(elem->in_num)) {
- error_report("Descriptor has out after in");
- return -EFAULT;
- }
- }
-
- /* Stop for now if there are not enough iovecs available. */
- if (*num >= VIRTQUEUE_MAX_SIZE) {
- error_report("Invalid SG num: %u", *num);
- return -EFAULT;
- }
-
- /* TODO handle non-contiguous memory across region boundaries */
- iov->iov_base = vring_map(&mr, desc->addr, desc->len,
- desc->flags & VRING_DESC_F_WRITE);
- if (!iov->iov_base) {
- error_report("Failed to map descriptor addr %#" PRIx64 " len %u",
- (uint64_t)desc->addr, desc->len);
- return -EFAULT;
- }
-
- /* The MemoryRegion is looked up again and unref'ed later, leave the
- * ref in place. */
- iov->iov_len = desc->len;
- *addr = desc->addr;
- *num += 1;
- return 0;
-}
-
-static void copy_in_vring_desc(VirtIODevice *vdev,
- const struct vring_desc *guest,
- struct vring_desc *host)
-{
- host->addr = virtio_ldq_p(vdev, &guest->addr);
- host->len = virtio_ldl_p(vdev, &guest->len);
- host->flags = virtio_lduw_p(vdev, &guest->flags);
- host->next = virtio_lduw_p(vdev, &guest->next);
-}
-
-/* This is stolen from linux/drivers/vhost/vhost.c. */
-static int get_indirect(VirtIODevice *vdev, Vring *vring,
- VirtQueueElement *elem, struct vring_desc *indirect)
-{
- struct vring_desc desc;
- unsigned int i = 0, count, found = 0;
- int ret;
-
- /* Sanity check */
- if (unlikely(indirect->len % sizeof(desc))) {
- error_report("Invalid length in indirect descriptor: "
- "len %#x not multiple of %#zx",
- indirect->len, sizeof(desc));
- vring->broken = true;
- return -EFAULT;
- }
-
- count = indirect->len / sizeof(desc);
- /* Buffers are chained via a 16 bit next field, so
- * we can have at most 2^16 of these. */
- if (unlikely(count > USHRT_MAX + 1)) {
- error_report("Indirect buffer length too big: %d", indirect->len);
- vring->broken = true;
- return -EFAULT;
- }
-
- do {
- struct vring_desc *desc_ptr;
- MemoryRegion *mr;
-
- /* Translate indirect descriptor */
- desc_ptr = vring_map(&mr,
- indirect->addr + found * sizeof(desc),
- sizeof(desc), false);
- if (!desc_ptr) {
- error_report("Failed to map indirect descriptor "
- "addr %#" PRIx64 " len %zu",
- (uint64_t)indirect->addr + found * sizeof(desc),
- sizeof(desc));
- vring->broken = true;
- return -EFAULT;
- }
- copy_in_vring_desc(vdev, desc_ptr, &desc);
- memory_region_unref(mr);
-
- /* Ensure descriptor has been loaded before accessing fields */
- barrier(); /* read_barrier_depends(); */
-
- if (unlikely(++found > count)) {
- error_report("Loop detected: last one at %u "
- "indirect size %u", i, count);
- vring->broken = true;
- return -EFAULT;
- }
-
- if (unlikely(desc.flags & VRING_DESC_F_INDIRECT)) {
- error_report("Nested indirect descriptor");
- vring->broken = true;
- return -EFAULT;
- }
-
- ret = get_desc(vring, elem, &desc);
- if (ret < 0) {
- vring->broken |= (ret == -EFAULT);
- return ret;
- }
- i = desc.next;
- } while (desc.flags & VRING_DESC_F_NEXT);
- return 0;
-}
-
-static void vring_unmap_element(VirtQueueElement *elem)
-{
- int i;
-
- /* This assumes that the iovecs, if changed, are never moved past
- * the end of the valid area. This is true if iovec manipulations
- * are done with iov_discard_front and iov_discard_back.
- */
- for (i = 0; i < elem->out_num; i++) {
- vring_unmap(elem->out_sg[i].iov_base, false);
- }
-
- for (i = 0; i < elem->in_num; i++) {
- vring_unmap(elem->in_sg[i].iov_base, true);
- }
-}
-
-/* This looks in the virtqueue and for the first available buffer, and converts
- * it to an iovec for convenient access. Since descriptors consist of some
- * number of output then some number of input descriptors, it's actually two
- * iovecs, but we pack them into one and note how many of each there were.
- *
- * This function returns the descriptor number found, or vq->num (which is
- * never a valid descriptor number) if none was found. A negative code is
- * returned on error.
- *
- * Stolen from linux/drivers/vhost/vhost.c.
- */
-int vring_pop(VirtIODevice *vdev, Vring *vring,
- VirtQueueElement *elem)
-{
- struct vring_desc desc;
- unsigned int i, head, found = 0, num = vring->vr.num;
- uint16_t avail_idx, last_avail_idx;
- int ret;
-
- /* Initialize elem so it can be safely unmapped */
- elem->in_num = elem->out_num = 0;
-
- /* If there was a fatal error then refuse operation */
- if (vring->broken) {
- ret = -EFAULT;
- goto out;
- }
-
- /* Check it isn't doing very strange things with descriptor numbers. */
- last_avail_idx = vring->last_avail_idx;
- avail_idx = vring_get_avail_idx(vdev, vring);
- barrier(); /* load indices now and not again later */
-
- if (unlikely((uint16_t)(avail_idx - last_avail_idx) > num)) {
- error_report("Guest moved used index from %u to %u",
- last_avail_idx, avail_idx);
- ret = -EFAULT;
- goto out;
- }
-
- /* If there's nothing new since last we looked. */
- if (avail_idx == last_avail_idx) {
- ret = -EAGAIN;
- goto out;
- }
-
- /* Only get avail ring entries after they have been exposed by guest. */
- smp_rmb();
-
- /* Grab the next descriptor number they're advertising, and increment
- * the index we've seen. */
- head = vring_get_avail_ring(vdev, vring, last_avail_idx % num);
-
- elem->index = head;
-
- /* If their number is silly, that's an error. */
- if (unlikely(head >= num)) {
- error_report("Guest says index %u > %u is available", head, num);
- ret = -EFAULT;
- goto out;
- }
-
- i = head;
- do {
- if (unlikely(i >= num)) {
- error_report("Desc index is %u > %u, head = %u", i, num, head);
- ret = -EFAULT;
- goto out;
- }
- if (unlikely(++found > num)) {
- error_report("Loop detected: last one at %u vq size %u head %u",
- i, num, head);
- ret = -EFAULT;
- goto out;
- }
- copy_in_vring_desc(vdev, &vring->vr.desc[i], &desc);
-
- /* Ensure descriptor is loaded before accessing fields */
- barrier();
-
- if (desc.flags & VRING_DESC_F_INDIRECT) {
- ret = get_indirect(vdev, vring, elem, &desc);
- if (ret < 0) {
- goto out;
- }
- continue;
- }
-
- ret = get_desc(vring, elem, &desc);
- if (ret < 0) {
- goto out;
- }
-
- i = desc.next;
- } while (desc.flags & VRING_DESC_F_NEXT);
-
- /* On success, increment avail index. */
- vring->last_avail_idx++;
- if (virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
- vring_avail_event(&vring->vr) =
- virtio_tswap16(vdev, vring->last_avail_idx);
- }
-
- return head;
-
-out:
- assert(ret < 0);
- if (ret == -EFAULT) {
- vring->broken = true;
- }
- vring_unmap_element(elem);
- return ret;
-}
-
-/* After we've used one of their buffers, we tell them about it.
- *
- * Stolen from linux/drivers/vhost/vhost.c.
- */
-void vring_push(VirtIODevice *vdev, Vring *vring, VirtQueueElement *elem,
- int len)
-{
- unsigned int head = elem->index;
- uint16_t new;
-
- vring_unmap_element(elem);
-
- /* Don't touch vring if a fatal error occurred */
- if (vring->broken) {
- return;
- }
-
- /* The virtqueue contains a ring of used buffers. Get a pointer to the
- * next entry in that used ring. */
- vring_set_used_ring_id(vdev, vring, vring->last_used_idx % vring->vr.num,
- head);
- vring_set_used_ring_len(vdev, vring, vring->last_used_idx % vring->vr.num,
- len);
-
- /* Make sure buffer is written before we update index. */
- smp_wmb();
-
- new = ++vring->last_used_idx;
- vring_set_used_idx(vdev, vring, new);
- if (unlikely((int16_t)(new - vring->signalled_used) < (uint16_t)1)) {
- vring->signalled_used_valid = false;
- }
-}
diff --git a/qemu/hw/virtio/vhost-backend.c b/qemu/hw/virtio/vhost-backend.c
index 4d68a2765..b35890289 100644
--- a/qemu/hw/virtio/vhost-backend.c
+++ b/qemu/hw/virtio/vhost-backend.c
@@ -8,9 +8,11 @@
*
*/
+#include "qemu/osdep.h"
#include "hw/virtio/vhost.h"
#include "hw/virtio/vhost-backend.h"
#include "qemu/error-report.h"
+#include "linux/vhost.h"
#include <sys/ioctl.h>
@@ -42,11 +44,152 @@ static int vhost_kernel_cleanup(struct vhost_dev *dev)
return close(fd);
}
+static int vhost_kernel_memslots_limit(struct vhost_dev *dev)
+{
+ int limit = 64;
+ char *s;
+
+ if (g_file_get_contents("/sys/module/vhost/parameters/max_mem_regions",
+ &s, NULL, NULL)) {
+ uint64_t val = g_ascii_strtoull(s, NULL, 10);
+ if (!((val == G_MAXUINT64 || !val) && errno)) {
+ return val;
+ }
+ error_report("ignoring invalid max_mem_regions value in vhost module:"
+ " %s", s);
+ }
+ return limit;
+}
+
+static int vhost_kernel_net_set_backend(struct vhost_dev *dev,
+ struct vhost_vring_file *file)
+{
+ return vhost_kernel_call(dev, VHOST_NET_SET_BACKEND, file);
+}
+
+static int vhost_kernel_scsi_set_endpoint(struct vhost_dev *dev,
+ struct vhost_scsi_target *target)
+{
+ return vhost_kernel_call(dev, VHOST_SCSI_SET_ENDPOINT, target);
+}
+
+static int vhost_kernel_scsi_clear_endpoint(struct vhost_dev *dev,
+ struct vhost_scsi_target *target)
+{
+ return vhost_kernel_call(dev, VHOST_SCSI_CLEAR_ENDPOINT, target);
+}
+
+static int vhost_kernel_scsi_get_abi_version(struct vhost_dev *dev, int *version)
+{
+ return vhost_kernel_call(dev, VHOST_SCSI_GET_ABI_VERSION, version);
+}
+
+static int vhost_kernel_set_log_base(struct vhost_dev *dev, uint64_t base,
+ struct vhost_log *log)
+{
+ return vhost_kernel_call(dev, VHOST_SET_LOG_BASE, &base);
+}
+
+static int vhost_kernel_set_mem_table(struct vhost_dev *dev,
+ struct vhost_memory *mem)
+{
+ return vhost_kernel_call(dev, VHOST_SET_MEM_TABLE, mem);
+}
+
+static int vhost_kernel_set_vring_addr(struct vhost_dev *dev,
+ struct vhost_vring_addr *addr)
+{
+ return vhost_kernel_call(dev, VHOST_SET_VRING_ADDR, addr);
+}
+
+static int vhost_kernel_set_vring_endian(struct vhost_dev *dev,
+ struct vhost_vring_state *ring)
+{
+ return vhost_kernel_call(dev, VHOST_SET_VRING_ENDIAN, ring);
+}
+
+static int vhost_kernel_set_vring_num(struct vhost_dev *dev,
+ struct vhost_vring_state *ring)
+{
+ return vhost_kernel_call(dev, VHOST_SET_VRING_NUM, ring);
+}
+
+static int vhost_kernel_set_vring_base(struct vhost_dev *dev,
+ struct vhost_vring_state *ring)
+{
+ return vhost_kernel_call(dev, VHOST_SET_VRING_BASE, ring);
+}
+
+static int vhost_kernel_get_vring_base(struct vhost_dev *dev,
+ struct vhost_vring_state *ring)
+{
+ return vhost_kernel_call(dev, VHOST_GET_VRING_BASE, ring);
+}
+
+static int vhost_kernel_set_vring_kick(struct vhost_dev *dev,
+ struct vhost_vring_file *file)
+{
+ return vhost_kernel_call(dev, VHOST_SET_VRING_KICK, file);
+}
+
+static int vhost_kernel_set_vring_call(struct vhost_dev *dev,
+ struct vhost_vring_file *file)
+{
+ return vhost_kernel_call(dev, VHOST_SET_VRING_CALL, file);
+}
+
+static int vhost_kernel_set_features(struct vhost_dev *dev,
+ uint64_t features)
+{
+ return vhost_kernel_call(dev, VHOST_SET_FEATURES, &features);
+}
+
+static int vhost_kernel_get_features(struct vhost_dev *dev,
+ uint64_t *features)
+{
+ return vhost_kernel_call(dev, VHOST_GET_FEATURES, features);
+}
+
+static int vhost_kernel_set_owner(struct vhost_dev *dev)
+{
+ return vhost_kernel_call(dev, VHOST_SET_OWNER, NULL);
+}
+
+static int vhost_kernel_reset_device(struct vhost_dev *dev)
+{
+ return vhost_kernel_call(dev, VHOST_RESET_OWNER, NULL);
+}
+
+static int vhost_kernel_get_vq_index(struct vhost_dev *dev, int idx)
+{
+ assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs);
+
+ return idx - dev->vq_index;
+}
+
static const VhostOps kernel_ops = {
.backend_type = VHOST_BACKEND_TYPE_KERNEL,
- .vhost_call = vhost_kernel_call,
.vhost_backend_init = vhost_kernel_init,
- .vhost_backend_cleanup = vhost_kernel_cleanup
+ .vhost_backend_cleanup = vhost_kernel_cleanup,
+ .vhost_backend_memslots_limit = vhost_kernel_memslots_limit,
+ .vhost_net_set_backend = vhost_kernel_net_set_backend,
+ .vhost_scsi_set_endpoint = vhost_kernel_scsi_set_endpoint,
+ .vhost_scsi_clear_endpoint = vhost_kernel_scsi_clear_endpoint,
+ .vhost_scsi_get_abi_version = vhost_kernel_scsi_get_abi_version,
+ .vhost_set_log_base = vhost_kernel_set_log_base,
+ .vhost_set_mem_table = vhost_kernel_set_mem_table,
+ .vhost_set_vring_addr = vhost_kernel_set_vring_addr,
+ .vhost_set_vring_endian = vhost_kernel_set_vring_endian,
+ .vhost_set_vring_num = vhost_kernel_set_vring_num,
+ .vhost_set_vring_base = vhost_kernel_set_vring_base,
+ .vhost_get_vring_base = vhost_kernel_get_vring_base,
+ .vhost_set_vring_kick = vhost_kernel_set_vring_kick,
+ .vhost_set_vring_call = vhost_kernel_set_vring_call,
+ .vhost_set_features = vhost_kernel_set_features,
+ .vhost_get_features = vhost_kernel_get_features,
+ .vhost_set_owner = vhost_kernel_set_owner,
+ .vhost_reset_device = vhost_kernel_reset_device,
+ .vhost_get_vq_index = vhost_kernel_get_vq_index,
};
int vhost_set_backend_type(struct vhost_dev *dev, VhostBackendType backend_type)
diff --git a/qemu/hw/virtio/vhost-user.c b/qemu/hw/virtio/vhost-user.c
index e7ab8293d..5914e8510 100644
--- a/qemu/hw/virtio/vhost-user.c
+++ b/qemu/hw/virtio/vhost-user.c
@@ -8,22 +8,35 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/virtio/vhost.h"
#include "hw/virtio/vhost-backend.h"
+#include "hw/virtio/virtio-net.h"
#include "sysemu/char.h"
#include "sysemu/kvm.h"
#include "qemu/error-report.h"
#include "qemu/sockets.h"
#include "exec/ram_addr.h"
+#include "migration/migration.h"
-#include <fcntl.h>
-#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <linux/vhost.h>
#define VHOST_MEMORY_MAX_NREGIONS 8
+#define VHOST_USER_F_PROTOCOL_FEATURES 30
+
+enum VhostUserProtocolFeature {
+ VHOST_USER_PROTOCOL_F_MQ = 0,
+ VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1,
+ VHOST_USER_PROTOCOL_F_RARP = 2,
+
+ VHOST_USER_PROTOCOL_F_MAX
+};
+
+#define VHOST_USER_PROTOCOL_FEATURE_MASK ((1 << VHOST_USER_PROTOCOL_F_MAX) - 1)
typedef enum VhostUserRequest {
VHOST_USER_NONE = 0,
@@ -41,6 +54,11 @@ typedef enum VhostUserRequest {
VHOST_USER_SET_VRING_KICK = 12,
VHOST_USER_SET_VRING_CALL = 13,
VHOST_USER_SET_VRING_ERR = 14,
+ VHOST_USER_GET_PROTOCOL_FEATURES = 15,
+ VHOST_USER_SET_PROTOCOL_FEATURES = 16,
+ VHOST_USER_GET_QUEUE_NUM = 17,
+ VHOST_USER_SET_VRING_ENABLE = 18,
+ VHOST_USER_SEND_RARP = 19,
VHOST_USER_MAX
} VhostUserRequest;
@@ -57,6 +75,11 @@ typedef struct VhostUserMemory {
VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS];
} VhostUserMemory;
+typedef struct VhostUserLog {
+ uint64_t mmap_size;
+ uint64_t mmap_offset;
+} VhostUserLog;
+
typedef struct VhostUserMsg {
VhostUserRequest request;
@@ -71,7 +94,8 @@ typedef struct VhostUserMsg {
struct vhost_vring_state state;
struct vhost_vring_addr addr;
VhostUserMemory memory;
- };
+ VhostUserLog log;
+ } payload;
} QEMU_PACKED VhostUserMsg;
static VhostUserMsg m __attribute__ ((unused));
@@ -89,37 +113,6 @@ static bool ioeventfd_enabled(void)
return kvm_enabled() && kvm_eventfds_enabled();
}
-static unsigned long int ioctl_to_vhost_user_request[VHOST_USER_MAX] = {
- -1, /* VHOST_USER_NONE */
- VHOST_GET_FEATURES, /* VHOST_USER_GET_FEATURES */
- VHOST_SET_FEATURES, /* VHOST_USER_SET_FEATURES */
- VHOST_SET_OWNER, /* VHOST_USER_SET_OWNER */
- VHOST_RESET_OWNER, /* VHOST_USER_RESET_OWNER */
- VHOST_SET_MEM_TABLE, /* VHOST_USER_SET_MEM_TABLE */
- VHOST_SET_LOG_BASE, /* VHOST_USER_SET_LOG_BASE */
- VHOST_SET_LOG_FD, /* VHOST_USER_SET_LOG_FD */
- VHOST_SET_VRING_NUM, /* VHOST_USER_SET_VRING_NUM */
- VHOST_SET_VRING_ADDR, /* VHOST_USER_SET_VRING_ADDR */
- VHOST_SET_VRING_BASE, /* VHOST_USER_SET_VRING_BASE */
- VHOST_GET_VRING_BASE, /* VHOST_USER_GET_VRING_BASE */
- VHOST_SET_VRING_KICK, /* VHOST_USER_SET_VRING_KICK */
- VHOST_SET_VRING_CALL, /* VHOST_USER_SET_VRING_CALL */
- VHOST_SET_VRING_ERR /* VHOST_USER_SET_VRING_ERR */
-};
-
-static VhostUserRequest vhost_user_request_translate(unsigned long int request)
-{
- VhostUserRequest idx;
-
- for (idx = 0; idx < VHOST_USER_MAX; idx++) {
- if (ioctl_to_vhost_user_request[idx] == request) {
- break;
- }
- }
-
- return (idx == VHOST_USER_MAX) ? VHOST_USER_NONE : idx;
-}
-
static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg)
{
CharDriverState *chr = dev->opaque;
@@ -128,8 +121,8 @@ static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg)
r = qemu_chr_fe_read_all(chr, p, size);
if (r != size) {
- error_report("Failed to read msg header. Read %d instead of %d.", r,
- size);
+ error_report("Failed to read msg header. Read %d instead of %d."
+ " Original request %d.", r, size, msg->request);
goto fail;
}
@@ -166,12 +159,35 @@ fail:
return -1;
}
+static bool vhost_user_one_time_request(VhostUserRequest request)
+{
+ switch (request) {
+ case VHOST_USER_SET_OWNER:
+ case VHOST_USER_RESET_OWNER:
+ case VHOST_USER_SET_MEM_TABLE:
+ case VHOST_USER_GET_QUEUE_NUM:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* most non-init callers ignore the error */
static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg,
int *fds, int fd_num)
{
CharDriverState *chr = dev->opaque;
int size = VHOST_USER_HDR_SIZE + msg->size;
+ /*
+ * For non-vring specific requests, like VHOST_USER_SET_MEM_TABLE,
+ * we just need send it once in the first time. For later such
+ * request, we just ignore it.
+ */
+ if (vhost_user_one_time_request(msg->request) && dev->vq_index != 0) {
+ return 0;
+ }
+
if (fd_num) {
qemu_chr_fe_set_msgfds(chr, fds, fd_num);
}
@@ -180,157 +196,364 @@ static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg,
0 : -1;
}
-static int vhost_user_call(struct vhost_dev *dev, unsigned long int request,
- void *arg)
+static int vhost_user_set_log_base(struct vhost_dev *dev, uint64_t base,
+ struct vhost_log *log)
{
- VhostUserMsg msg;
- VhostUserRequest msg_request;
- struct vhost_vring_file *file = 0;
- int need_reply = 0;
int fds[VHOST_MEMORY_MAX_NREGIONS];
- int i, fd;
size_t fd_num = 0;
+ bool shmfd = virtio_has_feature(dev->protocol_features,
+ VHOST_USER_PROTOCOL_F_LOG_SHMFD);
+ VhostUserMsg msg = {
+ .request = VHOST_USER_SET_LOG_BASE,
+ .flags = VHOST_USER_VERSION,
+ .payload.log.mmap_size = log->size * sizeof(*(log->log)),
+ .payload.log.mmap_offset = 0,
+ .size = sizeof(msg.payload.log),
+ };
- assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER);
+ if (shmfd && log->fd != -1) {
+ fds[fd_num++] = log->fd;
+ }
- msg_request = vhost_user_request_translate(request);
- msg.request = msg_request;
- msg.flags = VHOST_USER_VERSION;
- msg.size = 0;
+ vhost_user_write(dev, &msg, fds, fd_num);
- switch (request) {
- case VHOST_GET_FEATURES:
- need_reply = 1;
- break;
-
- case VHOST_SET_FEATURES:
- case VHOST_SET_LOG_BASE:
- msg.u64 = *((__u64 *) arg);
- msg.size = sizeof(m.u64);
- break;
-
- case VHOST_SET_OWNER:
- case VHOST_RESET_OWNER:
- break;
-
- case VHOST_SET_MEM_TABLE:
- for (i = 0; i < dev->mem->nregions; ++i) {
- struct vhost_memory_region *reg = dev->mem->regions + i;
- ram_addr_t ram_addr;
-
- assert((uintptr_t)reg->userspace_addr == reg->userspace_addr);
- qemu_ram_addr_from_host((void *)(uintptr_t)reg->userspace_addr, &ram_addr);
- fd = qemu_get_ram_fd(ram_addr);
- if (fd > 0) {
- msg.memory.regions[fd_num].userspace_addr = reg->userspace_addr;
- msg.memory.regions[fd_num].memory_size = reg->memory_size;
- msg.memory.regions[fd_num].guest_phys_addr = reg->guest_phys_addr;
- msg.memory.regions[fd_num].mmap_offset = reg->userspace_addr -
- (uintptr_t) qemu_get_ram_block_host_ptr(ram_addr);
- assert(fd_num < VHOST_MEMORY_MAX_NREGIONS);
- fds[fd_num++] = fd;
- }
+ if (shmfd) {
+ msg.size = 0;
+ if (vhost_user_read(dev, &msg) < 0) {
+ return 0;
}
- msg.memory.nregions = fd_num;
-
- if (!fd_num) {
- error_report("Failed initializing vhost-user memory map, "
- "consider using -object memory-backend-file share=on");
+ if (msg.request != VHOST_USER_SET_LOG_BASE) {
+ error_report("Received unexpected msg type. "
+ "Expected %d received %d",
+ VHOST_USER_SET_LOG_BASE, msg.request);
return -1;
}
+ }
- msg.size = sizeof(m.memory.nregions);
- msg.size += sizeof(m.memory.padding);
- msg.size += fd_num * sizeof(VhostUserMemoryRegion);
-
- break;
-
- case VHOST_SET_LOG_FD:
- fds[fd_num++] = *((int *) arg);
- break;
-
- case VHOST_SET_VRING_NUM:
- case VHOST_SET_VRING_BASE:
- memcpy(&msg.state, arg, sizeof(struct vhost_vring_state));
- msg.size = sizeof(m.state);
- break;
-
- case VHOST_GET_VRING_BASE:
- memcpy(&msg.state, arg, sizeof(struct vhost_vring_state));
- msg.size = sizeof(m.state);
- need_reply = 1;
- break;
-
- case VHOST_SET_VRING_ADDR:
- memcpy(&msg.addr, arg, sizeof(struct vhost_vring_addr));
- msg.size = sizeof(m.addr);
- break;
-
- case VHOST_SET_VRING_KICK:
- case VHOST_SET_VRING_CALL:
- case VHOST_SET_VRING_ERR:
- file = arg;
- msg.u64 = file->index & VHOST_USER_VRING_IDX_MASK;
- msg.size = sizeof(m.u64);
- if (ioeventfd_enabled() && file->fd > 0) {
- fds[fd_num++] = file->fd;
- } else {
- msg.u64 |= VHOST_USER_VRING_NOFD_MASK;
+ return 0;
+}
+
+static int vhost_user_set_mem_table(struct vhost_dev *dev,
+ struct vhost_memory *mem)
+{
+ int fds[VHOST_MEMORY_MAX_NREGIONS];
+ int i, fd;
+ size_t fd_num = 0;
+ VhostUserMsg msg = {
+ .request = VHOST_USER_SET_MEM_TABLE,
+ .flags = VHOST_USER_VERSION,
+ };
+
+ for (i = 0; i < dev->mem->nregions; ++i) {
+ struct vhost_memory_region *reg = dev->mem->regions + i;
+ ram_addr_t ram_addr;
+
+ assert((uintptr_t)reg->userspace_addr == reg->userspace_addr);
+ qemu_ram_addr_from_host((void *)(uintptr_t)reg->userspace_addr,
+ &ram_addr);
+ fd = qemu_get_ram_fd(ram_addr);
+ if (fd > 0) {
+ msg.payload.memory.regions[fd_num].userspace_addr = reg->userspace_addr;
+ msg.payload.memory.regions[fd_num].memory_size = reg->memory_size;
+ msg.payload.memory.regions[fd_num].guest_phys_addr = reg->guest_phys_addr;
+ msg.payload.memory.regions[fd_num].mmap_offset = reg->userspace_addr -
+ (uintptr_t) qemu_get_ram_block_host_ptr(ram_addr);
+ assert(fd_num < VHOST_MEMORY_MAX_NREGIONS);
+ fds[fd_num++] = fd;
}
- break;
- default:
- error_report("vhost-user trying to send unhandled ioctl");
+ }
+
+ msg.payload.memory.nregions = fd_num;
+
+ if (!fd_num) {
+ error_report("Failed initializing vhost-user memory map, "
+ "consider using -object memory-backend-file share=on");
return -1;
- break;
}
- if (vhost_user_write(dev, &msg, fds, fd_num) < 0) {
+ msg.size = sizeof(msg.payload.memory.nregions);
+ msg.size += sizeof(msg.payload.memory.padding);
+ msg.size += fd_num * sizeof(VhostUserMemoryRegion);
+
+ vhost_user_write(dev, &msg, fds, fd_num);
+
+ return 0;
+}
+
+static int vhost_user_set_vring_addr(struct vhost_dev *dev,
+ struct vhost_vring_addr *addr)
+{
+ VhostUserMsg msg = {
+ .request = VHOST_USER_SET_VRING_ADDR,
+ .flags = VHOST_USER_VERSION,
+ .payload.addr = *addr,
+ .size = sizeof(msg.payload.addr),
+ };
+
+ vhost_user_write(dev, &msg, NULL, 0);
+
+ return 0;
+}
+
+static int vhost_user_set_vring_endian(struct vhost_dev *dev,
+ struct vhost_vring_state *ring)
+{
+ error_report("vhost-user trying to send unhandled ioctl");
+ return -1;
+}
+
+static int vhost_set_vring(struct vhost_dev *dev,
+ unsigned long int request,
+ struct vhost_vring_state *ring)
+{
+ VhostUserMsg msg = {
+ .request = request,
+ .flags = VHOST_USER_VERSION,
+ .payload.state = *ring,
+ .size = sizeof(msg.payload.state),
+ };
+
+ vhost_user_write(dev, &msg, NULL, 0);
+
+ return 0;
+}
+
+static int vhost_user_set_vring_num(struct vhost_dev *dev,
+ struct vhost_vring_state *ring)
+{
+ return vhost_set_vring(dev, VHOST_USER_SET_VRING_NUM, ring);
+}
+
+static int vhost_user_set_vring_base(struct vhost_dev *dev,
+ struct vhost_vring_state *ring)
+{
+ return vhost_set_vring(dev, VHOST_USER_SET_VRING_BASE, ring);
+}
+
+static int vhost_user_set_vring_enable(struct vhost_dev *dev, int enable)
+{
+ int i;
+
+ if (!virtio_has_feature(dev->features, VHOST_USER_F_PROTOCOL_FEATURES)) {
+ return -1;
+ }
+
+ for (i = 0; i < dev->nvqs; ++i) {
+ struct vhost_vring_state state = {
+ .index = dev->vq_index + i,
+ .num = enable,
+ };
+
+ vhost_set_vring(dev, VHOST_USER_SET_VRING_ENABLE, &state);
+ }
+
+ return 0;
+}
+
+static int vhost_user_get_vring_base(struct vhost_dev *dev,
+ struct vhost_vring_state *ring)
+{
+ VhostUserMsg msg = {
+ .request = VHOST_USER_GET_VRING_BASE,
+ .flags = VHOST_USER_VERSION,
+ .payload.state = *ring,
+ .size = sizeof(msg.payload.state),
+ };
+
+ vhost_user_write(dev, &msg, NULL, 0);
+
+ if (vhost_user_read(dev, &msg) < 0) {
return 0;
}
- if (need_reply) {
- if (vhost_user_read(dev, &msg) < 0) {
- return 0;
- }
+ if (msg.request != VHOST_USER_GET_VRING_BASE) {
+ error_report("Received unexpected msg type. Expected %d received %d",
+ VHOST_USER_GET_VRING_BASE, msg.request);
+ return -1;
+ }
- if (msg_request != msg.request) {
- error_report("Received unexpected msg type."
- " Expected %d received %d", msg_request, msg.request);
- return -1;
- }
+ if (msg.size != sizeof(msg.payload.state)) {
+ error_report("Received bad msg size.");
+ return -1;
+ }
- switch (msg_request) {
- case VHOST_USER_GET_FEATURES:
- if (msg.size != sizeof(m.u64)) {
- error_report("Received bad msg size.");
- return -1;
- }
- *((__u64 *) arg) = msg.u64;
- break;
- case VHOST_USER_GET_VRING_BASE:
- if (msg.size != sizeof(m.state)) {
- error_report("Received bad msg size.");
- return -1;
- }
- memcpy(arg, &msg.state, sizeof(struct vhost_vring_state));
- break;
- default:
- error_report("Received unexpected msg type.");
- return -1;
- break;
- }
+ *ring = msg.payload.state;
+
+ return 0;
+}
+
+static int vhost_set_vring_file(struct vhost_dev *dev,
+ VhostUserRequest request,
+ struct vhost_vring_file *file)
+{
+ int fds[VHOST_MEMORY_MAX_NREGIONS];
+ size_t fd_num = 0;
+ VhostUserMsg msg = {
+ .request = request,
+ .flags = VHOST_USER_VERSION,
+ .payload.u64 = file->index & VHOST_USER_VRING_IDX_MASK,
+ .size = sizeof(msg.payload.u64),
+ };
+
+ if (ioeventfd_enabled() && file->fd > 0) {
+ fds[fd_num++] = file->fd;
+ } else {
+ msg.payload.u64 |= VHOST_USER_VRING_NOFD_MASK;
}
+ vhost_user_write(dev, &msg, fds, fd_num);
+
+ return 0;
+}
+
+static int vhost_user_set_vring_kick(struct vhost_dev *dev,
+ struct vhost_vring_file *file)
+{
+ return vhost_set_vring_file(dev, VHOST_USER_SET_VRING_KICK, file);
+}
+
+static int vhost_user_set_vring_call(struct vhost_dev *dev,
+ struct vhost_vring_file *file)
+{
+ return vhost_set_vring_file(dev, VHOST_USER_SET_VRING_CALL, file);
+}
+
+static int vhost_user_set_u64(struct vhost_dev *dev, int request, uint64_t u64)
+{
+ VhostUserMsg msg = {
+ .request = request,
+ .flags = VHOST_USER_VERSION,
+ .payload.u64 = u64,
+ .size = sizeof(msg.payload.u64),
+ };
+
+ vhost_user_write(dev, &msg, NULL, 0);
+
+ return 0;
+}
+
+static int vhost_user_set_features(struct vhost_dev *dev,
+ uint64_t features)
+{
+ return vhost_user_set_u64(dev, VHOST_USER_SET_FEATURES, features);
+}
+
+static int vhost_user_set_protocol_features(struct vhost_dev *dev,
+ uint64_t features)
+{
+ return vhost_user_set_u64(dev, VHOST_USER_SET_PROTOCOL_FEATURES, features);
+}
+
+static int vhost_user_get_u64(struct vhost_dev *dev, int request, uint64_t *u64)
+{
+ VhostUserMsg msg = {
+ .request = request,
+ .flags = VHOST_USER_VERSION,
+ };
+
+ if (vhost_user_one_time_request(request) && dev->vq_index != 0) {
+ return 0;
+ }
+
+ vhost_user_write(dev, &msg, NULL, 0);
+
+ if (vhost_user_read(dev, &msg) < 0) {
+ return 0;
+ }
+
+ if (msg.request != request) {
+ error_report("Received unexpected msg type. Expected %d received %d",
+ request, msg.request);
+ return -1;
+ }
+
+ if (msg.size != sizeof(msg.payload.u64)) {
+ error_report("Received bad msg size.");
+ return -1;
+ }
+
+ *u64 = msg.payload.u64;
+
+ return 0;
+}
+
+static int vhost_user_get_features(struct vhost_dev *dev, uint64_t *features)
+{
+ return vhost_user_get_u64(dev, VHOST_USER_GET_FEATURES, features);
+}
+
+static int vhost_user_set_owner(struct vhost_dev *dev)
+{
+ VhostUserMsg msg = {
+ .request = VHOST_USER_SET_OWNER,
+ .flags = VHOST_USER_VERSION,
+ };
+
+ vhost_user_write(dev, &msg, NULL, 0);
+
+ return 0;
+}
+
+static int vhost_user_reset_device(struct vhost_dev *dev)
+{
+ VhostUserMsg msg = {
+ .request = VHOST_USER_RESET_OWNER,
+ .flags = VHOST_USER_VERSION,
+ };
+
+ vhost_user_write(dev, &msg, NULL, 0);
+
return 0;
}
static int vhost_user_init(struct vhost_dev *dev, void *opaque)
{
+ uint64_t features;
+ int err;
+
assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER);
dev->opaque = opaque;
+ err = vhost_user_get_features(dev, &features);
+ if (err < 0) {
+ return err;
+ }
+
+ if (virtio_has_feature(features, VHOST_USER_F_PROTOCOL_FEATURES)) {
+ dev->backend_features |= 1ULL << VHOST_USER_F_PROTOCOL_FEATURES;
+
+ err = vhost_user_get_u64(dev, VHOST_USER_GET_PROTOCOL_FEATURES,
+ &features);
+ if (err < 0) {
+ return err;
+ }
+
+ dev->protocol_features = features & VHOST_USER_PROTOCOL_FEATURE_MASK;
+ err = vhost_user_set_protocol_features(dev, dev->protocol_features);
+ if (err < 0) {
+ return err;
+ }
+
+ /* query the max queues we support if backend supports Multiple Queue */
+ if (dev->protocol_features & (1ULL << VHOST_USER_PROTOCOL_F_MQ)) {
+ err = vhost_user_get_u64(dev, VHOST_USER_GET_QUEUE_NUM,
+ &dev->max_queues);
+ if (err < 0) {
+ return err;
+ }
+ }
+ }
+
+ if (dev->migration_blocker == NULL &&
+ !virtio_has_feature(dev->protocol_features,
+ VHOST_USER_PROTOCOL_F_LOG_SHMFD)) {
+ error_setg(&dev->migration_blocker,
+ "Migration disabled: vhost-user backend lacks "
+ "VHOST_USER_PROTOCOL_F_LOG_SHMFD feature.");
+ }
+
return 0;
}
@@ -343,9 +566,92 @@ static int vhost_user_cleanup(struct vhost_dev *dev)
return 0;
}
+static int vhost_user_get_vq_index(struct vhost_dev *dev, int idx)
+{
+ assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs);
+
+ return idx;
+}
+
+static int vhost_user_memslots_limit(struct vhost_dev *dev)
+{
+ return VHOST_MEMORY_MAX_NREGIONS;
+}
+
+static bool vhost_user_requires_shm_log(struct vhost_dev *dev)
+{
+ assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER);
+
+ return virtio_has_feature(dev->protocol_features,
+ VHOST_USER_PROTOCOL_F_LOG_SHMFD);
+}
+
+static int vhost_user_migration_done(struct vhost_dev *dev, char* mac_addr)
+{
+ VhostUserMsg msg = { 0 };
+ int err;
+
+ assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER);
+
+ /* If guest supports GUEST_ANNOUNCE do nothing */
+ if (virtio_has_feature(dev->acked_features, VIRTIO_NET_F_GUEST_ANNOUNCE)) {
+ return 0;
+ }
+
+ /* if backend supports VHOST_USER_PROTOCOL_F_RARP ask it to send the RARP */
+ if (virtio_has_feature(dev->protocol_features,
+ VHOST_USER_PROTOCOL_F_RARP)) {
+ msg.request = VHOST_USER_SEND_RARP;
+ msg.flags = VHOST_USER_VERSION;
+ memcpy((char *)&msg.payload.u64, mac_addr, 6);
+ msg.size = sizeof(msg.payload.u64);
+
+ err = vhost_user_write(dev, &msg, NULL, 0);
+ return err;
+ }
+ return -1;
+}
+
+static bool vhost_user_can_merge(struct vhost_dev *dev,
+ uint64_t start1, uint64_t size1,
+ uint64_t start2, uint64_t size2)
+{
+ ram_addr_t ram_addr;
+ int mfd, rfd;
+ MemoryRegion *mr;
+
+ mr = qemu_ram_addr_from_host((void *)(uintptr_t)start1, &ram_addr);
+ assert(mr);
+ mfd = qemu_get_ram_fd(ram_addr);
+
+ mr = qemu_ram_addr_from_host((void *)(uintptr_t)start2, &ram_addr);
+ assert(mr);
+ rfd = qemu_get_ram_fd(ram_addr);
+
+ return mfd == rfd;
+}
+
const VhostOps user_ops = {
.backend_type = VHOST_BACKEND_TYPE_USER,
- .vhost_call = vhost_user_call,
.vhost_backend_init = vhost_user_init,
- .vhost_backend_cleanup = vhost_user_cleanup
- };
+ .vhost_backend_cleanup = vhost_user_cleanup,
+ .vhost_backend_memslots_limit = vhost_user_memslots_limit,
+ .vhost_set_log_base = vhost_user_set_log_base,
+ .vhost_set_mem_table = vhost_user_set_mem_table,
+ .vhost_set_vring_addr = vhost_user_set_vring_addr,
+ .vhost_set_vring_endian = vhost_user_set_vring_endian,
+ .vhost_set_vring_num = vhost_user_set_vring_num,
+ .vhost_set_vring_base = vhost_user_set_vring_base,
+ .vhost_get_vring_base = vhost_user_get_vring_base,
+ .vhost_set_vring_kick = vhost_user_set_vring_kick,
+ .vhost_set_vring_call = vhost_user_set_vring_call,
+ .vhost_set_features = vhost_user_set_features,
+ .vhost_get_features = vhost_user_get_features,
+ .vhost_set_owner = vhost_user_set_owner,
+ .vhost_reset_device = vhost_user_reset_device,
+ .vhost_get_vq_index = vhost_user_get_vq_index,
+ .vhost_set_vring_enable = vhost_user_set_vring_enable,
+ .vhost_requires_shm_log = vhost_user_requires_shm_log,
+ .vhost_migration_done = vhost_user_migration_done,
+ .vhost_backend_can_merge = vhost_user_can_merge,
+};
diff --git a/qemu/hw/virtio/vhost.c b/qemu/hw/virtio/vhost.c
index 2712c6fc0..440071815 100644
--- a/qemu/hw/virtio/vhost.c
+++ b/qemu/hw/virtio/vhost.c
@@ -13,11 +13,14 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/virtio/vhost.h"
#include "hw/hw.h"
#include "qemu/atomic.h"
#include "qemu/range.h"
#include "qemu/error-report.h"
+#include "qemu/memfd.h"
#include <linux/vhost.h>
#include "exec/address-spaces.h"
#include "hw/virtio/virtio-bus.h"
@@ -25,6 +28,23 @@
#include "migration/migration.h"
static struct vhost_log *vhost_log;
+static struct vhost_log *vhost_log_shm;
+
+static unsigned int used_memslots;
+static QLIST_HEAD(, vhost_dev) vhost_devices =
+ QLIST_HEAD_INITIALIZER(vhost_devices);
+
+bool vhost_has_free_slot(void)
+{
+ unsigned int slots_limit = ~0U;
+ struct vhost_dev *hdev;
+
+ QLIST_FOREACH(hdev, &vhost_devices, entry) {
+ unsigned int r = hdev->vhost_ops->vhost_backend_memslots_limit(hdev);
+ slots_limit = MIN(slots_limit, r);
+ }
+ return slots_limit > used_memslots;
+}
static void vhost_dev_sync_region(struct vhost_dev *dev,
MemoryRegionSection *section,
@@ -241,6 +261,13 @@ static void vhost_dev_assign_memory(struct vhost_dev *dev,
continue;
}
+ if (dev->vhost_ops->vhost_backend_can_merge &&
+ !dev->vhost_ops->vhost_backend_can_merge(dev, uaddr, size,
+ reg->userspace_addr,
+ reg->memory_size)) {
+ continue;
+ }
+
if (merged) {
--to;
assert(to >= 0);
@@ -286,25 +313,46 @@ static uint64_t vhost_get_log_size(struct vhost_dev *dev)
}
return log_size;
}
-static struct vhost_log *vhost_log_alloc(uint64_t size)
+
+static struct vhost_log *vhost_log_alloc(uint64_t size, bool share)
{
- struct vhost_log *log = g_malloc0(sizeof *log + size * sizeof(*(log->log)));
+ struct vhost_log *log;
+ uint64_t logsize = size * sizeof(*(log->log));
+ int fd = -1;
+
+ log = g_new0(struct vhost_log, 1);
+ if (share) {
+ log->log = qemu_memfd_alloc("vhost-log", logsize,
+ F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL,
+ &fd);
+ memset(log->log, 0, logsize);
+ } else {
+ log->log = g_malloc0(logsize);
+ }
log->size = size;
log->refcnt = 1;
+ log->fd = fd;
return log;
}
-static struct vhost_log *vhost_log_get(uint64_t size)
+static struct vhost_log *vhost_log_get(uint64_t size, bool share)
{
- if (!vhost_log || vhost_log->size != size) {
- vhost_log = vhost_log_alloc(size);
+ struct vhost_log *log = share ? vhost_log_shm : vhost_log;
+
+ if (!log || log->size != size) {
+ log = vhost_log_alloc(size, share);
+ if (share) {
+ vhost_log_shm = log;
+ } else {
+ vhost_log = log;
+ }
} else {
- ++vhost_log->refcnt;
+ ++log->refcnt;
}
- return vhost_log;
+ return log;
}
static void vhost_log_put(struct vhost_dev *dev, bool sync)
@@ -321,20 +369,35 @@ static void vhost_log_put(struct vhost_dev *dev, bool sync)
if (dev->log_size && sync) {
vhost_log_sync_range(dev, 0, dev->log_size * VHOST_LOG_CHUNK - 1);
}
+
if (vhost_log == log) {
+ g_free(log->log);
vhost_log = NULL;
+ } else if (vhost_log_shm == log) {
+ qemu_memfd_free(log->log, log->size * sizeof(*(log->log)),
+ log->fd);
+ vhost_log_shm = NULL;
}
+
g_free(log);
}
}
-static inline void vhost_dev_log_resize(struct vhost_dev* dev, uint64_t size)
+static bool vhost_dev_log_is_shared(struct vhost_dev *dev)
{
- struct vhost_log *log = vhost_log_get(size);
+ return dev->vhost_ops->vhost_requires_shm_log &&
+ dev->vhost_ops->vhost_requires_shm_log(dev);
+}
+
+static inline void vhost_dev_log_resize(struct vhost_dev *dev, uint64_t size)
+{
+ struct vhost_log *log = vhost_log_get(size, vhost_dev_log_is_shared(dev));
uint64_t log_base = (uintptr_t)log->log;
int r;
- r = dev->vhost_ops->vhost_call(dev, VHOST_SET_LOG_BASE, &log_base);
+ /* inform backend of log switching, this must be done before
+ releasing the current log, to ensure no logging is lost */
+ r = dev->vhost_ops->vhost_set_log_base(dev, log_base, log);
assert(r >= 0);
vhost_log_put(dev, true);
dev->log = log;
@@ -457,6 +520,7 @@ static void vhost_set_memory(MemoryListener *listener,
dev->mem_changed_start_addr = MIN(dev->mem_changed_start_addr, start_addr);
dev->mem_changed_end_addr = MAX(dev->mem_changed_end_addr, start_addr + size - 1);
dev->memory_changed = true;
+ used_memslots = dev->mem->nregions;
}
static bool vhost_section(MemoryRegionSection *section)
@@ -500,7 +564,7 @@ static void vhost_commit(MemoryListener *listener)
}
if (!dev->log_enabled) {
- r = dev->vhost_ops->vhost_call(dev, VHOST_SET_MEM_TABLE, dev->mem);
+ r = dev->vhost_ops->vhost_set_mem_table(dev, dev->mem);
assert(r >= 0);
dev->memory_changed = false;
return;
@@ -513,7 +577,7 @@ static void vhost_commit(MemoryListener *listener)
if (dev->log_size < log_size) {
vhost_dev_log_resize(dev, log_size + VHOST_LOG_BUFFER);
}
- r = dev->vhost_ops->vhost_call(dev, VHOST_SET_MEM_TABLE, dev->mem);
+ r = dev->vhost_ops->vhost_set_mem_table(dev, dev->mem);
assert(r >= 0);
/* To log less, can only decrease log size after table update. */
if (dev->log_size > log_size + VHOST_LOG_BUFFER) {
@@ -581,7 +645,7 @@ static int vhost_virtqueue_set_addr(struct vhost_dev *dev,
.log_guest_addr = vq->used_phys,
.flags = enable_log ? (1 << VHOST_VRING_F_LOG) : 0,
};
- int r = dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_ADDR, &addr);
+ int r = dev->vhost_ops->vhost_set_vring_addr(dev, &addr);
if (r < 0) {
return -errno;
}
@@ -595,19 +659,20 @@ static int vhost_dev_set_features(struct vhost_dev *dev, bool enable_log)
if (enable_log) {
features |= 0x1ULL << VHOST_F_LOG_ALL;
}
- r = dev->vhost_ops->vhost_call(dev, VHOST_SET_FEATURES, &features);
+ r = dev->vhost_ops->vhost_set_features(dev, features);
return r < 0 ? -errno : 0;
}
static int vhost_dev_set_log(struct vhost_dev *dev, bool enable_log)
{
- int r, t, i;
+ int r, t, i, idx;
r = vhost_dev_set_features(dev, enable_log);
if (r < 0) {
goto err_features;
}
for (i = 0; i < dev->nvqs; ++i) {
- r = vhost_virtqueue_set_addr(dev, dev->vqs + i, i,
+ idx = dev->vhost_ops->vhost_get_vq_index(dev, dev->vq_index + i);
+ r = vhost_virtqueue_set_addr(dev, dev->vqs + i, idx,
enable_log);
if (r < 0) {
goto err_vq;
@@ -616,7 +681,8 @@ static int vhost_dev_set_log(struct vhost_dev *dev, bool enable_log)
return 0;
err_vq:
for (; i >= 0; --i) {
- t = vhost_virtqueue_set_addr(dev, dev->vqs + i, i,
+ idx = dev->vhost_ops->vhost_get_vq_index(dev, dev->vq_index + i);
+ t = vhost_virtqueue_set_addr(dev, dev->vqs + i, idx,
dev->log_enabled);
assert(t >= 0);
}
@@ -691,6 +757,27 @@ static void vhost_log_stop(MemoryListener *listener,
/* FIXME: implement */
}
+/* The vhost driver natively knows how to handle the vrings of non
+ * cross-endian legacy devices and modern devices. Only legacy devices
+ * exposed to a bi-endian guest may require the vhost driver to use a
+ * specific endianness.
+ */
+static inline bool vhost_needs_vring_endian(VirtIODevice *vdev)
+{
+ if (virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) {
+ return false;
+ }
+#ifdef TARGET_IS_BIENDIAN
+#ifdef HOST_WORDS_BIGENDIAN
+ return vdev->device_endian == VIRTIO_DEVICE_ENDIAN_LITTLE;
+#else
+ return vdev->device_endian == VIRTIO_DEVICE_ENDIAN_BIG;
+#endif
+#else
+ return false;
+#endif
+}
+
static int vhost_virtqueue_set_vring_endian_legacy(struct vhost_dev *dev,
bool is_big_endian,
int vhost_vq_index)
@@ -700,7 +787,7 @@ static int vhost_virtqueue_set_vring_endian_legacy(struct vhost_dev *dev,
.num = is_big_endian
};
- if (!dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_ENDIAN, &s)) {
+ if (!dev->vhost_ops->vhost_set_vring_endian(dev, &s)) {
return 0;
}
@@ -719,7 +806,7 @@ static int vhost_virtqueue_start(struct vhost_dev *dev,
{
hwaddr s, l, a;
int r;
- int vhost_vq_index = idx - dev->vq_index;
+ int vhost_vq_index = dev->vhost_ops->vhost_get_vq_index(dev, idx);
struct vhost_vring_file file = {
.index = vhost_vq_index
};
@@ -728,22 +815,20 @@ static int vhost_virtqueue_start(struct vhost_dev *dev,
};
struct VirtQueue *vvq = virtio_get_queue(vdev, idx);
- assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs);
vq->num = state.num = virtio_queue_get_num(vdev, idx);
- r = dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_NUM, &state);
+ r = dev->vhost_ops->vhost_set_vring_num(dev, &state);
if (r) {
return -errno;
}
state.num = virtio_queue_get_last_avail_idx(vdev, idx);
- r = dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_BASE, &state);
+ r = dev->vhost_ops->vhost_set_vring_base(dev, &state);
if (r) {
return -errno;
}
- if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1) &&
- virtio_legacy_is_cross_endian(vdev)) {
+ if (vhost_needs_vring_endian(vdev)) {
r = vhost_virtqueue_set_vring_endian_legacy(dev,
virtio_is_big_endian(vdev),
vhost_vq_index);
@@ -789,7 +874,7 @@ static int vhost_virtqueue_start(struct vhost_dev *dev,
}
file.fd = event_notifier_get_fd(virtio_queue_get_host_notifier(vvq));
- r = dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_KICK, &file);
+ r = dev->vhost_ops->vhost_set_vring_kick(dev, &file);
if (r) {
r = -errno;
goto fail_kick;
@@ -798,6 +883,14 @@ static int vhost_virtqueue_start(struct vhost_dev *dev,
/* Clear and discard previous events if any. */
event_notifier_test_and_clear(&vq->masked_notifier);
+ /* Init vring in unmasked state, unless guest_notifier_mask
+ * will do it later.
+ */
+ if (!vdev->use_guest_notifier_mask) {
+ /* TODO: check and handle errors. */
+ vhost_virtqueue_mask(dev, vdev, idx, false);
+ }
+
return 0;
fail_kick:
@@ -822,13 +915,13 @@ static void vhost_virtqueue_stop(struct vhost_dev *dev,
struct vhost_virtqueue *vq,
unsigned idx)
{
- int vhost_vq_index = idx - dev->vq_index;
+ int vhost_vq_index = dev->vhost_ops->vhost_get_vq_index(dev, idx);
struct vhost_vring_state state = {
.index = vhost_vq_index,
};
int r;
- assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs);
- r = dev->vhost_ops->vhost_call(dev, VHOST_GET_VRING_BASE, &state);
+
+ r = dev->vhost_ops->vhost_get_vring_base(dev, &state);
if (r < 0) {
fprintf(stderr, "vhost VQ %d ring restore failed: %d\n", idx, r);
fflush(stderr);
@@ -839,8 +932,7 @@ static void vhost_virtqueue_stop(struct vhost_dev *dev,
/* In the cross-endian case, we need to reset the vring endianness to
* native as legacy devices expect so by default.
*/
- if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1) &&
- virtio_legacy_is_cross_endian(vdev)) {
+ if (vhost_needs_vring_endian(vdev)) {
r = vhost_virtqueue_set_vring_endian_legacy(dev,
!virtio_is_big_endian(vdev),
vhost_vq_index);
@@ -875,8 +967,9 @@ static void vhost_eventfd_del(MemoryListener *listener,
static int vhost_virtqueue_init(struct vhost_dev *dev,
struct vhost_virtqueue *vq, int n)
{
+ int vhost_vq_index = dev->vhost_ops->vhost_get_vq_index(dev, n);
struct vhost_vring_file file = {
- .index = n,
+ .index = vhost_vq_index,
};
int r = event_notifier_init(&vq->masked_notifier, 0);
if (r < 0) {
@@ -884,7 +977,7 @@ static int vhost_virtqueue_init(struct vhost_dev *dev,
}
file.fd = event_notifier_get_fd(&vq->masked_notifier);
- r = dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_CALL, &file);
+ r = dev->vhost_ops->vhost_set_vring_call(dev, &file);
if (r) {
r = -errno;
goto fail_call;
@@ -906,6 +999,8 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
uint64_t features;
int i, r;
+ hdev->migration_blocker = NULL;
+
if (vhost_set_backend_type(hdev, backend_type) < 0) {
close((uintptr_t)opaque);
return -1;
@@ -916,18 +1011,26 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
return -errno;
}
- r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_OWNER, NULL);
+ if (used_memslots > hdev->vhost_ops->vhost_backend_memslots_limit(hdev)) {
+ fprintf(stderr, "vhost backend memory slots limit is less"
+ " than current number of present memory slots\n");
+ close((uintptr_t)opaque);
+ return -1;
+ }
+ QLIST_INSERT_HEAD(&vhost_devices, hdev, entry);
+
+ r = hdev->vhost_ops->vhost_set_owner(hdev);
if (r < 0) {
goto fail;
}
- r = hdev->vhost_ops->vhost_call(hdev, VHOST_GET_FEATURES, &features);
+ r = hdev->vhost_ops->vhost_get_features(hdev, &features);
if (r < 0) {
goto fail;
}
for (i = 0; i < hdev->nvqs; ++i) {
- r = vhost_virtqueue_init(hdev, hdev->vqs + i, i);
+ r = vhost_virtqueue_init(hdev, hdev->vqs + i, hdev->vq_index + i);
if (r < 0) {
goto fail_vq;
}
@@ -949,12 +1052,21 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
.eventfd_del = vhost_eventfd_del,
.priority = 10
};
- hdev->migration_blocker = NULL;
- if (!(hdev->features & (0x1ULL << VHOST_F_LOG_ALL))) {
- error_setg(&hdev->migration_blocker,
- "Migration disabled: vhost lacks VHOST_F_LOG_ALL feature.");
+
+ if (hdev->migration_blocker == NULL) {
+ if (!(hdev->features & (0x1ULL << VHOST_F_LOG_ALL))) {
+ error_setg(&hdev->migration_blocker,
+ "Migration disabled: vhost lacks VHOST_F_LOG_ALL feature.");
+ } else if (!qemu_memfd_check()) {
+ error_setg(&hdev->migration_blocker,
+ "Migration disabled: failed to allocate shared memory");
+ }
+ }
+
+ if (hdev->migration_blocker != NULL) {
migrate_add_blocker(hdev->migration_blocker);
}
+
hdev->mem = g_malloc0(offsetof(struct vhost_memory, regions));
hdev->n_mem_sections = 0;
hdev->mem_sections = NULL;
@@ -972,6 +1084,7 @@ fail_vq:
fail:
r = -errno;
hdev->vhost_ops->vhost_backend_cleanup(hdev);
+ QLIST_REMOVE(hdev, entry);
return r;
}
@@ -989,6 +1102,7 @@ void vhost_dev_cleanup(struct vhost_dev *hdev)
g_free(hdev->mem);
g_free(hdev->mem_sections);
hdev->vhost_ops->vhost_backend_cleanup(hdev);
+ QLIST_REMOVE(hdev, entry);
}
/* Stop processing guest IO notifications in qemu.
@@ -1066,18 +1180,17 @@ void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n,
{
struct VirtQueue *vvq = virtio_get_queue(vdev, n);
int r, index = n - hdev->vq_index;
+ struct vhost_vring_file file;
- assert(n >= hdev->vq_index && n < hdev->vq_index + hdev->nvqs);
-
- struct vhost_vring_file file = {
- .index = index
- };
if (mask) {
+ assert(vdev->use_guest_notifier_mask);
file.fd = event_notifier_get_fd(&hdev->vqs[index].masked_notifier);
} else {
file.fd = event_notifier_get_fd(virtio_queue_get_guest_notifier(vvq));
}
- r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_VRING_CALL, &file);
+
+ file.index = hdev->vhost_ops->vhost_get_vq_index(hdev, n);
+ r = hdev->vhost_ops->vhost_set_vring_call(hdev, &file);
assert(r >= 0);
}
@@ -1119,7 +1232,7 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
if (r < 0) {
goto fail_features;
}
- r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_MEM_TABLE, hdev->mem);
+ r = hdev->vhost_ops->vhost_set_mem_table(hdev, hdev->mem);
if (r < 0) {
r = -errno;
goto fail_mem;
@@ -1138,10 +1251,12 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
uint64_t log_base;
hdev->log_size = vhost_get_log_size(hdev);
- hdev->log = vhost_log_get(hdev->log_size);
+ hdev->log = vhost_log_get(hdev->log_size,
+ vhost_dev_log_is_shared(hdev));
log_base = (uintptr_t)hdev->log->log;
- r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_LOG_BASE,
- hdev->log_size ? &log_base : NULL);
+ r = hdev->vhost_ops->vhost_set_log_base(hdev,
+ hdev->log_size ? log_base : 0,
+ hdev->log);
if (r < 0) {
r = -errno;
goto fail_log;
diff --git a/qemu/hw/virtio/virtio-balloon.c b/qemu/hw/virtio/virtio-balloon.c
index 3577b7af9..9dbe68179 100644
--- a/qemu/hw/virtio/virtio-balloon.c
+++ b/qemu/hw/virtio/virtio-balloon.c
@@ -13,12 +13,12 @@
*
*/
+#include "qemu/osdep.h"
#include "qemu/iov.h"
#include "qemu/timer.h"
#include "qemu-common.h"
#include "hw/virtio/virtio.h"
#include "hw/i386/pc.h"
-#include "cpu.h"
#include "sysemu/balloon.h"
#include "hw/virtio/virtio-balloon.h"
#include "sysemu/kvm.h"
@@ -34,12 +34,16 @@
#include "hw/virtio/virtio-bus.h"
#include "hw/virtio/virtio-access.h"
+#define BALLOON_PAGE_SIZE (1 << VIRTIO_BALLOON_PFN_SHIFT)
+
static void balloon_page(void *addr, int deflate)
{
#if defined(__linux__)
- if (!kvm_enabled() || kvm_has_sync_mmu())
- qemu_madvise(addr, TARGET_PAGE_SIZE,
+ if (!qemu_balloon_is_inhibited() && (!kvm_enabled() ||
+ kvm_has_sync_mmu())) {
+ qemu_madvise(addr, BALLOON_PAGE_SIZE,
deflate ? QEMU_MADV_WILLNEED : QEMU_MADV_DONTNEED);
+ }
#endif
}
@@ -50,6 +54,7 @@ static const char *balloon_stat_names[] = {
[VIRTIO_BALLOON_S_MINFLT] = "stat-minor-faults",
[VIRTIO_BALLOON_S_MEMFREE] = "stat-free-memory",
[VIRTIO_BALLOON_S_MEMTOT] = "stat-total-memory",
+ [VIRTIO_BALLOON_S_AVAIL] = "stat-available-memory",
[VIRTIO_BALLOON_S_NR] = NULL
};
@@ -70,7 +75,7 @@ static inline void reset_stats(VirtIOBalloon *dev)
static bool balloon_stats_supported(const VirtIOBalloon *s)
{
VirtIODevice *vdev = VIRTIO_DEVICE(s);
- return virtio_has_feature(vdev, VIRTIO_BALLOON_F_STATS_VQ);
+ return virtio_vdev_has_feature(vdev, VIRTIO_BALLOON_F_STATS_VQ);
}
static bool balloon_stats_enabled(const VirtIOBalloon *s)
@@ -98,39 +103,43 @@ static void balloon_stats_poll_cb(void *opaque)
VirtIOBalloon *s = opaque;
VirtIODevice *vdev = VIRTIO_DEVICE(s);
- if (!balloon_stats_supported(s)) {
+ if (s->stats_vq_elem == NULL || !balloon_stats_supported(s)) {
/* re-schedule */
balloon_stats_change_timer(s, s->stats_poll_interval);
return;
}
- virtqueue_push(s->svq, &s->stats_vq_elem, s->stats_vq_offset);
+ virtqueue_push(s->svq, s->stats_vq_elem, s->stats_vq_offset);
virtio_notify(vdev, s->svq);
+ g_free(s->stats_vq_elem);
+ s->stats_vq_elem = NULL;
}
-static void balloon_stats_get_all(Object *obj, struct Visitor *v,
- void *opaque, const char *name, Error **errp)
+static void balloon_stats_get_all(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
Error *err = NULL;
VirtIOBalloon *s = opaque;
int i;
- visit_start_struct(v, NULL, "guest-stats", name, 0, &err);
+ visit_start_struct(v, name, NULL, 0, &err);
if (err) {
goto out;
}
- visit_type_int(v, &s->stats_last_update, "last-update", &err);
+ visit_type_int(v, "last-update", &s->stats_last_update, &err);
if (err) {
goto out_end;
}
- visit_start_struct(v, NULL, NULL, "stats", 0, &err);
+ visit_start_struct(v, "stats", NULL, 0, &err);
if (err) {
goto out_end;
}
- for (i = 0; !err && i < VIRTIO_BALLOON_S_NR; i++) {
- visit_type_int64(v, (int64_t *) &s->stats[i], balloon_stat_names[i],
- &err);
+ for (i = 0; i < VIRTIO_BALLOON_S_NR; i++) {
+ visit_type_uint64(v, balloon_stat_names[i], &s->stats[i], &err);
+ if (err) {
+ break;
+ }
}
error_propagate(errp, err);
err = NULL;
@@ -144,23 +153,23 @@ out:
error_propagate(errp, err);
}
-static void balloon_stats_get_poll_interval(Object *obj, struct Visitor *v,
- void *opaque, const char *name,
+static void balloon_stats_get_poll_interval(Object *obj, Visitor *v,
+ const char *name, void *opaque,
Error **errp)
{
VirtIOBalloon *s = opaque;
- visit_type_int(v, &s->stats_poll_interval, name, errp);
+ visit_type_int(v, name, &s->stats_poll_interval, errp);
}
-static void balloon_stats_set_poll_interval(Object *obj, struct Visitor *v,
- void *opaque, const char *name,
+static void balloon_stats_set_poll_interval(Object *obj, Visitor *v,
+ const char *name, void *opaque,
Error **errp)
{
VirtIOBalloon *s = opaque;
Error *local_err = NULL;
int64_t value;
- visit_type_int(v, &value, name, &local_err);
+ visit_type_int(v, name, &value, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
@@ -203,14 +212,18 @@ static void balloon_stats_set_poll_interval(Object *obj, struct Visitor *v,
static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq)
{
VirtIOBalloon *s = VIRTIO_BALLOON(vdev);
- VirtQueueElement elem;
+ VirtQueueElement *elem;
MemoryRegionSection section;
- while (virtqueue_pop(vq, &elem)) {
+ for (;;) {
size_t offset = 0;
uint32_t pfn;
+ elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
+ if (!elem) {
+ return;
+ }
- while (iov_to_buf(elem.out_sg, elem.out_num, offset, &pfn, 4) == 4) {
+ while (iov_to_buf(elem->out_sg, elem->out_num, offset, &pfn, 4) == 4) {
ram_addr_t pa;
ram_addr_t addr;
int p = virtio_ldl_p(vdev, &pfn);
@@ -233,23 +246,34 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq)
memory_region_unref(section.mr);
}
- virtqueue_push(vq, &elem, offset);
+ virtqueue_push(vq, elem, offset);
virtio_notify(vdev, vq);
+ g_free(elem);
}
}
static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq)
{
VirtIOBalloon *s = VIRTIO_BALLOON(vdev);
- VirtQueueElement *elem = &s->stats_vq_elem;
+ VirtQueueElement *elem;
VirtIOBalloonStat stat;
size_t offset = 0;
qemu_timeval tv;
- if (!virtqueue_pop(vq, elem)) {
+ elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
+ if (!elem) {
goto out;
}
+ if (s->stats_vq_elem != NULL) {
+ /* This should never happen if the driver follows the spec. */
+ virtqueue_push(vq, s->stats_vq_elem, 0);
+ virtio_notify(vdev, vq);
+ g_free(s->stats_vq_elem);
+ }
+
+ s->stats_vq_elem = elem;
+
/* Initialize the stats to get rid of any stale values. This is only
* needed to handle the case where a guest supports fewer stats than it
* used to (ie. it has booted into an old kernel).
@@ -292,6 +316,39 @@ static void virtio_balloon_get_config(VirtIODevice *vdev, uint8_t *config_data)
memcpy(config_data, &config, sizeof(struct virtio_balloon_config));
}
+static int build_dimm_list(Object *obj, void *opaque)
+{
+ GSList **list = opaque;
+
+ if (object_dynamic_cast(obj, TYPE_PC_DIMM)) {
+ DeviceState *dev = DEVICE(obj);
+ if (dev->realized) { /* only realized DIMMs matter */
+ *list = g_slist_prepend(*list, dev);
+ }
+ }
+
+ object_child_foreach(obj, build_dimm_list, opaque);
+ return 0;
+}
+
+static ram_addr_t get_current_ram_size(void)
+{
+ GSList *list = NULL, *item;
+ ram_addr_t size = ram_size;
+
+ build_dimm_list(qdev_get_machine(), &list);
+ for (item = list; item; item = g_slist_next(item)) {
+ Object *obj = OBJECT(item->data);
+ if (!strcmp(object_get_typename(obj), TYPE_PC_DIMM)) {
+ size += object_property_get_int(obj, PC_DIMM_SIZE_PROP,
+ &error_abort);
+ }
+ }
+ g_slist_free(list);
+
+ return size;
+}
+
static void virtio_balloon_set_config(VirtIODevice *vdev,
const uint8_t *config_data)
{
@@ -370,6 +427,10 @@ static int virtio_balloon_load_device(VirtIODevice *vdev, QEMUFile *f,
s->num_pages = qemu_get_be32(f);
s->actual = qemu_get_be32(f);
+
+ if (balloon_stats_enabled(s)) {
+ balloon_stats_change_timer(s, s->stats_poll_interval);
+ }
return 0;
}
@@ -412,6 +473,16 @@ static void virtio_balloon_device_unrealize(DeviceState *dev, Error **errp)
virtio_cleanup(vdev);
}
+static void virtio_balloon_device_reset(VirtIODevice *vdev)
+{
+ VirtIOBalloon *s = VIRTIO_BALLOON(vdev);
+
+ if (s->stats_vq_elem != NULL) {
+ g_free(s->stats_vq_elem);
+ s->stats_vq_elem = NULL;
+ }
+}
+
static void virtio_balloon_instance_init(Object *obj)
{
VirtIOBalloon *s = VIRTIO_BALLOON(obj);
@@ -440,6 +511,7 @@ static void virtio_balloon_class_init(ObjectClass *klass, void *data)
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
vdc->realize = virtio_balloon_device_realize;
vdc->unrealize = virtio_balloon_device_unrealize;
+ vdc->reset = virtio_balloon_device_reset;
vdc->get_config = virtio_balloon_get_config;
vdc->set_config = virtio_balloon_set_config;
vdc->get_features = virtio_balloon_get_features;
diff --git a/qemu/hw/virtio/virtio-bus.c b/qemu/hw/virtio/virtio-bus.c
index febda76b9..574f0e23f 100644
--- a/qemu/hw/virtio/virtio-bus.c
+++ b/qemu/hw/virtio/virtio-bus.c
@@ -22,6 +22,7 @@
*
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "qemu/error-report.h"
#include "hw/qdev.h"
@@ -56,6 +57,9 @@ void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp)
assert(vdc->get_features != NULL);
vdev->host_features = vdc->get_features(vdev, vdev->host_features,
errp);
+ if (klass->post_plugged != NULL) {
+ klass->post_plugged(qbus->parent, errp);
+ }
}
/* Reset the virtio_bus */
diff --git a/qemu/hw/virtio/virtio-mmio.c b/qemu/hw/virtio/virtio-mmio.c
index 18660b07b..d4cd91f8c 100644
--- a/qemu/hw/virtio/virtio-mmio.c
+++ b/qemu/hw/virtio/virtio-mmio.c
@@ -19,6 +19,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/virtio/virtio.h"
#include "qemu/host-utils.h"
diff --git a/qemu/hw/virtio/virtio-pci.c b/qemu/hw/virtio/virtio-pci.c
index c024161f5..bfedbbf17 100644
--- a/qemu/hw/virtio/virtio-pci.c
+++ b/qemu/hw/virtio/virtio-pci.c
@@ -15,7 +15,7 @@
* GNU GPL, version 2 or (at your option) any later version.
*/
-#include <inttypes.h>
+#include "qemu/osdep.h"
#include "standard-headers/linux/virtio_pci.h"
#include "hw/virtio/virtio.h"
@@ -26,6 +26,7 @@
#include "hw/virtio/virtio-balloon.h"
#include "hw/virtio/virtio-input.h"
#include "hw/pci/pci.h"
+#include "qapi/error.h"
#include "qemu/error-report.h"
#include "hw/pci/msi.h"
#include "hw/pci/msix.h"
@@ -47,6 +48,7 @@
static void virtio_pci_bus_new(VirtioBusState *bus, size_t bus_size,
VirtIOPCIProxy *dev);
+static void virtio_pci_reset(DeviceState *qdev);
/* virtio device */
/* DeviceState to VirtIOPCIProxy. For use off data-path. TODO: use QOM. */
@@ -86,6 +88,129 @@ static void virtio_pci_save_config(DeviceState *d, QEMUFile *f)
qemu_put_be16(f, vdev->config_vector);
}
+static void virtio_pci_load_modern_queue_state(VirtIOPCIQueue *vq,
+ QEMUFile *f)
+{
+ vq->num = qemu_get_be16(f);
+ vq->enabled = qemu_get_be16(f);
+ vq->desc[0] = qemu_get_be32(f);
+ vq->desc[1] = qemu_get_be32(f);
+ vq->avail[0] = qemu_get_be32(f);
+ vq->avail[1] = qemu_get_be32(f);
+ vq->used[0] = qemu_get_be32(f);
+ vq->used[1] = qemu_get_be32(f);
+}
+
+static bool virtio_pci_has_extra_state(DeviceState *d)
+{
+ VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+
+ return proxy->flags & VIRTIO_PCI_FLAG_MIGRATE_EXTRA;
+}
+
+static int get_virtio_pci_modern_state(QEMUFile *f, void *pv, size_t size)
+{
+ VirtIOPCIProxy *proxy = pv;
+ int i;
+
+ proxy->dfselect = qemu_get_be32(f);
+ proxy->gfselect = qemu_get_be32(f);
+ proxy->guest_features[0] = qemu_get_be32(f);
+ proxy->guest_features[1] = qemu_get_be32(f);
+ for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
+ virtio_pci_load_modern_queue_state(&proxy->vqs[i], f);
+ }
+
+ return 0;
+}
+
+static void virtio_pci_save_modern_queue_state(VirtIOPCIQueue *vq,
+ QEMUFile *f)
+{
+ qemu_put_be16(f, vq->num);
+ qemu_put_be16(f, vq->enabled);
+ qemu_put_be32(f, vq->desc[0]);
+ qemu_put_be32(f, vq->desc[1]);
+ qemu_put_be32(f, vq->avail[0]);
+ qemu_put_be32(f, vq->avail[1]);
+ qemu_put_be32(f, vq->used[0]);
+ qemu_put_be32(f, vq->used[1]);
+}
+
+static void put_virtio_pci_modern_state(QEMUFile *f, void *pv, size_t size)
+{
+ VirtIOPCIProxy *proxy = pv;
+ int i;
+
+ qemu_put_be32(f, proxy->dfselect);
+ qemu_put_be32(f, proxy->gfselect);
+ qemu_put_be32(f, proxy->guest_features[0]);
+ qemu_put_be32(f, proxy->guest_features[1]);
+ for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
+ virtio_pci_save_modern_queue_state(&proxy->vqs[i], f);
+ }
+}
+
+static const VMStateInfo vmstate_info_virtio_pci_modern_state = {
+ .name = "virtqueue_state",
+ .get = get_virtio_pci_modern_state,
+ .put = put_virtio_pci_modern_state,
+};
+
+static bool virtio_pci_modern_state_needed(void *opaque)
+{
+ VirtIOPCIProxy *proxy = opaque;
+
+ return !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN);
+}
+
+static const VMStateDescription vmstate_virtio_pci_modern_state = {
+ .name = "virtio_pci/modern_state",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = &virtio_pci_modern_state_needed,
+ .fields = (VMStateField[]) {
+ {
+ .name = "modern_state",
+ .version_id = 0,
+ .field_exists = NULL,
+ .size = 0,
+ .info = &vmstate_info_virtio_pci_modern_state,
+ .flags = VMS_SINGLE,
+ .offset = 0,
+ },
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_virtio_pci = {
+ .name = "virtio_pci",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_END_OF_LIST()
+ },
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_virtio_pci_modern_state,
+ NULL
+ }
+};
+
+static void virtio_pci_save_extra_state(DeviceState *d, QEMUFile *f)
+{
+ VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+
+ vmstate_save_state(f, &vmstate_virtio_pci, proxy, NULL);
+}
+
+static int virtio_pci_load_extra_state(DeviceState *d, QEMUFile *f)
+{
+ VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+
+ return vmstate_load_state(f, &vmstate_virtio_pci, proxy, 1);
+}
+
static void virtio_pci_save_queue(DeviceState *d, int n, QEMUFile *f)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
@@ -133,6 +258,7 @@ static int virtio_pci_load_queue(DeviceState *d, int n, QEMUFile *f)
if (vector != VIRTIO_NO_VECTOR) {
return msix_vector_use(&proxy->pci_dev, vector);
}
+
return 0;
}
@@ -146,7 +272,10 @@ static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy,
EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
bool legacy = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_LEGACY);
bool modern = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN);
+ bool fast_mmio = kvm_ioeventfd_any_length_enabled();
+ bool modern_pio = proxy->flags & VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY;
MemoryRegion *modern_mr = &proxy->notify.mr;
+ MemoryRegion *modern_notify_mr = &proxy->notify_pio.mr;
MemoryRegion *legacy_mr = &proxy->bar;
hwaddr modern_addr = QEMU_VIRTIO_PCI_QUEUE_MEM_MULT *
virtio_get_queue_index(vq);
@@ -162,8 +291,17 @@ static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy,
}
virtio_queue_set_host_notifier_fd_handler(vq, true, set_handler);
if (modern) {
- memory_region_add_eventfd(modern_mr, modern_addr, 2,
- true, n, notifier);
+ if (fast_mmio) {
+ memory_region_add_eventfd(modern_mr, modern_addr, 0,
+ false, n, notifier);
+ } else {
+ memory_region_add_eventfd(modern_mr, modern_addr, 2,
+ false, n, notifier);
+ }
+ if (modern_pio) {
+ memory_region_add_eventfd(modern_notify_mr, 0, 2,
+ true, n, notifier);
+ }
}
if (legacy) {
memory_region_add_eventfd(legacy_mr, legacy_addr, 2,
@@ -171,8 +309,17 @@ static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy,
}
} else {
if (modern) {
- memory_region_del_eventfd(modern_mr, modern_addr, 2,
- true, n, notifier);
+ if (fast_mmio) {
+ memory_region_del_eventfd(modern_mr, modern_addr, 0,
+ false, n, notifier);
+ } else {
+ memory_region_del_eventfd(modern_mr, modern_addr, 2,
+ false, n, notifier);
+ }
+ if (modern_pio) {
+ memory_region_del_eventfd(modern_notify_mr, 0, 2,
+ true, n, notifier);
+ }
}
if (legacy) {
memory_region_del_eventfd(legacy_mr, legacy_addr, 2,
@@ -259,9 +406,7 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
case VIRTIO_PCI_QUEUE_PFN:
pa = (hwaddr)val << VIRTIO_PCI_QUEUE_ADDR_SHIFT;
if (pa == 0) {
- virtio_pci_stop_ioeventfd(proxy);
- virtio_reset(vdev);
- msix_unuse_all_vectors(&proxy->pci_dev);
+ virtio_pci_reset(DEVICE(proxy));
}
else
virtio_queue_set_addr(vdev, vdev->queue_sel, pa);
@@ -287,8 +432,7 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
}
if (vdev->status == 0) {
- virtio_reset(vdev);
- msix_unuse_all_vectors(&proxy->pci_dev);
+ virtio_pci_reset(DEVICE(proxy));
}
/* Linux before 2.6.34 drives the device without enabling
@@ -590,7 +734,7 @@ static int kvm_virtio_pci_vq_vector_use(VirtIOPCIProxy *proxy,
int ret;
if (irqfd->users == 0) {
- ret = kvm_irqchip_add_msi_route(kvm_state, msg);
+ ret = kvm_irqchip_add_msi_route(kvm_state, msg, &proxy->pci_dev);
if (ret < 0) {
return ret;
}
@@ -661,7 +805,7 @@ static int kvm_virtio_pci_vector_use(VirtIOPCIProxy *proxy, int nvqs)
/* If guest supports masking, set up irqfd now.
* Otherwise, delay until unmasked in the frontend.
*/
- if (k->guest_notifier_mask) {
+ if (vdev->use_guest_notifier_mask && k->guest_notifier_mask) {
ret = kvm_virtio_pci_irqfd_use(proxy, queue_no, vector);
if (ret < 0) {
kvm_virtio_pci_vq_vector_release(proxy, vector);
@@ -677,7 +821,7 @@ undo:
if (vector >= msix_nr_vectors_allocated(dev)) {
continue;
}
- if (k->guest_notifier_mask) {
+ if (vdev->use_guest_notifier_mask && k->guest_notifier_mask) {
kvm_virtio_pci_irqfd_release(proxy, queue_no, vector);
}
kvm_virtio_pci_vq_vector_release(proxy, vector);
@@ -704,7 +848,7 @@ static void kvm_virtio_pci_vector_release(VirtIOPCIProxy *proxy, int nvqs)
/* If guest supports masking, clean up irqfd now.
* Otherwise, it was cleaned when masked in the frontend.
*/
- if (k->guest_notifier_mask) {
+ if (vdev->use_guest_notifier_mask && k->guest_notifier_mask) {
kvm_virtio_pci_irqfd_release(proxy, queue_no, vector);
}
kvm_virtio_pci_vq_vector_release(proxy, vector);
@@ -726,7 +870,8 @@ static int virtio_pci_vq_vector_unmask(VirtIOPCIProxy *proxy,
if (proxy->vector_irqfd) {
irqfd = &proxy->vector_irqfd[vector];
if (irqfd->msg.data != msg.data || irqfd->msg.address != msg.address) {
- ret = kvm_irqchip_update_msi_route(kvm_state, irqfd->virq, msg);
+ ret = kvm_irqchip_update_msi_route(kvm_state, irqfd->virq, msg,
+ &proxy->pci_dev);
if (ret < 0) {
return ret;
}
@@ -736,7 +881,7 @@ static int virtio_pci_vq_vector_unmask(VirtIOPCIProxy *proxy,
/* If guest supports masking, irqfd is already setup, unmask it.
* Otherwise, set it up now.
*/
- if (k->guest_notifier_mask) {
+ if (vdev->use_guest_notifier_mask && k->guest_notifier_mask) {
k->guest_notifier_mask(vdev, queue_no, false);
/* Test after unmasking to avoid losing events. */
if (k->guest_notifier_pending &&
@@ -759,7 +904,7 @@ static void virtio_pci_vq_vector_mask(VirtIOPCIProxy *proxy,
/* If guest supports masking, keep irqfd but mask it.
* Otherwise, clean it up now.
*/
- if (k->guest_notifier_mask) {
+ if (vdev->use_guest_notifier_mask && k->guest_notifier_mask) {
k->guest_notifier_mask(vdev, queue_no, true);
} else {
kvm_virtio_pci_irqfd_release(proxy, queue_no, vector);
@@ -876,7 +1021,9 @@ static int virtio_pci_set_guest_notifier(DeviceState *d, int n, bool assign,
event_notifier_cleanup(notifier);
}
- if (!msix_enabled(&proxy->pci_dev) && vdc->guest_notifier_mask) {
+ if (!msix_enabled(&proxy->pci_dev) &&
+ vdev->use_guest_notifier_mask &&
+ vdc->guest_notifier_mask) {
vdc->guest_notifier_mask(vdev, n, !assign);
}
@@ -1205,8 +1352,7 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr,
}
if (vdev->status == 0) {
- virtio_reset(vdev);
- msix_unuse_all_vectors(&proxy->pci_dev);
+ virtio_pci_reset(DEVICE(proxy));
}
break;
@@ -1238,6 +1384,7 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr,
proxy->vqs[vdev->queue_sel].avail[0],
((uint64_t)proxy->vqs[vdev->queue_sel].used[1]) << 32 |
proxy->vqs[vdev->queue_sel].used[0]);
+ proxy->vqs[vdev->queue_sel].enabled = 1;
break;
case VIRTIO_PCI_COMMON_Q_DESCLO:
proxy->vqs[vdev->queue_sel].desc[0] = val;
@@ -1280,6 +1427,17 @@ static void virtio_pci_notify_write(void *opaque, hwaddr addr,
}
}
+static void virtio_pci_notify_write_pio(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ VirtIODevice *vdev = opaque;
+ unsigned queue = val;
+
+ if (queue < VIRTIO_QUEUE_MAX) {
+ virtio_queue_notify(vdev, queue);
+ }
+}
+
static uint64_t virtio_pci_isr_read(void *opaque, hwaddr addr,
unsigned size)
{
@@ -1373,6 +1531,16 @@ static void virtio_pci_modern_regions_init(VirtIOPCIProxy *proxy)
},
.endianness = DEVICE_LITTLE_ENDIAN,
};
+ static const MemoryRegionOps notify_pio_ops = {
+ .read = virtio_pci_notify_read,
+ .write = virtio_pci_notify_write_pio,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ };
+
memory_region_init_io(&proxy->common.mr, OBJECT(proxy),
&common_ops,
@@ -1397,30 +1565,60 @@ static void virtio_pci_modern_regions_init(VirtIOPCIProxy *proxy)
virtio_bus_get_device(&proxy->bus),
"virtio-pci-notify",
proxy->notify.size);
+
+ memory_region_init_io(&proxy->notify_pio.mr, OBJECT(proxy),
+ &notify_pio_ops,
+ virtio_bus_get_device(&proxy->bus),
+ "virtio-pci-notify-pio",
+ proxy->notify.size);
}
static void virtio_pci_modern_region_map(VirtIOPCIProxy *proxy,
VirtIOPCIRegion *region,
- struct virtio_pci_cap *cap)
+ struct virtio_pci_cap *cap,
+ MemoryRegion *mr,
+ uint8_t bar)
{
- memory_region_add_subregion(&proxy->modern_bar,
- region->offset,
- &region->mr);
+ memory_region_add_subregion(mr, region->offset, &region->mr);
cap->cfg_type = region->type;
- cap->bar = proxy->modern_mem_bar;
+ cap->bar = bar;
cap->offset = cpu_to_le32(region->offset);
cap->length = cpu_to_le32(region->size);
virtio_pci_add_mem_cap(proxy, cap);
+
+}
+
+static void virtio_pci_modern_mem_region_map(VirtIOPCIProxy *proxy,
+ VirtIOPCIRegion *region,
+ struct virtio_pci_cap *cap)
+{
+ virtio_pci_modern_region_map(proxy, region, cap,
+ &proxy->modern_bar, proxy->modern_mem_bar);
}
-static void virtio_pci_modern_region_unmap(VirtIOPCIProxy *proxy,
- VirtIOPCIRegion *region)
+static void virtio_pci_modern_io_region_map(VirtIOPCIProxy *proxy,
+ VirtIOPCIRegion *region,
+ struct virtio_pci_cap *cap)
+{
+ virtio_pci_modern_region_map(proxy, region, cap,
+ &proxy->io_bar, proxy->modern_io_bar);
+}
+
+static void virtio_pci_modern_mem_region_unmap(VirtIOPCIProxy *proxy,
+ VirtIOPCIRegion *region)
{
memory_region_del_subregion(&proxy->modern_bar,
&region->mr);
}
+static void virtio_pci_modern_io_region_unmap(VirtIOPCIProxy *proxy,
+ VirtIOPCIRegion *region)
+{
+ memory_region_del_subregion(&proxy->io_bar,
+ &region->mr);
+}
+
/* This is called by virtio-bus just after the device is plugged. */
static void virtio_pci_device_plugged(DeviceState *d, Error **errp)
{
@@ -1428,6 +1626,7 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp)
VirtioBusState *bus = &proxy->bus;
bool legacy = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_LEGACY);
bool modern = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN);
+ bool modern_pio = proxy->flags & VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY;
uint8_t *config;
uint32_t size;
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
@@ -1466,16 +1665,31 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp)
.cap.cap_len = sizeof cfg,
.cap.cfg_type = VIRTIO_PCI_CAP_PCI_CFG,
};
- struct virtio_pci_cfg_cap *cfg_mask;
+ struct virtio_pci_notify_cap notify_pio = {
+ .cap.cap_len = sizeof notify,
+ .notify_off_multiplier = cpu_to_le32(0x0),
+ };
- /* TODO: add io access for speed */
+ struct virtio_pci_cfg_cap *cfg_mask;
virtio_add_feature(&vdev->host_features, VIRTIO_F_VERSION_1);
virtio_pci_modern_regions_init(proxy);
- virtio_pci_modern_region_map(proxy, &proxy->common, &cap);
- virtio_pci_modern_region_map(proxy, &proxy->isr, &cap);
- virtio_pci_modern_region_map(proxy, &proxy->device, &cap);
- virtio_pci_modern_region_map(proxy, &proxy->notify, &notify.cap);
+
+ virtio_pci_modern_mem_region_map(proxy, &proxy->common, &cap);
+ virtio_pci_modern_mem_region_map(proxy, &proxy->isr, &cap);
+ virtio_pci_modern_mem_region_map(proxy, &proxy->device, &cap);
+ virtio_pci_modern_mem_region_map(proxy, &proxy->notify, &notify.cap);
+
+ if (modern_pio) {
+ memory_region_init(&proxy->io_bar, OBJECT(proxy),
+ "virtio-pci-io", 0x4);
+
+ pci_register_bar(&proxy->pci_dev, proxy->modern_io_bar,
+ PCI_BASE_ADDRESS_SPACE_IO, &proxy->io_bar);
+
+ virtio_pci_modern_io_region_map(proxy, &proxy->notify_pio,
+ &notify_pio.cap);
+ }
pci_register_bar(&proxy->pci_dev, proxy->modern_mem_bar,
PCI_BASE_ADDRESS_SPACE_MEMORY |
@@ -1491,12 +1705,17 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp)
pci_set_long(cfg_mask->pci_cfg_data, ~0x0);
}
- if (proxy->nvectors &&
- msix_init_exclusive_bar(&proxy->pci_dev, proxy->nvectors,
- proxy->msix_bar)) {
- error_report("unable to init msix vectors to %" PRIu32,
- proxy->nvectors);
- proxy->nvectors = 0;
+ if (proxy->nvectors) {
+ int err = msix_init_exclusive_bar(&proxy->pci_dev, proxy->nvectors,
+ proxy->msix_bar);
+ if (err) {
+ /* Notice when a system that supports MSIx can't initialize it. */
+ if (err != -ENOTSUP) {
+ error_report("unable to init msix vectors to %" PRIu32,
+ proxy->nvectors);
+ }
+ proxy->nvectors = 0;
+ }
}
proxy->pci_dev.config_write = virtio_write_config;
@@ -1505,9 +1724,7 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp)
if (legacy) {
size = VIRTIO_PCI_REGION_SIZE(&proxy->pci_dev)
+ virtio_bus_get_vdev_config_len(bus);
- if (size & (size - 1)) {
- size = 1 << qemu_fls(size);
- }
+ size = pow2ceil(size);
memory_region_init_io(&proxy->bar, OBJECT(proxy),
&virtio_pci_config_ops,
@@ -1528,14 +1745,18 @@ static void virtio_pci_device_unplugged(DeviceState *d)
{
VirtIOPCIProxy *proxy = VIRTIO_PCI(d);
bool modern = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN);
+ bool modern_pio = proxy->flags & VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY;
virtio_pci_stop_ioeventfd(proxy);
if (modern) {
- virtio_pci_modern_region_unmap(proxy, &proxy->common);
- virtio_pci_modern_region_unmap(proxy, &proxy->isr);
- virtio_pci_modern_region_unmap(proxy, &proxy->device);
- virtio_pci_modern_region_unmap(proxy, &proxy->notify);
+ virtio_pci_modern_mem_region_unmap(proxy, &proxy->common);
+ virtio_pci_modern_mem_region_unmap(proxy, &proxy->isr);
+ virtio_pci_modern_mem_region_unmap(proxy, &proxy->device);
+ virtio_pci_modern_mem_region_unmap(proxy, &proxy->notify);
+ if (modern_pio) {
+ virtio_pci_modern_io_region_unmap(proxy, &proxy->notify_pio);
+ }
}
}
@@ -1555,6 +1776,7 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
*/
proxy->legacy_io_bar = 0;
proxy->msix_bar = 1;
+ proxy->modern_io_bar = 2;
proxy->modern_mem_bar = 4;
proxy->common.offset = 0x0;
@@ -1574,6 +1796,10 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
QEMU_VIRTIO_PCI_QUEUE_MEM_MULT * VIRTIO_QUEUE_MAX;
proxy->notify.type = VIRTIO_PCI_CAP_NOTIFY_CFG;
+ proxy->notify_pio.offset = 0x0;
+ proxy->notify_pio.size = 0x4;
+ proxy->notify_pio.type = VIRTIO_PCI_CAP_NOTIFY_CFG;
+
/* subclasses can enforce modern, so do this unconditionally */
memory_region_init(&proxy->modern_bar, OBJECT(proxy), "virtio-pci",
2 * QEMU_VIRTIO_PCI_QUEUE_MEM_MULT *
@@ -1588,6 +1814,29 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
address_space_init(&proxy->modern_as, &proxy->modern_cfg, "virtio-pci-cfg-as");
+ if (pci_is_express(pci_dev) && pci_bus_is_express(pci_dev->bus) &&
+ !pci_bus_is_root(pci_dev->bus)) {
+ int pos;
+
+ pos = pcie_endpoint_cap_init(pci_dev, 0);
+ assert(pos > 0);
+
+ pos = pci_add_capability(pci_dev, PCI_CAP_ID_PM, 0, PCI_PM_SIZEOF);
+ assert(pos > 0);
+
+ /*
+ * Indicates that this function complies with revision 1.2 of the
+ * PCI Power Management Interface Specification.
+ */
+ pci_set_word(pci_dev->config + pos + PCI_PM_PMC, 0x3);
+ } else {
+ /*
+ * make future invocations of pci_is_express() return false
+ * and pci_config_size() return PCI_CONFIG_SPACE_SIZE.
+ */
+ pci_dev->cap_present &= ~QEMU_PCI_CAP_EXPRESS;
+ }
+
virtio_pci_bus_new(&proxy->bus, sizeof(proxy->bus), proxy);
if (k->realize) {
k->realize(proxy, errp);
@@ -1606,9 +1855,15 @@ static void virtio_pci_reset(DeviceState *qdev)
{
VirtIOPCIProxy *proxy = VIRTIO_PCI(qdev);
VirtioBusState *bus = VIRTIO_BUS(&proxy->bus);
+ int i;
+
virtio_pci_stop_ioeventfd(proxy);
virtio_bus_reset(bus);
msix_unuse_all_vectors(&proxy->pci_dev);
+
+ for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
+ proxy->vqs[i].enabled = 0;
+ }
}
static Property virtio_pci_properties[] = {
@@ -1618,13 +1873,34 @@ static Property virtio_pci_properties[] = {
VIRTIO_PCI_FLAG_DISABLE_LEGACY_BIT, false),
DEFINE_PROP_BIT("disable-modern", VirtIOPCIProxy, flags,
VIRTIO_PCI_FLAG_DISABLE_MODERN_BIT, true),
+ DEFINE_PROP_BIT("migrate-extra", VirtIOPCIProxy, flags,
+ VIRTIO_PCI_FLAG_MIGRATE_EXTRA_BIT, true),
+ DEFINE_PROP_BIT("modern-pio-notify", VirtIOPCIProxy, flags,
+ VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY_BIT, false),
+ DEFINE_PROP_BIT("x-disable-pcie", VirtIOPCIProxy, flags,
+ VIRTIO_PCI_FLAG_DISABLE_PCIE_BIT, false),
DEFINE_PROP_END_OF_LIST(),
};
+static void virtio_pci_dc_realize(DeviceState *qdev, Error **errp)
+{
+ VirtioPCIClass *vpciklass = VIRTIO_PCI_GET_CLASS(qdev);
+ VirtIOPCIProxy *proxy = VIRTIO_PCI(qdev);
+ PCIDevice *pci_dev = &proxy->pci_dev;
+
+ if (!(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_PCIE) &&
+ !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN)) {
+ pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
+ }
+
+ vpciklass->parent_dc_realize(qdev, errp);
+}
+
static void virtio_pci_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ VirtioPCIClass *vpciklass = VIRTIO_PCI_CLASS(klass);
dc->props = virtio_pci_properties;
k->realize = virtio_pci_realize;
@@ -1632,6 +1908,8 @@ static void virtio_pci_class_init(ObjectClass *klass, void *data)
k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
k->revision = VIRTIO_PCI_ABI_VERSION;
k->class_id = PCI_CLASS_OTHERS;
+ vpciklass->parent_dc_realize = dc->realize;
+ dc->realize = virtio_pci_dc_realize;
dc->reset = virtio_pci_reset;
}
@@ -2009,10 +2287,6 @@ static const TypeInfo virtio_net_pci_info = {
/* virtio-rng-pci */
-static Property virtio_rng_pci_properties[] = {
- DEFINE_PROP_END_OF_LIST(),
-};
-
static void virtio_rng_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
{
VirtIORngPCI *vrng = VIRTIO_RNG_PCI(vpci_dev);
@@ -2039,7 +2313,6 @@ static void virtio_rng_pci_class_init(ObjectClass *klass, void *data)
k->realize = virtio_rng_pci_realize;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
- dc->props = virtio_rng_pci_properties;
pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_RNG;
@@ -2136,14 +2409,6 @@ static void virtio_tablet_initfn(Object *obj)
TYPE_VIRTIO_TABLET);
}
-static void virtio_host_initfn(Object *obj)
-{
- VirtIOInputHostPCI *dev = VIRTIO_INPUT_HOST_PCI(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VIRTIO_INPUT_HOST);
-}
-
static const TypeInfo virtio_input_pci_info = {
.name = TYPE_VIRTIO_INPUT_PCI,
.parent = TYPE_VIRTIO_PCI,
@@ -2182,12 +2447,22 @@ static const TypeInfo virtio_tablet_pci_info = {
.instance_init = virtio_tablet_initfn,
};
+#ifdef CONFIG_LINUX
+static void virtio_host_initfn(Object *obj)
+{
+ VirtIOInputHostPCI *dev = VIRTIO_INPUT_HOST_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VIRTIO_INPUT_HOST);
+}
+
static const TypeInfo virtio_host_pci_info = {
.name = TYPE_VIRTIO_INPUT_HOST_PCI,
.parent = TYPE_VIRTIO_INPUT_PCI,
.instance_size = sizeof(VirtIOInputHostPCI),
.instance_init = virtio_host_initfn,
};
+#endif
/* virtio-pci-bus */
@@ -2211,6 +2486,9 @@ static void virtio_pci_bus_class_init(ObjectClass *klass, void *data)
k->load_config = virtio_pci_load_config;
k->save_queue = virtio_pci_save_queue;
k->load_queue = virtio_pci_load_queue;
+ k->save_extra_state = virtio_pci_save_extra_state;
+ k->load_extra_state = virtio_pci_load_extra_state;
+ k->has_extra_state = virtio_pci_has_extra_state;
k->query_guest_notifiers = virtio_pci_query_guest_notifiers;
k->set_host_notifier = virtio_pci_set_host_notifier;
k->set_guest_notifiers = virtio_pci_set_guest_notifiers;
@@ -2235,7 +2513,9 @@ static void virtio_pci_register_types(void)
type_register_static(&virtio_keyboard_pci_info);
type_register_static(&virtio_mouse_pci_info);
type_register_static(&virtio_tablet_pci_info);
+#ifdef CONFIG_LINUX
type_register_static(&virtio_host_pci_info);
+#endif
type_register_static(&virtio_pci_bus_info);
type_register_static(&virtio_pci_info);
#ifdef CONFIG_VIRTFS
diff --git a/qemu/hw/virtio/virtio-pci.h b/qemu/hw/virtio/virtio-pci.h
index b6c442f52..e4548c2f9 100644
--- a/qemu/hw/virtio/virtio-pci.h
+++ b/qemu/hw/virtio/virtio-pci.h
@@ -23,7 +23,6 @@
#include "hw/virtio/virtio-scsi.h"
#include "hw/virtio/virtio-balloon.h"
#include "hw/virtio/virtio-bus.h"
-#include "hw/virtio/virtio-9p.h"
#include "hw/virtio/virtio-input.h"
#include "hw/virtio/virtio-gpu.h"
#ifdef CONFIG_VIRTFS
@@ -59,21 +58,35 @@ typedef struct VirtioBusClass VirtioPCIBusClass;
#define VIRTIO_PCI_BUS_CLASS(klass) \
OBJECT_CLASS_CHECK(VirtioPCIBusClass, klass, TYPE_VIRTIO_PCI_BUS)
+enum {
+ VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT,
+ VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT,
+ VIRTIO_PCI_FLAG_DISABLE_LEGACY_BIT,
+ VIRTIO_PCI_FLAG_DISABLE_MODERN_BIT,
+ VIRTIO_PCI_FLAG_MIGRATE_EXTRA_BIT,
+ VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY_BIT,
+ VIRTIO_PCI_FLAG_DISABLE_PCIE_BIT,
+};
+
/* Need to activate work-arounds for buggy guests at vmstate load. */
-#define VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT 0
#define VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION \
(1 << VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT)
/* Performance improves when virtqueue kick processing is decoupled from the
* vcpu thread using ioeventfd for some devices. */
-#define VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT 1
#define VIRTIO_PCI_FLAG_USE_IOEVENTFD (1 << VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT)
/* virtio version flags */
-#define VIRTIO_PCI_FLAG_DISABLE_LEGACY_BIT 2
-#define VIRTIO_PCI_FLAG_DISABLE_MODERN_BIT 3
#define VIRTIO_PCI_FLAG_DISABLE_LEGACY (1 << VIRTIO_PCI_FLAG_DISABLE_LEGACY_BIT)
#define VIRTIO_PCI_FLAG_DISABLE_MODERN (1 << VIRTIO_PCI_FLAG_DISABLE_MODERN_BIT)
+#define VIRTIO_PCI_FLAG_DISABLE_PCIE (1 << VIRTIO_PCI_FLAG_DISABLE_PCIE_BIT)
+
+/* migrate extra state */
+#define VIRTIO_PCI_FLAG_MIGRATE_EXTRA (1 << VIRTIO_PCI_FLAG_MIGRATE_EXTRA_BIT)
+
+/* have pio notification for modern device ? */
+#define VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY \
+ (1 << VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY_BIT)
typedef struct {
MSIMessage msg;
@@ -94,6 +107,7 @@ typedef struct {
typedef struct VirtioPCIClass {
PCIDeviceClass parent_class;
+ DeviceRealize parent_dc_realize;
void (*realize)(VirtIOPCIProxy *vpci_dev, Error **errp);
} VirtioPCIClass;
@@ -104,6 +118,14 @@ typedef struct VirtIOPCIRegion {
uint32_t type;
} VirtIOPCIRegion;
+typedef struct VirtIOPCIQueue {
+ uint16_t num;
+ bool enabled;
+ uint32_t desc[2];
+ uint32_t avail[2];
+ uint32_t used[2];
+} VirtIOPCIQueue;
+
struct VirtIOPCIProxy {
PCIDevice pci_dev;
MemoryRegion bar;
@@ -111,11 +133,14 @@ struct VirtIOPCIProxy {
VirtIOPCIRegion isr;
VirtIOPCIRegion device;
VirtIOPCIRegion notify;
+ VirtIOPCIRegion notify_pio;
MemoryRegion modern_bar;
+ MemoryRegion io_bar;
MemoryRegion modern_cfg;
AddressSpace modern_as;
uint32_t legacy_io_bar;
uint32_t msix_bar;
+ uint32_t modern_io_bar;
uint32_t modern_mem_bar;
int config_cap;
uint32_t flags;
@@ -124,13 +149,7 @@ struct VirtIOPCIProxy {
uint32_t dfselect;
uint32_t gfselect;
uint32_t guest_features[2];
- struct {
- uint16_t num;
- bool enabled;
- uint32_t desc[2];
- uint32_t avail[2];
- uint32_t used[2];
- } vqs[VIRTIO_QUEUE_MAX];
+ VirtIOPCIQueue vqs[VIRTIO_QUEUE_MAX];
bool ioeventfd_disabled;
bool ioeventfd_started;
@@ -226,7 +245,7 @@ struct VirtIONetPCI {
typedef struct V9fsPCIState {
VirtIOPCIProxy parent_obj;
- V9fsState vdev;
+ V9fsVirtioState vdev;
} V9fsPCIState;
#endif
@@ -267,6 +286,8 @@ struct VirtIOInputHIDPCI {
VirtIOInputHID vdev;
};
+#ifdef CONFIG_LINUX
+
#define TYPE_VIRTIO_INPUT_HOST_PCI "virtio-input-host-pci"
#define VIRTIO_INPUT_HOST_PCI(obj) \
OBJECT_CHECK(VirtIOInputHostPCI, (obj), TYPE_VIRTIO_INPUT_HOST_PCI)
@@ -276,6 +297,8 @@ struct VirtIOInputHostPCI {
VirtIOInputHost vdev;
};
+#endif
+
/*
* virtio-gpu-pci: This extends VirtioPCIProxy.
*/
diff --git a/qemu/hw/virtio/virtio-rng.c b/qemu/hw/virtio/virtio-rng.c
index 97d154191..6b991a764 100644
--- a/qemu/hw/virtio/virtio-rng.c
+++ b/qemu/hw/virtio/virtio-rng.c
@@ -9,6 +9,8 @@
* top-level directory.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu/iov.h"
#include "hw/qdev.h"
#include "hw/virtio/virtio.h"
@@ -43,7 +45,7 @@ static void chr_read(void *opaque, const void *buf, size_t size)
{
VirtIORNG *vrng = opaque;
VirtIODevice *vdev = VIRTIO_DEVICE(vrng);
- VirtQueueElement elem;
+ VirtQueueElement *elem;
size_t len;
int offset;
@@ -55,17 +57,26 @@ static void chr_read(void *opaque, const void *buf, size_t size)
offset = 0;
while (offset < size) {
- if (!virtqueue_pop(vrng->vq, &elem)) {
+ elem = virtqueue_pop(vrng->vq, sizeof(VirtQueueElement));
+ if (!elem) {
break;
}
- len = iov_from_buf(elem.in_sg, elem.in_num,
+ len = iov_from_buf(elem->in_sg, elem->in_num,
0, buf + offset, size - offset);
offset += len;
- virtqueue_push(vrng->vq, &elem, len);
+ virtqueue_push(vrng->vq, elem, len);
trace_virtio_rng_pushed(vrng, len);
+ g_free(elem);
}
virtio_notify(vdev, vrng->vq);
+
+ if (!virtio_queue_empty(vrng->vq)) {
+ /* If we didn't drain the queue, call virtio_rng_process
+ * to take care of asking for more data as appropriate.
+ */
+ virtio_rng_process(vrng);
+ }
}
static void virtio_rng_process(VirtIORNG *vrng)
diff --git a/qemu/hw/virtio/virtio.c b/qemu/hw/virtio/virtio.c
index 788b556a7..30ede3d1c 100644
--- a/qemu/hw/virtio/virtio.c
+++ b/qemu/hw/virtio/virtio.c
@@ -11,8 +11,10 @@
*
*/
-#include <inttypes.h>
-
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "trace.h"
#include "exec/address-spaces.h"
#include "qemu/error-report.h"
@@ -60,6 +62,7 @@ typedef struct VRingUsed
typedef struct VRing
{
unsigned int num;
+ unsigned int num_default;
unsigned int align;
hwaddr desc;
hwaddr avail;
@@ -69,7 +72,15 @@ typedef struct VRing
struct VirtQueue
{
VRing vring;
+
+ /* Next head to pop */
uint16_t last_avail_idx;
+
+ /* Last avail_idx read from VQ. */
+ uint16_t shadow_avail_idx;
+
+ uint16_t used_idx;
+
/* Last used index value we have signalled on */
uint16_t signalled_used;
@@ -85,6 +96,7 @@ struct VirtQueue
uint16_t vector;
void (*handle_output)(VirtIODevice *vdev, VirtQueue *vq);
+ void (*handle_aio_output)(VirtIODevice *vdev, VirtQueue *vq);
VirtIODevice *vdev;
EventNotifier guest_notifier;
EventNotifier host_notifier;
@@ -106,35 +118,15 @@ void virtio_queue_update_rings(VirtIODevice *vdev, int n)
vring->align);
}
-static inline uint64_t vring_desc_addr(VirtIODevice *vdev, hwaddr desc_pa,
- int i)
-{
- hwaddr pa;
- pa = desc_pa + sizeof(VRingDesc) * i + offsetof(VRingDesc, addr);
- return virtio_ldq_phys(vdev, pa);
-}
-
-static inline uint32_t vring_desc_len(VirtIODevice *vdev, hwaddr desc_pa, int i)
+static void vring_desc_read(VirtIODevice *vdev, VRingDesc *desc,
+ hwaddr desc_pa, int i)
{
- hwaddr pa;
- pa = desc_pa + sizeof(VRingDesc) * i + offsetof(VRingDesc, len);
- return virtio_ldl_phys(vdev, pa);
-}
-
-static inline uint16_t vring_desc_flags(VirtIODevice *vdev, hwaddr desc_pa,
- int i)
-{
- hwaddr pa;
- pa = desc_pa + sizeof(VRingDesc) * i + offsetof(VRingDesc, flags);
- return virtio_lduw_phys(vdev, pa);
-}
-
-static inline uint16_t vring_desc_next(VirtIODevice *vdev, hwaddr desc_pa,
- int i)
-{
- hwaddr pa;
- pa = desc_pa + sizeof(VRingDesc) * i + offsetof(VRingDesc, next);
- return virtio_lduw_phys(vdev, pa);
+ address_space_read(&address_space_memory, desc_pa + i * sizeof(VRingDesc),
+ MEMTXATTRS_UNSPECIFIED, (void *)desc, sizeof(VRingDesc));
+ virtio_tswap64s(vdev, &desc->addr);
+ virtio_tswap32s(vdev, &desc->len);
+ virtio_tswap16s(vdev, &desc->flags);
+ virtio_tswap16s(vdev, &desc->next);
}
static inline uint16_t vring_avail_flags(VirtQueue *vq)
@@ -148,7 +140,8 @@ static inline uint16_t vring_avail_idx(VirtQueue *vq)
{
hwaddr pa;
pa = vq->vring.avail + offsetof(VRingAvail, idx);
- return virtio_lduw_phys(vq->vdev, pa);
+ vq->shadow_avail_idx = virtio_lduw_phys(vq->vdev, pa);
+ return vq->shadow_avail_idx;
}
static inline uint16_t vring_avail_ring(VirtQueue *vq, int i)
@@ -163,18 +156,15 @@ static inline uint16_t vring_get_used_event(VirtQueue *vq)
return vring_avail_ring(vq, vq->vring.num);
}
-static inline void vring_used_ring_id(VirtQueue *vq, int i, uint32_t val)
-{
- hwaddr pa;
- pa = vq->vring.used + offsetof(VRingUsed, ring[i].id);
- virtio_stl_phys(vq->vdev, pa, val);
-}
-
-static inline void vring_used_ring_len(VirtQueue *vq, int i, uint32_t val)
+static inline void vring_used_write(VirtQueue *vq, VRingUsedElem *uelem,
+ int i)
{
hwaddr pa;
- pa = vq->vring.used + offsetof(VRingUsed, ring[i].len);
- virtio_stl_phys(vq->vdev, pa, val);
+ virtio_tswap32s(vq->vdev, &uelem->id);
+ virtio_tswap32s(vq->vdev, &uelem->len);
+ pa = vq->vring.used + offsetof(VRingUsed, ring[i]);
+ address_space_write(&address_space_memory, pa, MEMTXATTRS_UNSPECIFIED,
+ (void *)uelem, sizeof(VRingUsedElem));
}
static uint16_t vring_used_idx(VirtQueue *vq)
@@ -189,6 +179,7 @@ static inline void vring_used_idx_set(VirtQueue *vq, uint16_t val)
hwaddr pa;
pa = vq->vring.used + offsetof(VRingUsed, idx);
virtio_stw_phys(vq->vdev, pa, val);
+ vq->used_idx = val;
}
static inline void vring_used_flags_set_bit(VirtQueue *vq, int mask)
@@ -220,7 +211,7 @@ static inline void vring_set_avail_event(VirtQueue *vq, uint16_t val)
void virtio_queue_set_notification(VirtQueue *vq, int enable)
{
vq->notification = enable;
- if (virtio_has_feature(vq->vdev, VIRTIO_RING_F_EVENT_IDX)) {
+ if (virtio_vdev_has_feature(vq->vdev, VIRTIO_RING_F_EVENT_IDX)) {
vring_set_avail_event(vq, vring_avail_idx(vq));
} else if (enable) {
vring_used_flags_unset_bit(vq, VRING_USED_F_NO_NOTIFY);
@@ -238,19 +229,23 @@ int virtio_queue_ready(VirtQueue *vq)
return vq->vring.avail != 0;
}
+/* Fetch avail_idx from VQ memory only when we really need to know if
+ * guest has added some buffers. */
int virtio_queue_empty(VirtQueue *vq)
{
+ if (vq->shadow_avail_idx != vq->last_avail_idx) {
+ return 0;
+ }
+
return vring_avail_idx(vq) == vq->last_avail_idx;
}
-void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem,
- unsigned int len, unsigned int idx)
+static void virtqueue_unmap_sg(VirtQueue *vq, const VirtQueueElement *elem,
+ unsigned int len)
{
unsigned int offset;
int i;
- trace_virtqueue_fill(vq, elem, len, idx);
-
offset = 0;
for (i = 0; i < elem->in_num; i++) {
size_t size = MIN(len - offset, elem->in_sg[i].iov_len);
@@ -266,12 +261,29 @@ void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem,
cpu_physical_memory_unmap(elem->out_sg[i].iov_base,
elem->out_sg[i].iov_len,
0, elem->out_sg[i].iov_len);
+}
- idx = (idx + vring_used_idx(vq)) % vq->vring.num;
+void virtqueue_discard(VirtQueue *vq, const VirtQueueElement *elem,
+ unsigned int len)
+{
+ vq->last_avail_idx--;
+ virtqueue_unmap_sg(vq, elem, len);
+}
- /* Get a pointer to the next entry in the used ring. */
- vring_used_ring_id(vq, idx, elem->index);
- vring_used_ring_len(vq, idx, len);
+void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem,
+ unsigned int len, unsigned int idx)
+{
+ VRingUsedElem uelem;
+
+ trace_virtqueue_fill(vq, elem, len, idx);
+
+ virtqueue_unmap_sg(vq, elem, len);
+
+ idx = (idx + vq->used_idx) % vq->vring.num;
+
+ uelem.id = elem->index;
+ uelem.len = len;
+ vring_used_write(vq, &uelem, idx);
}
void virtqueue_flush(VirtQueue *vq, unsigned int count)
@@ -280,7 +292,7 @@ void virtqueue_flush(VirtQueue *vq, unsigned int count)
/* Make sure buffer is written before we update index. */
smp_wmb();
trace_virtqueue_flush(vq, count);
- old = vring_used_idx(vq);
+ old = vq->used_idx;
new = old + count;
vring_used_idx_set(vq, new);
vq->inuse -= count;
@@ -302,7 +314,7 @@ static int virtqueue_num_heads(VirtQueue *vq, unsigned int idx)
/* Check it isn't doing very strange things with descriptor numbers. */
if (num_heads > vq->vring.num) {
error_report("Guest moved used index from %u to %u",
- idx, vring_avail_idx(vq));
+ idx, vq->shadow_avail_idx);
exit(1);
}
/* On success, callers read a descriptor at vq->last_avail_idx.
@@ -331,18 +343,18 @@ static unsigned int virtqueue_get_head(VirtQueue *vq, unsigned int idx)
return head;
}
-static unsigned virtqueue_next_desc(VirtIODevice *vdev, hwaddr desc_pa,
- unsigned int i, unsigned int max)
+static unsigned virtqueue_read_next_desc(VirtIODevice *vdev, VRingDesc *desc,
+ hwaddr desc_pa, unsigned int max)
{
unsigned int next;
/* If this descriptor says it doesn't chain, we're done. */
- if (!(vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_NEXT)) {
+ if (!(desc->flags & VRING_DESC_F_NEXT)) {
return max;
}
/* Check they're not leading us off end of descriptors. */
- next = vring_desc_next(vdev, desc_pa, i);
+ next = desc->next;
/* Make sure compiler knows to grab that: we don't want it changing! */
smp_wmb();
@@ -351,6 +363,7 @@ static unsigned virtqueue_next_desc(VirtIODevice *vdev, hwaddr desc_pa,
exit(1);
}
+ vring_desc_read(vdev, desc, desc_pa, next);
return next;
}
@@ -367,6 +380,7 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes,
while (virtqueue_num_heads(vq, idx)) {
VirtIODevice *vdev = vq->vdev;
unsigned int max, num_bufs, indirect = 0;
+ VRingDesc desc;
hwaddr desc_pa;
int i;
@@ -374,9 +388,10 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes,
num_bufs = total_bufs;
i = virtqueue_get_head(vq, idx++);
desc_pa = vq->vring.desc;
+ vring_desc_read(vdev, &desc, desc_pa, i);
- if (vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_INDIRECT) {
- if (vring_desc_len(vdev, desc_pa, i) % sizeof(VRingDesc)) {
+ if (desc.flags & VRING_DESC_F_INDIRECT) {
+ if (desc.len % sizeof(VRingDesc)) {
error_report("Invalid size for indirect buffer table");
exit(1);
}
@@ -389,9 +404,10 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes,
/* loop over the indirect descriptor table */
indirect = 1;
- max = vring_desc_len(vdev, desc_pa, i) / sizeof(VRingDesc);
- desc_pa = vring_desc_addr(vdev, desc_pa, i);
+ max = desc.len / sizeof(VRingDesc);
+ desc_pa = desc.addr;
num_bufs = i = 0;
+ vring_desc_read(vdev, &desc, desc_pa, i);
}
do {
@@ -401,15 +417,15 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes,
exit(1);
}
- if (vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_WRITE) {
- in_total += vring_desc_len(vdev, desc_pa, i);
+ if (desc.flags & VRING_DESC_F_WRITE) {
+ in_total += desc.len;
} else {
- out_total += vring_desc_len(vdev, desc_pa, i);
+ out_total += desc.len;
}
if (in_total >= max_in_bytes && out_total >= max_out_bytes) {
goto done;
}
- } while ((i = virtqueue_next_desc(vdev, desc_pa, i, max)) != max);
+ } while ((i = virtqueue_read_next_desc(vdev, &desc, desc_pa, max)) != max);
if (!indirect)
total_bufs = num_bufs;
@@ -434,98 +450,256 @@ int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes,
return in_bytes <= in_total && out_bytes <= out_total;
}
-void virtqueue_map_sg(struct iovec *sg, hwaddr *addr,
- size_t num_sg, int is_write)
+static void virtqueue_map_desc(unsigned int *p_num_sg, hwaddr *addr, struct iovec *iov,
+ unsigned int max_num_sg, bool is_write,
+ hwaddr pa, size_t sz)
+{
+ unsigned num_sg = *p_num_sg;
+ assert(num_sg <= max_num_sg);
+
+ while (sz) {
+ hwaddr len = sz;
+
+ if (num_sg == max_num_sg) {
+ error_report("virtio: too many write descriptors in indirect table");
+ exit(1);
+ }
+
+ iov[num_sg].iov_base = cpu_physical_memory_map(pa, &len, is_write);
+ iov[num_sg].iov_len = len;
+ addr[num_sg] = pa;
+
+ sz -= len;
+ pa += len;
+ num_sg++;
+ }
+ *p_num_sg = num_sg;
+}
+
+static void virtqueue_map_iovec(struct iovec *sg, hwaddr *addr,
+ unsigned int *num_sg, unsigned int max_size,
+ int is_write)
{
unsigned int i;
hwaddr len;
- if (num_sg > VIRTQUEUE_MAX_SIZE) {
- error_report("virtio: map attempt out of bounds: %zd > %d",
- num_sg, VIRTQUEUE_MAX_SIZE);
- exit(1);
- }
+ /* Note: this function MUST validate input, some callers
+ * are passing in num_sg values received over the network.
+ */
+ /* TODO: teach all callers that this can fail, and return failure instead
+ * of asserting here.
+ * When we do, we might be able to re-enable NDEBUG below.
+ */
+#ifdef NDEBUG
+#error building with NDEBUG is not supported
+#endif
+ assert(*num_sg <= max_size);
- for (i = 0; i < num_sg; i++) {
+ for (i = 0; i < *num_sg; i++) {
len = sg[i].iov_len;
sg[i].iov_base = cpu_physical_memory_map(addr[i], &len, is_write);
- if (sg[i].iov_base == NULL || len != sg[i].iov_len) {
+ if (!sg[i].iov_base) {
error_report("virtio: error trying to map MMIO memory");
exit(1);
}
+ if (len != sg[i].iov_len) {
+ error_report("virtio: unexpected memory split");
+ exit(1);
+ }
}
}
-int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem)
+void virtqueue_map(VirtQueueElement *elem)
+{
+ virtqueue_map_iovec(elem->in_sg, elem->in_addr, &elem->in_num,
+ VIRTQUEUE_MAX_SIZE, 1);
+ virtqueue_map_iovec(elem->out_sg, elem->out_addr, &elem->out_num,
+ VIRTQUEUE_MAX_SIZE, 0);
+}
+
+void *virtqueue_alloc_element(size_t sz, unsigned out_num, unsigned in_num)
+{
+ VirtQueueElement *elem;
+ size_t in_addr_ofs = QEMU_ALIGN_UP(sz, __alignof__(elem->in_addr[0]));
+ size_t out_addr_ofs = in_addr_ofs + in_num * sizeof(elem->in_addr[0]);
+ size_t out_addr_end = out_addr_ofs + out_num * sizeof(elem->out_addr[0]);
+ size_t in_sg_ofs = QEMU_ALIGN_UP(out_addr_end, __alignof__(elem->in_sg[0]));
+ size_t out_sg_ofs = in_sg_ofs + in_num * sizeof(elem->in_sg[0]);
+ size_t out_sg_end = out_sg_ofs + out_num * sizeof(elem->out_sg[0]);
+
+ assert(sz >= sizeof(VirtQueueElement));
+ elem = g_malloc(out_sg_end);
+ elem->out_num = out_num;
+ elem->in_num = in_num;
+ elem->in_addr = (void *)elem + in_addr_ofs;
+ elem->out_addr = (void *)elem + out_addr_ofs;
+ elem->in_sg = (void *)elem + in_sg_ofs;
+ elem->out_sg = (void *)elem + out_sg_ofs;
+ return elem;
+}
+
+void *virtqueue_pop(VirtQueue *vq, size_t sz)
{
unsigned int i, head, max;
hwaddr desc_pa = vq->vring.desc;
VirtIODevice *vdev = vq->vdev;
+ VirtQueueElement *elem;
+ unsigned out_num, in_num;
+ hwaddr addr[VIRTQUEUE_MAX_SIZE];
+ struct iovec iov[VIRTQUEUE_MAX_SIZE];
+ VRingDesc desc;
- if (!virtqueue_num_heads(vq, vq->last_avail_idx))
- return 0;
+ if (virtio_queue_empty(vq)) {
+ return NULL;
+ }
+ /* Needed after virtio_queue_empty(), see comment in
+ * virtqueue_num_heads(). */
+ smp_rmb();
/* When we start there are none of either input nor output. */
- elem->out_num = elem->in_num = 0;
+ out_num = in_num = 0;
max = vq->vring.num;
i = head = virtqueue_get_head(vq, vq->last_avail_idx++);
- if (virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
+ if (virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
vring_set_avail_event(vq, vq->last_avail_idx);
}
- if (vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_INDIRECT) {
- if (vring_desc_len(vdev, desc_pa, i) % sizeof(VRingDesc)) {
+ vring_desc_read(vdev, &desc, desc_pa, i);
+ if (desc.flags & VRING_DESC_F_INDIRECT) {
+ if (desc.len % sizeof(VRingDesc)) {
error_report("Invalid size for indirect buffer table");
exit(1);
}
/* loop over the indirect descriptor table */
- max = vring_desc_len(vdev, desc_pa, i) / sizeof(VRingDesc);
- desc_pa = vring_desc_addr(vdev, desc_pa, i);
+ max = desc.len / sizeof(VRingDesc);
+ desc_pa = desc.addr;
i = 0;
+ vring_desc_read(vdev, &desc, desc_pa, i);
}
/* Collect all the descriptors */
do {
- struct iovec *sg;
-
- if (vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_WRITE) {
- if (elem->in_num >= ARRAY_SIZE(elem->in_sg)) {
- error_report("Too many write descriptors in indirect table");
- exit(1);
- }
- elem->in_addr[elem->in_num] = vring_desc_addr(vdev, desc_pa, i);
- sg = &elem->in_sg[elem->in_num++];
+ if (desc.flags & VRING_DESC_F_WRITE) {
+ virtqueue_map_desc(&in_num, addr + out_num, iov + out_num,
+ VIRTQUEUE_MAX_SIZE - out_num, true, desc.addr, desc.len);
} else {
- if (elem->out_num >= ARRAY_SIZE(elem->out_sg)) {
- error_report("Too many read descriptors in indirect table");
+ if (in_num) {
+ error_report("Incorrect order for descriptors");
exit(1);
}
- elem->out_addr[elem->out_num] = vring_desc_addr(vdev, desc_pa, i);
- sg = &elem->out_sg[elem->out_num++];
+ virtqueue_map_desc(&out_num, addr, iov,
+ VIRTQUEUE_MAX_SIZE, false, desc.addr, desc.len);
}
- sg->iov_len = vring_desc_len(vdev, desc_pa, i);
-
/* If we've got too many, that implies a descriptor loop. */
- if ((elem->in_num + elem->out_num) > max) {
+ if ((in_num + out_num) > max) {
error_report("Looped descriptor");
exit(1);
}
- } while ((i = virtqueue_next_desc(vdev, desc_pa, i, max)) != max);
-
- /* Now map what we have collected */
- virtqueue_map_sg(elem->in_sg, elem->in_addr, elem->in_num, 1);
- virtqueue_map_sg(elem->out_sg, elem->out_addr, elem->out_num, 0);
+ } while ((i = virtqueue_read_next_desc(vdev, &desc, desc_pa, max)) != max);
+ /* Now copy what we have collected and mapped */
+ elem = virtqueue_alloc_element(sz, out_num, in_num);
elem->index = head;
+ for (i = 0; i < out_num; i++) {
+ elem->out_addr[i] = addr[i];
+ elem->out_sg[i] = iov[i];
+ }
+ for (i = 0; i < in_num; i++) {
+ elem->in_addr[i] = addr[out_num + i];
+ elem->in_sg[i] = iov[out_num + i];
+ }
vq->inuse++;
trace_virtqueue_pop(vq, elem, elem->in_num, elem->out_num);
- return elem->in_num + elem->out_num;
+ return elem;
+}
+
+/* Reading and writing a structure directly to QEMUFile is *awful*, but
+ * it is what QEMU has always done by mistake. We can change it sooner
+ * or later by bumping the version number of the affected vm states.
+ * In the meanwhile, since the in-memory layout of VirtQueueElement
+ * has changed, we need to marshal to and from the layout that was
+ * used before the change.
+ */
+typedef struct VirtQueueElementOld {
+ unsigned int index;
+ unsigned int out_num;
+ unsigned int in_num;
+ hwaddr in_addr[VIRTQUEUE_MAX_SIZE];
+ hwaddr out_addr[VIRTQUEUE_MAX_SIZE];
+ struct iovec in_sg[VIRTQUEUE_MAX_SIZE];
+ struct iovec out_sg[VIRTQUEUE_MAX_SIZE];
+} VirtQueueElementOld;
+
+void *qemu_get_virtqueue_element(QEMUFile *f, size_t sz)
+{
+ VirtQueueElement *elem;
+ VirtQueueElementOld data;
+ int i;
+
+ qemu_get_buffer(f, (uint8_t *)&data, sizeof(VirtQueueElementOld));
+
+ elem = virtqueue_alloc_element(sz, data.out_num, data.in_num);
+ elem->index = data.index;
+
+ for (i = 0; i < elem->in_num; i++) {
+ elem->in_addr[i] = data.in_addr[i];
+ }
+
+ for (i = 0; i < elem->out_num; i++) {
+ elem->out_addr[i] = data.out_addr[i];
+ }
+
+ for (i = 0; i < elem->in_num; i++) {
+ /* Base is overwritten by virtqueue_map. */
+ elem->in_sg[i].iov_base = 0;
+ elem->in_sg[i].iov_len = data.in_sg[i].iov_len;
+ }
+
+ for (i = 0; i < elem->out_num; i++) {
+ /* Base is overwritten by virtqueue_map. */
+ elem->out_sg[i].iov_base = 0;
+ elem->out_sg[i].iov_len = data.out_sg[i].iov_len;
+ }
+
+ virtqueue_map(elem);
+ return elem;
+}
+
+void qemu_put_virtqueue_element(QEMUFile *f, VirtQueueElement *elem)
+{
+ VirtQueueElementOld data;
+ int i;
+
+ memset(&data, 0, sizeof(data));
+ data.index = elem->index;
+ data.in_num = elem->in_num;
+ data.out_num = elem->out_num;
+
+ for (i = 0; i < elem->in_num; i++) {
+ data.in_addr[i] = elem->in_addr[i];
+ }
+
+ for (i = 0; i < elem->out_num; i++) {
+ data.out_addr[i] = elem->out_addr[i];
+ }
+
+ for (i = 0; i < elem->in_num; i++) {
+ /* Base is overwritten by virtqueue_map when loading. Do not
+ * save it, as it would leak the QEMU address space layout. */
+ data.in_sg[i].iov_len = elem->in_sg[i].iov_len;
+ }
+
+ for (i = 0; i < elem->out_num; i++) {
+ /* Do not save iov_base as above. */
+ data.out_sg[i].iov_len = elem->out_sg[i].iov_len;
+ }
+ qemu_put_buffer(f, (uint8_t *)&data, sizeof(VirtQueueElementOld));
}
/* virtio device */
@@ -560,7 +734,7 @@ int virtio_set_status(VirtIODevice *vdev, uint8_t val)
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
trace_virtio_set_status(vdev, val);
- if (virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) {
+ if (virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) {
if (!(vdev->status & VIRTIO_CONFIG_S_FEATURES_OK) &&
val & VIRTIO_CONFIG_S_FEATURES_OK) {
int ret = virtio_validate_features(vdev);
@@ -629,10 +803,13 @@ void virtio_reset(void *opaque)
vdev->vq[i].vring.avail = 0;
vdev->vq[i].vring.used = 0;
vdev->vq[i].last_avail_idx = 0;
+ vdev->vq[i].shadow_avail_idx = 0;
+ vdev->vq[i].used_idx = 0;
virtio_queue_set_vector(vdev, i, VIRTIO_NO_VECTOR);
vdev->vq[i].signalled_used = 0;
vdev->vq[i].signalled_used_valid = false;
vdev->vq[i].notification = true;
+ vdev->vq[i].vring.num = vdev->vq[i].vring.num_default;
}
}
@@ -898,7 +1075,7 @@ void virtio_queue_set_align(VirtIODevice *vdev, int n, int align)
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
/* virtio-1 compliant devices cannot change the alignment */
- if (virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) {
+ if (virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) {
error_report("tried to modify queue alignment for virtio-1 device");
return;
}
@@ -912,7 +1089,17 @@ void virtio_queue_set_align(VirtIODevice *vdev, int n, int align)
virtio_queue_update_rings(vdev, n);
}
-void virtio_queue_notify_vq(VirtQueue *vq)
+static void virtio_queue_notify_aio_vq(VirtQueue *vq)
+{
+ if (vq->vring.desc && vq->handle_aio_output) {
+ VirtIODevice *vdev = vq->vdev;
+
+ trace_virtio_queue_notify(vdev, vq - vdev->vq, vq);
+ vq->handle_aio_output(vdev, vq);
+ }
+}
+
+static void virtio_queue_notify_vq(VirtQueue *vq)
{
if (vq->vring.desc && vq->handle_output) {
VirtIODevice *vdev = vq->vdev;
@@ -964,8 +1151,10 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size,
abort();
vdev->vq[i].vring.num = queue_size;
+ vdev->vq[i].vring.num_default = queue_size;
vdev->vq[i].vring.align = VIRTIO_PCI_VRING_ALIGN;
vdev->vq[i].handle_output = handle_output;
+ vdev->vq[i].handle_aio_output = NULL;
return &vdev->vq[i];
}
@@ -977,6 +1166,7 @@ void virtio_del_queue(VirtIODevice *vdev, int n)
}
vdev->vq[n].vring.num = 0;
+ vdev->vq[n].vring.num_default = 0;
}
void virtio_irq(VirtQueue *vq)
@@ -986,32 +1176,32 @@ void virtio_irq(VirtQueue *vq)
virtio_notify_vector(vq->vdev, vq->vector);
}
-static bool vring_notify(VirtIODevice *vdev, VirtQueue *vq)
+bool virtio_should_notify(VirtIODevice *vdev, VirtQueue *vq)
{
uint16_t old, new;
bool v;
/* We need to expose used array entries before checking used event. */
smp_mb();
/* Always notify when queue is empty (when feature acknowledge) */
- if (virtio_has_feature(vdev, VIRTIO_F_NOTIFY_ON_EMPTY) &&
- !vq->inuse && vring_avail_idx(vq) == vq->last_avail_idx) {
+ if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFY_ON_EMPTY) &&
+ !vq->inuse && virtio_queue_empty(vq)) {
return true;
}
- if (!virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
+ if (!virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
return !(vring_avail_flags(vq) & VRING_AVAIL_F_NO_INTERRUPT);
}
v = vq->signalled_used_valid;
vq->signalled_used_valid = true;
old = vq->signalled_used;
- new = vq->signalled_used = vring_used_idx(vq);
+ new = vq->signalled_used = vq->used_idx;
return !v || vring_need_event(vring_get_used_event(vq), new, old);
}
void virtio_notify(VirtIODevice *vdev, VirtQueue *vq)
{
- if (!vring_notify(vdev, vq)) {
+ if (!virtio_should_notify(vdev, vq)) {
return;
}
@@ -1035,7 +1225,7 @@ static bool virtio_device_endian_needed(void *opaque)
VirtIODevice *vdev = opaque;
assert(vdev->device_endian != VIRTIO_DEVICE_ENDIAN_UNKNOWN);
- if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) {
+ if (!virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) {
return vdev->device_endian != virtio_default_endian();
}
/* Devices conforming to VIRTIO 1.0 or later are always LE. */
@@ -1056,33 +1246,38 @@ static bool virtio_virtqueue_needed(void *opaque)
return virtio_host_has_feature(vdev, VIRTIO_F_VERSION_1);
}
-static void put_virtqueue_state(QEMUFile *f, void *pv, size_t size)
+static bool virtio_ringsize_needed(void *opaque)
{
- VirtIODevice *vdev = pv;
+ VirtIODevice *vdev = opaque;
int i;
for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
- qemu_put_be64(f, vdev->vq[i].vring.avail);
- qemu_put_be64(f, vdev->vq[i].vring.used);
+ if (vdev->vq[i].vring.num != vdev->vq[i].vring.num_default) {
+ return true;
+ }
}
+ return false;
}
-static int get_virtqueue_state(QEMUFile *f, void *pv, size_t size)
+static bool virtio_extra_state_needed(void *opaque)
{
- VirtIODevice *vdev = pv;
- int i;
+ VirtIODevice *vdev = opaque;
+ BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
- for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
- vdev->vq[i].vring.avail = qemu_get_be64(f);
- vdev->vq[i].vring.used = qemu_get_be64(f);
- }
- return 0;
+ return k->has_extra_state &&
+ k->has_extra_state(qbus->parent);
}
-static VMStateInfo vmstate_info_virtqueue = {
+static const VMStateDescription vmstate_virtqueue = {
.name = "virtqueue_state",
- .get = get_virtqueue_state,
- .put = put_virtqueue_state,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(vring.avail, struct VirtQueue),
+ VMSTATE_UINT64(vring.used, struct VirtQueue),
+ VMSTATE_END_OF_LIST()
+ }
};
static const VMStateDescription vmstate_virtio_virtqueues = {
@@ -1091,12 +1286,74 @@ static const VMStateDescription vmstate_virtio_virtqueues = {
.minimum_version_id = 1,
.needed = &virtio_virtqueue_needed,
.fields = (VMStateField[]) {
+ VMSTATE_STRUCT_VARRAY_POINTER_KNOWN(vq, struct VirtIODevice,
+ VIRTIO_QUEUE_MAX, 0, vmstate_virtqueue, VirtQueue),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_ringsize = {
+ .name = "ringsize_state",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(vring.num_default, struct VirtQueue),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_virtio_ringsize = {
+ .name = "virtio/ringsize",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = &virtio_ringsize_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT_VARRAY_POINTER_KNOWN(vq, struct VirtIODevice,
+ VIRTIO_QUEUE_MAX, 0, vmstate_ringsize, VirtQueue),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int get_extra_state(QEMUFile *f, void *pv, size_t size)
+{
+ VirtIODevice *vdev = pv;
+ BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
+
+ if (!k->load_extra_state) {
+ return -1;
+ } else {
+ return k->load_extra_state(qbus->parent, f);
+ }
+}
+
+static void put_extra_state(QEMUFile *f, void *pv, size_t size)
+{
+ VirtIODevice *vdev = pv;
+ BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
+
+ k->save_extra_state(qbus->parent, f);
+}
+
+static const VMStateInfo vmstate_info_extra_state = {
+ .name = "virtqueue_extra_state",
+ .get = get_extra_state,
+ .put = put_extra_state,
+};
+
+static const VMStateDescription vmstate_virtio_extra_state = {
+ .name = "virtio/extra_state",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = &virtio_extra_state_needed,
+ .fields = (VMStateField[]) {
{
- .name = "virtqueues",
+ .name = "extra_state",
.version_id = 0,
.field_exists = NULL,
.size = 0,
- .info = &vmstate_info_virtqueue,
+ .info = &vmstate_info_extra_state,
.flags = VMS_SINGLE,
.offset = 0,
},
@@ -1138,6 +1395,8 @@ static const VMStateDescription vmstate_virtio = {
&vmstate_virtio_device_endian,
&vmstate_virtio_64bit_features,
&vmstate_virtio_virtqueues,
+ &vmstate_virtio_ringsize,
+ &vmstate_virtio_extra_state,
NULL
}
};
@@ -1264,7 +1523,7 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
num = qemu_get_be32(f);
if (num > VIRTIO_QUEUE_MAX) {
- error_report("Invalid number of PCI queues: 0x%x", num);
+ error_report("Invalid number of virtqueues: 0x%x", num);
return -1;
}
@@ -1348,6 +1607,8 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
vdev->vq[i].last_avail_idx, nheads);
return -1;
}
+ vdev->vq[i].used_idx = vring_used_idx(&vdev->vq[i]);
+ vdev->vq[i].shadow_avail_idx = vring_avail_idx(&vdev->vq[i]);
}
}
@@ -1430,6 +1691,7 @@ void virtio_init(VirtIODevice *vdev, const char *name,
vdev->vmstate = qemu_add_vm_change_state_handler(virtio_vmstate_change,
vdev);
vdev->device_endian = virtio_default_endian();
+ vdev->use_guest_notifier_mask = true;
}
hwaddr virtio_queue_get_desc_addr(VirtIODevice *vdev, int n)
@@ -1460,7 +1722,7 @@ hwaddr virtio_queue_get_desc_size(VirtIODevice *vdev, int n)
hwaddr virtio_queue_get_avail_size(VirtIODevice *vdev, int n)
{
return offsetof(VRingAvail, ring) +
- sizeof(uint64_t) * vdev->vq[n].vring.num;
+ sizeof(uint16_t) * vdev->vq[n].vring.num;
}
hwaddr virtio_queue_get_used_size(VirtIODevice *vdev, int n)
@@ -1483,6 +1745,7 @@ uint16_t virtio_queue_get_last_avail_idx(VirtIODevice *vdev, int n)
void virtio_queue_set_last_avail_idx(VirtIODevice *vdev, int n, uint16_t idx)
{
vdev->vq[n].last_avail_idx = idx;
+ vdev->vq[n].shadow_avail_idx = idx;
}
void virtio_queue_invalidate_signalled_used(VirtIODevice *vdev, int n)
@@ -1512,10 +1775,10 @@ void virtio_queue_set_guest_notifier_fd_handler(VirtQueue *vq, bool assign,
bool with_irqfd)
{
if (assign && !with_irqfd) {
- event_notifier_set_handler(&vq->guest_notifier,
+ event_notifier_set_handler(&vq->guest_notifier, false,
virtio_queue_guest_notifier_read);
} else {
- event_notifier_set_handler(&vq->guest_notifier, NULL);
+ event_notifier_set_handler(&vq->guest_notifier, false, NULL);
}
if (!assign) {
/* Test and clear notifier before closing it,
@@ -1529,6 +1792,31 @@ EventNotifier *virtio_queue_get_guest_notifier(VirtQueue *vq)
return &vq->guest_notifier;
}
+static void virtio_queue_host_notifier_aio_read(EventNotifier *n)
+{
+ VirtQueue *vq = container_of(n, VirtQueue, host_notifier);
+ if (event_notifier_test_and_clear(n)) {
+ virtio_queue_notify_aio_vq(vq);
+ }
+}
+
+void virtio_queue_aio_set_host_notifier_handler(VirtQueue *vq, AioContext *ctx,
+ void (*handle_output)(VirtIODevice *,
+ VirtQueue *))
+{
+ if (handle_output) {
+ vq->handle_aio_output = handle_output;
+ aio_set_event_notifier(ctx, &vq->host_notifier, true,
+ virtio_queue_host_notifier_aio_read);
+ } else {
+ aio_set_event_notifier(ctx, &vq->host_notifier, true, NULL);
+ /* Test and clear notifier before after disabling event,
+ * in case poll callback didn't have time to run. */
+ virtio_queue_host_notifier_aio_read(&vq->host_notifier);
+ vq->handle_aio_output = NULL;
+ }
+}
+
static void virtio_queue_host_notifier_read(EventNotifier *n)
{
VirtQueue *vq = container_of(n, VirtQueue, host_notifier);
@@ -1541,10 +1829,10 @@ void virtio_queue_set_host_notifier_fd_handler(VirtQueue *vq, bool assign,
bool set_handler)
{
if (assign && set_handler) {
- event_notifier_set_handler(&vq->host_notifier,
+ event_notifier_set_handler(&vq->host_notifier, true,
virtio_queue_host_notifier_read);
} else {
- event_notifier_set_handler(&vq->host_notifier, NULL);
+ event_notifier_set_handler(&vq->host_notifier, true, NULL);
}
if (!assign) {
/* Test and clear notifier before after disabling event,
diff --git a/qemu/hw/watchdog/watchdog.c b/qemu/hw/watchdog/watchdog.c
index 8d4b0eeeb..bbf3646ba 100644
--- a/qemu/hw/watchdog/watchdog.c
+++ b/qemu/hw/watchdog/watchdog.c
@@ -19,7 +19,7 @@
* By Richard W.M. Jones (rjones@redhat.com).
*/
-#include "qemu-common.h"
+#include "qemu/osdep.h"
#include "qemu/option.h"
#include "qemu/config-file.h"
#include "qemu/queue.h"
@@ -28,15 +28,7 @@
#include "sysemu/watchdog.h"
#include "qapi-event.h"
#include "hw/nmi.h"
-
-/* Possible values for action parameter. */
-#define WDT_RESET 1 /* Hard reset. */
-#define WDT_SHUTDOWN 2 /* Shutdown. */
-#define WDT_POWEROFF 3 /* Quit. */
-#define WDT_PAUSE 4 /* Pause. */
-#define WDT_DEBUG 5 /* Prints a message and continues running. */
-#define WDT_NONE 6 /* Do nothing. */
-#define WDT_NMI 7 /* Inject nmi into the guest */
+#include "qemu/help_option.h"
static int watchdog_action = WDT_RESET;
static QLIST_HEAD(watchdog_list, WatchdogTimerModel) watchdog_list;
@@ -105,6 +97,11 @@ int select_watchdog_action(const char *p)
return 0;
}
+int get_watchdog_action(void)
+{
+ return watchdog_action;
+}
+
/* This actually performs the "action" once a watchdog has expired,
* ie. reboot, shutdown, exit, etc.
*/
diff --git a/qemu/hw/watchdog/wdt_diag288.c b/qemu/hw/watchdog/wdt_diag288.c
index 2a885a447..f54a35a0e 100644
--- a/qemu/hw/watchdog/wdt_diag288.c
+++ b/qemu/hw/watchdog/wdt_diag288.c
@@ -11,6 +11,7 @@
*
*/
+#include "qemu/osdep.h"
#include "sysemu/watchdog.h"
#include "hw/sysbus.h"
#include "qemu/timer.h"
@@ -50,8 +51,19 @@ static void diag288_reset(void *opaque)
static void diag288_timer_expired(void *dev)
{
qemu_log_mask(CPU_LOG_RESET, "Watchdog timer expired.\n");
+ /* Reset the watchdog only if the guest gets notified about
+ * expiry. watchdog_perform_action() may temporarily relinquish
+ * the BQL; reset before triggering the action to avoid races with
+ * diag288 instructions. */
+ switch (get_watchdog_action()) {
+ case WDT_DEBUG:
+ case WDT_NONE:
+ case WDT_PAUSE:
+ break;
+ default:
+ wdt_diag288_reset(dev);
+ }
watchdog_perform_action();
- wdt_diag288_reset(dev);
}
static int wdt_diag288_handle_timer(DIAG288State *diag288,
@@ -67,7 +79,7 @@ static int wdt_diag288_handle_timer(DIAG288State *diag288,
}
timer_mod(diag288->timer,
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
- timeout * get_ticks_per_sec());
+ timeout * NANOSECONDS_PER_SECOND);
break;
case WDT_DIAG288_CANCEL:
if (!diag288->enabled) {
diff --git a/qemu/hw/watchdog/wdt_i6300esb.c b/qemu/hw/watchdog/wdt_i6300esb.c
index cfa2b1be1..a83d95121 100644
--- a/qemu/hw/watchdog/wdt_i6300esb.c
+++ b/qemu/hw/watchdog/wdt_i6300esb.c
@@ -19,7 +19,7 @@
* By Richard W.M. Jones (rjones@redhat.com).
*/
-#include <inttypes.h>
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/timer.h"
@@ -129,14 +129,9 @@ static void i6300esb_restart_timer(I6300State *d, int stage)
else
timeout <<= 5;
- /* Get the timeout in units of ticks_per_sec.
- *
- * ticks_per_sec is typically 10^9 == 0x3B9ACA00 (30 bits), with
- * 20 bits of user supplied preload, and 15 bits of scale, the
- * multiply here can exceed 64-bits, before we divide by 33MHz, so
- * we use a higher-precision intermediate result.
- */
- timeout = muldiv64(get_ticks_per_sec(), timeout, 33000000);
+ /* Get the timeout in nanoseconds. */
+
+ timeout = timeout * 30; /* on a PCI bus, 1 tick is 30 ns*/
i6300esb_debug("stage %d, timeout %" PRIi64 "\n", d->stage, timeout);
diff --git a/qemu/hw/watchdog/wdt_ib700.c b/qemu/hw/watchdog/wdt_ib700.c
index 0917a713d..532afe89e 100644
--- a/qemu/hw/watchdog/wdt_ib700.c
+++ b/qemu/hw/watchdog/wdt_ib700.c
@@ -19,6 +19,7 @@
* By Richard W.M. Jones (rjones@redhat.com).
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/timer.h"
#include "sysemu/watchdog.h"
@@ -63,7 +64,7 @@ static void ib700_write_enable_reg(void *vp, uint32_t addr, uint32_t data)
ib700_debug("addr = %x, data = %x\n", addr, data);
- timeout = (int64_t) time_map[data & 0xF] * get_ticks_per_sec();
+ timeout = (int64_t) time_map[data & 0xF] * NANOSECONDS_PER_SECOND;
timer_mod(s->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + timeout);
}
diff --git a/qemu/hw/xen/Makefile.objs b/qemu/hw/xen/Makefile.objs
index a0ca0aa3d..d3670940b 100644
--- a/qemu/hw/xen/Makefile.objs
+++ b/qemu/hw/xen/Makefile.objs
@@ -2,4 +2,4 @@
common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o
obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o
-obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o
+obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_graphics.o xen_pt_msi.o
diff --git a/qemu/hw/xen/xen-host-pci-device.c b/qemu/hw/xen/xen-host-pci-device.c
index 743b37b99..eed8cc88e 100644
--- a/qemu/hw/xen/xen-host-pci-device.c
+++ b/qemu/hw/xen/xen-host-pci-device.c
@@ -6,7 +6,10 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu-common.h"
+#include "qemu/cutils.h"
#include "xen-host-pci-device.h"
#define XEN_HOST_PCI_MAX_EXT_CAP \
@@ -31,25 +34,20 @@
#define IORESOURCE_PREFETCH 0x00001000 /* No side effects */
#define IORESOURCE_MEM_64 0x00100000
-static int xen_host_pci_sysfs_path(const XenHostPCIDevice *d,
- const char *name, char *buf, ssize_t size)
+static void xen_host_pci_sysfs_path(const XenHostPCIDevice *d,
+ const char *name, char *buf, ssize_t size)
{
int rc;
rc = snprintf(buf, size, "/sys/bus/pci/devices/%04x:%02x:%02x.%d/%s",
d->domain, d->bus, d->dev, d->func, name);
-
- if (rc >= size || rc < 0) {
- /* The ouput is truncated or an other error is encountered */
- return -ENODEV;
- }
- return 0;
+ assert(rc >= 0 && rc < size);
}
/* This size should be enough to read the first 7 lines of a resource file */
#define XEN_HOST_PCI_RESOURCE_BUFFER_SIZE 400
-static int xen_host_pci_get_resource(XenHostPCIDevice *d)
+static void xen_host_pci_get_resource(XenHostPCIDevice *d, Error **errp)
{
int i, rc, fd;
char path[PATH_MAX];
@@ -58,25 +56,22 @@ static int xen_host_pci_get_resource(XenHostPCIDevice *d)
char *endptr, *s;
uint8_t type;
- rc = xen_host_pci_sysfs_path(d, "resource", path, sizeof (path));
- if (rc) {
- return rc;
- }
+ xen_host_pci_sysfs_path(d, "resource", path, sizeof(path));
+
fd = open(path, O_RDONLY);
if (fd == -1) {
- XEN_HOST_PCI_LOG("Error: Can't open %s: %s\n", path, strerror(errno));
- return -errno;
+ error_setg_file_open(errp, errno, path);
+ return;
}
do {
- rc = read(fd, &buf, sizeof (buf) - 1);
+ rc = read(fd, &buf, sizeof(buf) - 1);
if (rc < 0 && errno != EINTR) {
- rc = -errno;
+ error_setg_errno(errp, errno, "read err");
goto out;
}
} while (rc < 0);
buf[rc] = 0;
- rc = 0;
s = buf;
for (i = 0; i < PCI_NUM_REGIONS; i++) {
@@ -129,70 +124,69 @@ static int xen_host_pci_get_resource(XenHostPCIDevice *d)
d->rom.bus_flags = flags & IORESOURCE_BITS;
}
}
+
if (i != PCI_NUM_REGIONS) {
- /* Invalid format or input to short */
- rc = -ENODEV;
+ error_setg(errp, "Invalid format or input too short: %s", buf);
}
out:
close(fd);
- return rc;
}
/* This size should be enough to read a long from a file */
#define XEN_HOST_PCI_GET_VALUE_BUFFER_SIZE 22
-static int xen_host_pci_get_value(XenHostPCIDevice *d, const char *name,
- unsigned int *pvalue, int base)
+static void xen_host_pci_get_value(XenHostPCIDevice *d, const char *name,
+ unsigned int *pvalue, int base, Error **errp)
{
char path[PATH_MAX];
char buf[XEN_HOST_PCI_GET_VALUE_BUFFER_SIZE];
int fd, rc;
unsigned long value;
- char *endptr;
+ const char *endptr;
+
+ xen_host_pci_sysfs_path(d, name, path, sizeof(path));
- rc = xen_host_pci_sysfs_path(d, name, path, sizeof (path));
- if (rc) {
- return rc;
- }
fd = open(path, O_RDONLY);
if (fd == -1) {
- XEN_HOST_PCI_LOG("Error: Can't open %s: %s\n", path, strerror(errno));
- return -errno;
+ error_setg_file_open(errp, errno, path);
+ return;
}
+
do {
- rc = read(fd, &buf, sizeof (buf) - 1);
+ rc = read(fd, &buf, sizeof(buf) - 1);
if (rc < 0 && errno != EINTR) {
- rc = -errno;
+ error_setg_errno(errp, errno, "read err");
goto out;
}
} while (rc < 0);
+
buf[rc] = 0;
- value = strtol(buf, &endptr, base);
- if (endptr == buf || *endptr != '\n') {
- rc = -1;
- } else if ((value == LONG_MIN || value == LONG_MAX) && errno == ERANGE) {
- rc = -errno;
- } else {
- rc = 0;
+ rc = qemu_strtoul(buf, &endptr, base, &value);
+ if (!rc) {
+ assert(value <= UINT_MAX);
*pvalue = value;
+ } else {
+ error_setg_errno(errp, -rc, "failed to parse value '%s'", buf);
}
+
out:
close(fd);
- return rc;
}
-static inline int xen_host_pci_get_hex_value(XenHostPCIDevice *d,
- const char *name,
- unsigned int *pvalue)
+static inline void xen_host_pci_get_hex_value(XenHostPCIDevice *d,
+ const char *name,
+ unsigned int *pvalue,
+ Error **errp)
{
- return xen_host_pci_get_value(d, name, pvalue, 16);
+ xen_host_pci_get_value(d, name, pvalue, 16, errp);
}
-static inline int xen_host_pci_get_dec_value(XenHostPCIDevice *d,
- const char *name,
- unsigned int *pvalue)
+static inline void xen_host_pci_get_dec_value(XenHostPCIDevice *d,
+ const char *name,
+ unsigned int *pvalue,
+ Error **errp)
{
- return xen_host_pci_get_value(d, name, pvalue, 10);
+ xen_host_pci_get_value(d, name, pvalue, 10, errp);
}
static bool xen_host_pci_dev_is_virtfn(XenHostPCIDevice *d)
@@ -200,26 +194,21 @@ static bool xen_host_pci_dev_is_virtfn(XenHostPCIDevice *d)
char path[PATH_MAX];
struct stat buf;
- if (xen_host_pci_sysfs_path(d, "physfn", path, sizeof (path))) {
- return false;
- }
+ xen_host_pci_sysfs_path(d, "physfn", path, sizeof(path));
+
return !stat(path, &buf);
}
-static int xen_host_pci_config_open(XenHostPCIDevice *d)
+static void xen_host_pci_config_open(XenHostPCIDevice *d, Error **errp)
{
char path[PATH_MAX];
- int rc;
- rc = xen_host_pci_sysfs_path(d, "config", path, sizeof (path));
- if (rc) {
- return rc;
- }
+ xen_host_pci_sysfs_path(d, "config", path, sizeof(path));
+
d->config_fd = open(path, O_RDWR);
- if (d->config_fd < 0) {
- return -errno;
+ if (d->config_fd == -1) {
+ error_setg_file_open(errp, errno, path);
}
- return 0;
}
static int xen_host_pci_config_read(XenHostPCIDevice *d,
@@ -341,11 +330,12 @@ int xen_host_pci_find_ext_cap_offset(XenHostPCIDevice *d, uint32_t cap)
return -1;
}
-int xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain,
- uint8_t bus, uint8_t dev, uint8_t func)
+void xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain,
+ uint8_t bus, uint8_t dev, uint8_t func,
+ Error **errp)
{
unsigned int v;
- int rc = 0;
+ Error *err = NULL;
d->config_fd = -1;
d->domain = domain;
@@ -353,38 +343,56 @@ int xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain,
d->dev = dev;
d->func = func;
- rc = xen_host_pci_config_open(d);
- if (rc) {
+ xen_host_pci_config_open(d, &err);
+ if (err) {
goto error;
}
- rc = xen_host_pci_get_resource(d);
- if (rc) {
+
+ xen_host_pci_get_resource(d, &err);
+ if (err) {
goto error;
}
- rc = xen_host_pci_get_hex_value(d, "vendor", &v);
- if (rc) {
+
+ xen_host_pci_get_hex_value(d, "vendor", &v, &err);
+ if (err) {
goto error;
}
d->vendor_id = v;
- rc = xen_host_pci_get_hex_value(d, "device", &v);
- if (rc) {
+
+ xen_host_pci_get_hex_value(d, "device", &v, &err);
+ if (err) {
goto error;
}
d->device_id = v;
- rc = xen_host_pci_get_dec_value(d, "irq", &v);
- if (rc) {
+
+ xen_host_pci_get_dec_value(d, "irq", &v, &err);
+ if (err) {
goto error;
}
d->irq = v;
+
+ xen_host_pci_get_hex_value(d, "class", &v, &err);
+ if (err) {
+ goto error;
+ }
+ d->class_code = v;
+
d->is_virtfn = xen_host_pci_dev_is_virtfn(d);
- return 0;
+ return;
+
error:
+ error_propagate(errp, err);
+
if (d->config_fd >= 0) {
close(d->config_fd);
d->config_fd = -1;
}
- return rc;
+}
+
+bool xen_host_pci_device_closed(XenHostPCIDevice *d)
+{
+ return d->config_fd == -1;
}
void xen_host_pci_device_put(XenHostPCIDevice *d)
diff --git a/qemu/hw/xen/xen-host-pci-device.h b/qemu/hw/xen/xen-host-pci-device.h
index c2486f0c1..6acf36e13 100644
--- a/qemu/hw/xen/xen-host-pci-device.h
+++ b/qemu/hw/xen/xen-host-pci-device.h
@@ -25,6 +25,7 @@ typedef struct XenHostPCIDevice {
uint16_t vendor_id;
uint16_t device_id;
+ uint32_t class_code;
int irq;
XenHostPCIIORegion io_regions[PCI_NUM_REGIONS - 1];
@@ -35,9 +36,11 @@ typedef struct XenHostPCIDevice {
int config_fd;
} XenHostPCIDevice;
-int xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain,
- uint8_t bus, uint8_t dev, uint8_t func);
+void xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain,
+ uint8_t bus, uint8_t dev, uint8_t func,
+ Error **errp);
void xen_host_pci_device_put(XenHostPCIDevice *pci_dev);
+bool xen_host_pci_device_closed(XenHostPCIDevice *d);
int xen_host_pci_get_byte(XenHostPCIDevice *d, int pos, uint8_t *p);
int xen_host_pci_get_word(XenHostPCIDevice *d, int pos, uint16_t *p);
diff --git a/qemu/hw/xen/xen_backend.c b/qemu/hw/xen/xen_backend.c
index 2510e2e4f..60575ad38 100644
--- a/qemu/hw/xen/xen_backend.c
+++ b/qemu/hw/xen/xen_backend.c
@@ -22,15 +22,7 @@
* TODO: add some xenbus / xenstore concepts overview here.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <sys/types.h>
-#include <sys/stat.h>
+#include "qemu/osdep.h"
#include <sys/mman.h>
#include <sys/signal.h>
@@ -44,7 +36,8 @@
/* ------------------------------------------------------------- */
/* public */
-XenXC xen_xc = XC_HANDLER_INITIAL_VALUE;
+xc_interface *xen_xc = NULL;
+xenforeignmemory_handle *xen_fmem = NULL;
struct xs_handle *xenstore = NULL;
const char *xen_protocol;
@@ -243,24 +236,24 @@ static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev,
xendev->debug = debug;
xendev->local_port = -1;
- xendev->evtchndev = xen_xc_evtchn_open(NULL, 0);
- if (xendev->evtchndev == XC_HANDLER_INITIAL_VALUE) {
+ xendev->evtchndev = xenevtchn_open(NULL, 0);
+ if (xendev->evtchndev == NULL) {
xen_be_printf(NULL, 0, "can't open evtchn device\n");
g_free(xendev);
return NULL;
}
- fcntl(xc_evtchn_fd(xendev->evtchndev), F_SETFD, FD_CLOEXEC);
+ fcntl(xenevtchn_fd(xendev->evtchndev), F_SETFD, FD_CLOEXEC);
if (ops->flags & DEVOPS_FLAG_NEED_GNTDEV) {
- xendev->gnttabdev = xen_xc_gnttab_open(NULL, 0);
- if (xendev->gnttabdev == XC_HANDLER_INITIAL_VALUE) {
+ xendev->gnttabdev = xengnttab_open(NULL, 0);
+ if (xendev->gnttabdev == NULL) {
xen_be_printf(NULL, 0, "can't open gnttab device\n");
- xc_evtchn_close(xendev->evtchndev);
+ xenevtchn_close(xendev->evtchndev);
g_free(xendev);
return NULL;
}
} else {
- xendev->gnttabdev = XC_HANDLER_INITIAL_VALUE;
+ xendev->gnttabdev = NULL;
}
QTAILQ_INSERT_TAIL(&xendevs, xendev, next);
@@ -306,11 +299,11 @@ static struct XenDevice *xen_be_del_xendev(int dom, int dev)
g_free(xendev->fe);
}
- if (xendev->evtchndev != XC_HANDLER_INITIAL_VALUE) {
- xc_evtchn_close(xendev->evtchndev);
+ if (xendev->evtchndev != NULL) {
+ xenevtchn_close(xendev->evtchndev);
}
- if (xendev->gnttabdev != XC_HANDLER_INITIAL_VALUE) {
- xc_gnttab_close(xendev->gnttabdev);
+ if (xendev->gnttabdev != NULL) {
+ xengnttab_close(xendev->gnttabdev);
}
QTAILQ_REMOVE(&xendevs, xendev, next);
@@ -691,13 +684,14 @@ static void xen_be_evtchn_event(void *opaque)
struct XenDevice *xendev = opaque;
evtchn_port_t port;
- port = xc_evtchn_pending(xendev->evtchndev);
+ port = xenevtchn_pending(xendev->evtchndev);
if (port != xendev->local_port) {
- xen_be_printf(xendev, 0, "xc_evtchn_pending returned %d (expected %d)\n",
+ xen_be_printf(xendev, 0,
+ "xenevtchn_pending returned %d (expected %d)\n",
port, xendev->local_port);
return;
}
- xc_evtchn_unmask(xendev->evtchndev, port);
+ xenevtchn_unmask(xendev->evtchndev, port);
if (xendev->ops->event) {
xendev->ops->event(xendev);
@@ -716,7 +710,7 @@ int xen_be_init(void)
qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL);
- if (xen_xc == XC_HANDLER_INITIAL_VALUE) {
+ if (xen_xc == NULL || xen_fmem == NULL) {
/* Check if xen_init() have been called */
goto err;
}
@@ -740,14 +734,14 @@ int xen_be_bind_evtchn(struct XenDevice *xendev)
if (xendev->local_port != -1) {
return 0;
}
- xendev->local_port = xc_evtchn_bind_interdomain
+ xendev->local_port = xenevtchn_bind_interdomain
(xendev->evtchndev, xendev->dom, xendev->remote_port);
if (xendev->local_port == -1) {
- xen_be_printf(xendev, 0, "xc_evtchn_bind_interdomain failed\n");
+ xen_be_printf(xendev, 0, "xenevtchn_bind_interdomain failed\n");
return -1;
}
xen_be_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port);
- qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev),
+ qemu_set_fd_handler(xenevtchn_fd(xendev->evtchndev),
xen_be_evtchn_event, NULL, xendev);
return 0;
}
@@ -757,15 +751,15 @@ void xen_be_unbind_evtchn(struct XenDevice *xendev)
if (xendev->local_port == -1) {
return;
}
- qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), NULL, NULL, NULL);
- xc_evtchn_unbind(xendev->evtchndev, xendev->local_port);
+ qemu_set_fd_handler(xenevtchn_fd(xendev->evtchndev), NULL, NULL, NULL);
+ xenevtchn_unbind(xendev->evtchndev, xendev->local_port);
xen_be_printf(xendev, 2, "unbind evtchn port %d\n", xendev->local_port);
xendev->local_port = -1;
}
int xen_be_send_notify(struct XenDevice *xendev)
{
- return xc_evtchn_notify(xendev->evtchndev, xendev->local_port);
+ return xenevtchn_notify(xendev->evtchndev, xendev->local_port);
}
/*
diff --git a/qemu/hw/xen/xen_devconfig.c b/qemu/hw/xen/xen_devconfig.c
index e138dbbec..1f30fe4f5 100644
--- a/qemu/hw/xen/xen_devconfig.c
+++ b/qemu/hw/xen/xen_devconfig.c
@@ -1,3 +1,4 @@
+#include "qemu/osdep.h"
#include "hw/xen/xen_backend.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
diff --git a/qemu/hw/xen/xen_pt.c b/qemu/hw/xen/xen_pt.c
index ed5fcaec0..f593b046e 100644
--- a/qemu/hw/xen/xen_pt.c
+++ b/qemu/hw/xen/xen_pt.c
@@ -52,10 +52,13 @@
* - Set entry->pirq to '-1'.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include <sys/ioctl.h>
#include "hw/pci/pci.h"
#include "hw/xen/xen.h"
+#include "hw/i386/pc.h"
#include "hw/xen/xen_backend.h"
#include "xen_pt.h"
#include "qemu/range.h"
@@ -378,7 +381,7 @@ static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr,
}
}
- /* need to shift back before passing them to xen_host_pci_device */
+ /* need to shift back before passing them to xen_host_pci_set_block. */
val >>= (addr & 3) << 3;
memory_region_transaction_commit();
@@ -406,7 +409,7 @@ out:
(uint8_t *)&val + index, len);
if (rc < 0) {
- XEN_PT_ERR(d, "pci_write_block failed. return value: %d.\n", rc);
+ XEN_PT_ERR(d, "xen_host_pci_set_block failed. return value: %d.\n", rc);
}
}
}
@@ -502,6 +505,7 @@ static int xen_pt_register_regions(XenPCIPassthroughState *s, uint16_t *cmd)
d->rom.size, d->rom.base_addr);
}
+ xen_pt_register_vga_regions(d);
return 0;
}
@@ -683,15 +687,89 @@ static const MemoryListener xen_pt_io_listener = {
.priority = 10,
};
+static void
+xen_igd_passthrough_isa_bridge_create(XenPCIPassthroughState *s,
+ XenHostPCIDevice *dev)
+{
+ uint16_t gpu_dev_id;
+ PCIDevice *d = &s->dev;
+
+ gpu_dev_id = dev->device_id;
+ igd_passthrough_isa_bridge_create(d->bus, gpu_dev_id);
+}
+
+/* destroy. */
+static void xen_pt_destroy(PCIDevice *d) {
+
+ XenPCIPassthroughState *s = XEN_PT_DEVICE(d);
+ XenHostPCIDevice *host_dev = &s->real_device;
+ uint8_t machine_irq = s->machine_irq;
+ uint8_t intx;
+ int rc;
+
+ if (machine_irq && !xen_host_pci_device_closed(&s->real_device)) {
+ intx = xen_pt_pci_intx(s);
+ rc = xc_domain_unbind_pt_irq(xen_xc, xen_domid, machine_irq,
+ PT_IRQ_TYPE_PCI,
+ pci_bus_num(d->bus),
+ PCI_SLOT(s->dev.devfn),
+ intx,
+ 0 /* isa_irq */);
+ if (rc < 0) {
+ XEN_PT_ERR(d, "unbinding of interrupt INT%c failed."
+ " (machine irq: %i, err: %d)"
+ " But bravely continuing on..\n",
+ 'a' + intx, machine_irq, errno);
+ }
+ }
+
+ /* N.B. xen_pt_config_delete takes care of freeing them. */
+ if (s->msi) {
+ xen_pt_msi_disable(s);
+ }
+ if (s->msix) {
+ xen_pt_msix_disable(s);
+ }
+
+ if (machine_irq) {
+ xen_pt_mapped_machine_irq[machine_irq]--;
+
+ if (xen_pt_mapped_machine_irq[machine_irq] == 0) {
+ rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq);
+
+ if (rc < 0) {
+ XEN_PT_ERR(d, "unmapping of interrupt %i failed. (err: %d)"
+ " But bravely continuing on..\n",
+ machine_irq, errno);
+ }
+ }
+ s->machine_irq = 0;
+ }
+
+ /* delete all emulated config registers */
+ xen_pt_config_delete(s);
+
+ xen_pt_unregister_vga_regions(host_dev);
+
+ if (s->listener_set) {
+ memory_listener_unregister(&s->memory_listener);
+ memory_listener_unregister(&s->io_listener);
+ s->listener_set = false;
+ }
+ if (!xen_host_pci_device_closed(&s->real_device)) {
+ xen_host_pci_device_put(&s->real_device);
+ }
+}
/* init */
-static int xen_pt_initfn(PCIDevice *d)
+static void xen_pt_realize(PCIDevice *d, Error **errp)
{
XenPCIPassthroughState *s = XEN_PT_DEVICE(d);
- int rc = 0;
- uint8_t machine_irq = 0;
+ int i, rc = 0;
+ uint8_t machine_irq = 0, scratch;
uint16_t cmd = 0;
int pirq = XEN_PT_UNASSIGNED_PIRQ;
+ Error *err = NULL;
/* register real device */
XEN_PT_LOG(d, "Assigning real physical device %02x:%02x.%d"
@@ -699,12 +777,14 @@ static int xen_pt_initfn(PCIDevice *d)
s->hostaddr.bus, s->hostaddr.slot, s->hostaddr.function,
s->dev.devfn);
- rc = xen_host_pci_device_get(&s->real_device,
- s->hostaddr.domain, s->hostaddr.bus,
- s->hostaddr.slot, s->hostaddr.function);
- if (rc) {
- XEN_PT_ERR(d, "Failed to \"open\" the real pci device. rc: %i\n", rc);
- return -1;
+ xen_host_pci_device_get(&s->real_device,
+ s->hostaddr.domain, s->hostaddr.bus,
+ s->hostaddr.slot, s->hostaddr.function,
+ &err);
+ if (err) {
+ error_append_hint(&err, "Failed to \"open\" the real pci device");
+ error_propagate(errp, err);
+ return;
}
s->is_virtfn = s->real_device.is_virtfn;
@@ -715,37 +795,62 @@ static int xen_pt_initfn(PCIDevice *d)
}
/* Initialize virtualized PCI configuration (Extended 256 Bytes) */
- if (xen_host_pci_get_block(&s->real_device, 0, d->config,
- PCI_CONFIG_SPACE_SIZE) == -1) {
- xen_host_pci_device_put(&s->real_device);
- return -1;
- }
+ memset(d->config, 0, PCI_CONFIG_SPACE_SIZE);
s->memory_listener = xen_pt_memory_listener;
s->io_listener = xen_pt_io_listener;
+ /* Setup VGA bios for passthrough GFX */
+ if ((s->real_device.domain == 0) && (s->real_device.bus == 0) &&
+ (s->real_device.dev == 2) && (s->real_device.func == 0)) {
+ if (!is_igd_vga_passthrough(&s->real_device)) {
+ error_setg(errp, "Need to enable igd-passthru if you're trying"
+ " to passthrough IGD GFX");
+ xen_host_pci_device_put(&s->real_device);
+ return;
+ }
+
+ xen_pt_setup_vga(s, &s->real_device, &err);
+ if (err) {
+ error_append_hint(&err, "Setup VGA BIOS of passthrough"
+ " GFX failed");
+ error_propagate(errp, err);
+ xen_host_pci_device_put(&s->real_device);
+ return;
+ }
+
+ /* Register ISA bridge for passthrough GFX. */
+ xen_igd_passthrough_isa_bridge_create(s, &s->real_device);
+ }
+
/* Handle real device's MMIO/PIO BARs */
xen_pt_register_regions(s, &cmd);
/* reinitialize each config register to be emulated */
- if (xen_pt_config_init(s)) {
- XEN_PT_ERR(d, "PCI Config space initialisation failed.\n");
- xen_host_pci_device_put(&s->real_device);
- return -1;
+ xen_pt_config_init(s, &err);
+ if (err) {
+ error_append_hint(&err, "PCI Config space initialisation failed");
+ error_report_err(err);
+ rc = -1;
+ goto err_out;
}
/* Bind interrupt */
- if (!s->dev.config[PCI_INTERRUPT_PIN]) {
- XEN_PT_LOG(d, "no pin interrupt\n");
+ rc = xen_host_pci_get_byte(&s->real_device, PCI_INTERRUPT_PIN, &scratch);
+ if (rc) {
+ error_setg_errno(errp, errno, "Failed to read PCI_INTERRUPT_PIN");
+ goto err_out;
+ }
+ if (!scratch) {
+ error_setg(errp, "no pin interrupt");
goto out;
}
machine_irq = s->real_device.irq;
rc = xc_physdev_map_pirq(xen_xc, xen_domid, machine_irq, &pirq);
-
if (rc < 0) {
- XEN_PT_ERR(d, "Mapping machine irq %u to pirq %i failed, (err: %d)\n",
- machine_irq, pirq, errno);
+ error_setg_errno(errp, errno, "Mapping machine irq %u to"
+ " pirq %i failed", machine_irq, pirq);
/* Disable PCI intx assertion (turn on bit10 of devctl) */
cmd |= PCI_COMMAND_INTX_DISABLE;
@@ -766,8 +871,8 @@ static int xen_pt_initfn(PCIDevice *d)
PCI_SLOT(d->devfn),
e_intx);
if (rc < 0) {
- XEN_PT_ERR(d, "Binding of interrupt %i failed! (err: %d)\n",
- e_intx, errno);
+ error_setg_errno(errp, errno, "Binding of interrupt %u failed",
+ e_intx);
/* Disable PCI intx assertion (turn on bit10 of devctl) */
cmd |= PCI_COMMAND_INTX_DISABLE;
@@ -775,8 +880,8 @@ static int xen_pt_initfn(PCIDevice *d)
if (xen_pt_mapped_machine_irq[machine_irq] == 0) {
if (xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq)) {
- XEN_PT_ERR(d, "Unmapping of machine interrupt %i failed!"
- " (err: %d)\n", machine_irq, errno);
+ error_setg_errno(errp, errno, "Unmapping of machine"
+ " interrupt %u failed", machine_irq);
}
}
s->machine_irq = 0;
@@ -785,69 +890,45 @@ static int xen_pt_initfn(PCIDevice *d)
out:
if (cmd) {
- xen_host_pci_set_word(&s->real_device, PCI_COMMAND,
- pci_get_word(d->config + PCI_COMMAND) | cmd);
+ uint16_t val;
+
+ rc = xen_host_pci_get_word(&s->real_device, PCI_COMMAND, &val);
+ if (rc) {
+ error_setg_errno(errp, errno, "Failed to read PCI_COMMAND");
+ goto err_out;
+ } else {
+ val |= cmd;
+ rc = xen_host_pci_set_word(&s->real_device, PCI_COMMAND, val);
+ if (rc) {
+ error_setg_errno(errp, errno, "Failed to write PCI_COMMAND"
+ " val = 0x%x", val);
+ goto err_out;
+ }
+ }
}
memory_listener_register(&s->memory_listener, &s->dev.bus_master_as);
memory_listener_register(&s->io_listener, &address_space_io);
+ s->listener_set = true;
XEN_PT_LOG(d,
- "Real physical device %02x:%02x.%d registered successfully!\n",
+ "Real physical device %02x:%02x.%d registered successfully\n",
s->hostaddr.bus, s->hostaddr.slot, s->hostaddr.function);
- return 0;
-}
-
-static void xen_pt_unregister_device(PCIDevice *d)
-{
- XenPCIPassthroughState *s = XEN_PT_DEVICE(d);
- uint8_t machine_irq = s->machine_irq;
- uint8_t intx = xen_pt_pci_intx(s);
- int rc;
-
- if (machine_irq) {
- rc = xc_domain_unbind_pt_irq(xen_xc, xen_domid, machine_irq,
- PT_IRQ_TYPE_PCI,
- pci_bus_num(d->bus),
- PCI_SLOT(s->dev.devfn),
- intx,
- 0 /* isa_irq */);
- if (rc < 0) {
- XEN_PT_ERR(d, "unbinding of interrupt INT%c failed."
- " (machine irq: %i, err: %d)"
- " But bravely continuing on..\n",
- 'a' + intx, machine_irq, errno);
- }
- }
-
- if (s->msi) {
- xen_pt_msi_disable(s);
- }
- if (s->msix) {
- xen_pt_msix_disable(s);
- }
-
- if (machine_irq) {
- xen_pt_mapped_machine_irq[machine_irq]--;
+ return;
- if (xen_pt_mapped_machine_irq[machine_irq] == 0) {
- rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq);
-
- if (rc < 0) {
- XEN_PT_ERR(d, "unmapping of interrupt %i failed. (err: %d)"
- " But bravely continuing on..\n",
- machine_irq, errno);
- }
- }
+err_out:
+ for (i = 0; i < PCI_ROM_SLOT; i++) {
+ object_unparent(OBJECT(&s->bar[i]));
}
+ object_unparent(OBJECT(&s->rom));
- /* delete all emulated config registers */
- xen_pt_config_delete(s);
-
- memory_listener_unregister(&s->memory_listener);
- memory_listener_unregister(&s->io_listener);
+ xen_pt_destroy(d);
+ assert(rc);
+}
- xen_host_pci_device_put(&s->real_device);
+static void xen_pt_unregister_device(PCIDevice *d)
+{
+ xen_pt_destroy(d);
}
static Property xen_pci_passthrough_properties[] = {
@@ -861,7 +942,7 @@ static void xen_pci_passthrough_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
- k->init = xen_pt_initfn;
+ k->realize = xen_pt_realize;
k->exit = xen_pt_unregister_device;
k->config_read = xen_pt_pci_read_config;
k->config_write = xen_pt_pci_write_config;
@@ -870,10 +951,18 @@ static void xen_pci_passthrough_class_init(ObjectClass *klass, void *data)
dc->props = xen_pci_passthrough_properties;
};
+static void xen_pci_passthrough_finalize(Object *obj)
+{
+ XenPCIPassthroughState *s = XEN_PT_DEVICE(obj);
+
+ xen_pt_msix_delete(s);
+}
+
static const TypeInfo xen_pci_passthrough_info = {
.name = TYPE_XEN_PT_DEVICE,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(XenPCIPassthroughState),
+ .instance_finalize = xen_pci_passthrough_finalize,
.class_init = xen_pci_passthrough_class_init,
};
diff --git a/qemu/hw/xen/xen_pt.h b/qemu/hw/xen/xen_pt.h
index 393f36ccb..c2f8e1fc2 100644
--- a/qemu/hw/xen/xen_pt.h
+++ b/qemu/hw/xen/xen_pt.h
@@ -40,6 +40,9 @@ typedef struct XenPCIPassthroughState XenPCIPassthroughState;
#define XEN_PT_DEVICE(obj) \
OBJECT_CHECK(XenPCIPassthroughState, (obj), TYPE_XEN_PT_DEVICE)
+uint32_t igd_read_opregion(XenPCIPassthroughState *s);
+void igd_write_opregion(XenPCIPassthroughState *s, uint32_t val);
+
/* function type for config reg */
typedef int (*xen_pt_conf_reg_init)
(XenPCIPassthroughState *, XenPTRegInfo *, uint32_t real_offset,
@@ -66,8 +69,9 @@ typedef int (*xen_pt_conf_byte_read)
#define XEN_PT_BAR_ALLF 0xFFFFFFFF
#define XEN_PT_BAR_UNMAPPED (-1)
-#define PCI_CAP_MAX 48
+#define XEN_PCI_CAP_MAX 48
+#define XEN_PCI_INTEL_OPREGION 0xfc
typedef enum {
XEN_PT_GRP_TYPE_HARDWIRED = 0, /* 0 Hardwired reg group */
@@ -109,6 +113,8 @@ struct XenPTRegInfo {
uint32_t res_mask;
/* reg read only field mask (ON:RO/ROS, OFF:other) */
uint32_t ro_mask;
+ /* reg read/write-1-clear field mask (ON:RW1C/RW1CS, OFF:other) */
+ uint32_t rw1c_mask;
/* reg emulate field mask (ON:emu, OFF:passthrough) */
uint32_t emu_mask;
xen_pt_conf_reg_init init;
@@ -134,7 +140,11 @@ struct XenPTRegInfo {
struct XenPTReg {
QLIST_ENTRY(XenPTReg) entries;
XenPTRegInfo *reg;
- uint32_t data; /* emulated value */
+ union {
+ uint8_t *byte;
+ uint16_t *half_word;
+ uint32_t *word;
+ } ptr; /* pointer to dev.config. */
};
typedef const struct XenPTRegGroupInfo XenPTRegGroupInfo;
@@ -179,13 +189,13 @@ typedef struct XenPTMSIXEntry {
int pirq;
uint64_t addr;
uint32_t data;
- uint32_t vector_ctrl;
+ uint32_t latch[4];
bool updated; /* indicate whether MSI ADDR or DATA is updated */
- bool warned; /* avoid issuing (bogus) warning more than once */
} XenPTMSIXEntry;
typedef struct XenPTMSIX {
uint32_t ctrl_offset;
bool enabled;
+ bool maskall;
int total_entries;
int bar_index;
uint64_t table_base;
@@ -217,9 +227,10 @@ struct XenPCIPassthroughState {
MemoryListener memory_listener;
MemoryListener io_listener;
+ bool listener_set;
};
-int xen_pt_config_init(XenPCIPassthroughState *s);
+void xen_pt_config_init(XenPCIPassthroughState *s, Error **errp);
void xen_pt_config_delete(XenPCIPassthroughState *s);
XenPTRegGroup *xen_pt_find_reg_grp(XenPCIPassthroughState *s, uint32_t address);
XenPTReg *xen_pt_find_reg(XenPTRegGroup *reg_grp, uint32_t address);
@@ -282,6 +293,7 @@ static inline uint8_t xen_pt_pci_intx(XenPCIPassthroughState *s)
" value=%i, acceptable range is 1 - 4\n", r_val);
r_val = 0;
} else {
+ /* Note that if s.real_device.config_fd is closed we make 0xff. */
r_val -= 1;
}
@@ -289,13 +301,13 @@ static inline uint8_t xen_pt_pci_intx(XenPCIPassthroughState *s)
}
/* MSI/MSI-X */
-int xen_pt_msi_set_enable(XenPCIPassthroughState *s, bool en);
int xen_pt_msi_setup(XenPCIPassthroughState *s);
int xen_pt_msi_update(XenPCIPassthroughState *d);
void xen_pt_msi_disable(XenPCIPassthroughState *s);
int xen_pt_msix_init(XenPCIPassthroughState *s, uint32_t base);
void xen_pt_msix_delete(XenPCIPassthroughState *s);
+void xen_pt_msix_unmap(XenPCIPassthroughState *s);
int xen_pt_msix_update(XenPCIPassthroughState *s);
int xen_pt_msix_update_remap(XenPCIPassthroughState *s, int bar_index);
void xen_pt_msix_disable(XenPCIPassthroughState *s);
@@ -305,5 +317,19 @@ static inline bool xen_pt_has_msix_mapping(XenPCIPassthroughState *s, int bar)
return s->msix && s->msix->bar_index == bar;
}
-
+extern void *pci_assign_dev_load_option_rom(PCIDevice *dev,
+ struct Object *owner, int *size,
+ unsigned int domain,
+ unsigned int bus, unsigned int slot,
+ unsigned int function);
+extern bool has_igd_gfx_passthru;
+static inline bool is_igd_vga_passthrough(XenHostPCIDevice *dev)
+{
+ return (has_igd_gfx_passthru
+ && ((dev->class_code >> 0x8) == PCI_CLASS_DISPLAY_VGA));
+}
+int xen_pt_register_vga_regions(XenHostPCIDevice *dev);
+int xen_pt_unregister_vga_regions(XenHostPCIDevice *dev);
+void xen_pt_setup_vga(XenPCIPassthroughState *s, XenHostPCIDevice *dev,
+ Error **errp);
#endif /* !XEN_PT_H */
diff --git a/qemu/hw/xen/xen_pt_config_init.c b/qemu/hw/xen/xen_pt_config_init.c
index dd37be38a..9869ffda0 100644
--- a/qemu/hw/xen/xen_pt_config_init.c
+++ b/qemu/hw/xen/xen_pt_config_init.c
@@ -12,6 +12,8 @@
* This file implements direct PCI assignment to a HVM guest
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu/timer.h"
#include "hw/xen/xen_backend.h"
#include "xen_pt.h"
@@ -128,10 +130,11 @@ static int xen_pt_byte_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
{
XenPTRegInfo *reg = cfg_entry->reg;
uint8_t valid_emu_mask = 0;
+ uint8_t *data = cfg_entry->ptr.byte;
/* emulate byte register */
valid_emu_mask = reg->emu_mask & valid_mask;
- *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask);
+ *value = XEN_PT_MERGE_VALUE(*value, *data, ~valid_emu_mask);
return 0;
}
@@ -140,10 +143,11 @@ static int xen_pt_word_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
{
XenPTRegInfo *reg = cfg_entry->reg;
uint16_t valid_emu_mask = 0;
+ uint16_t *data = cfg_entry->ptr.half_word;
/* emulate word register */
valid_emu_mask = reg->emu_mask & valid_mask;
- *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask);
+ *value = XEN_PT_MERGE_VALUE(*value, *data, ~valid_emu_mask);
return 0;
}
@@ -152,10 +156,11 @@ static int xen_pt_long_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
{
XenPTRegInfo *reg = cfg_entry->reg;
uint32_t valid_emu_mask = 0;
+ uint32_t *data = cfg_entry->ptr.word;
/* emulate long register */
valid_emu_mask = reg->emu_mask & valid_mask;
- *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask);
+ *value = XEN_PT_MERGE_VALUE(*value, *data, ~valid_emu_mask);
return 0;
}
@@ -169,13 +174,15 @@ static int xen_pt_byte_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
XenPTRegInfo *reg = cfg_entry->reg;
uint8_t writable_mask = 0;
uint8_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
+ uint8_t *data = cfg_entry->ptr.byte;
/* modify emulate register */
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
- cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
+ *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
/* create value for writing to I/O device register */
- *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
+ *val = XEN_PT_MERGE_VALUE(*val, dev_value & ~reg->rw1c_mask,
+ throughable_mask);
return 0;
}
@@ -186,13 +193,15 @@ static int xen_pt_word_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
XenPTRegInfo *reg = cfg_entry->reg;
uint16_t writable_mask = 0;
uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
+ uint16_t *data = cfg_entry->ptr.half_word;
/* modify emulate register */
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
- cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
+ *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
/* create value for writing to I/O device register */
- *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
+ *val = XEN_PT_MERGE_VALUE(*val, dev_value & ~reg->rw1c_mask,
+ throughable_mask);
return 0;
}
@@ -203,13 +212,15 @@ static int xen_pt_long_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
XenPTRegInfo *reg = cfg_entry->reg;
uint32_t writable_mask = 0;
uint32_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
+ uint32_t *data = cfg_entry->ptr.word;
/* modify emulate register */
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
- cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
+ *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
/* create value for writing to I/O device register */
- *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
+ *val = XEN_PT_MERGE_VALUE(*val, dev_value & ~reg->rw1c_mask,
+ throughable_mask);
return 0;
}
@@ -255,7 +266,7 @@ static int xen_pt_status_reg_init(XenPCIPassthroughState *s,
reg_entry = xen_pt_find_reg(reg_grp_entry, PCI_CAPABILITY_LIST);
if (reg_entry) {
/* check Capabilities Pointer register */
- if (reg_entry->data) {
+ if (*reg_entry->ptr.half_word) {
reg_field |= PCI_STATUS_CAP_LIST;
} else {
reg_field &= ~PCI_STATUS_CAP_LIST;
@@ -301,10 +312,11 @@ static int xen_pt_cmd_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
XenPTRegInfo *reg = cfg_entry->reg;
uint16_t writable_mask = 0;
uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
+ uint16_t *data = cfg_entry->ptr.half_word;
/* modify emulate register */
writable_mask = ~reg->ro_mask & valid_mask;
- cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
+ *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
/* create value for writing to I/O device register */
if (*val & PCI_COMMAND_INTX_DISABLE) {
@@ -447,7 +459,7 @@ static int xen_pt_bar_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
/* emulate BAR */
valid_emu_mask = bar_emu_mask & valid_mask;
- *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask);
+ *value = XEN_PT_MERGE_VALUE(*value, *cfg_entry->ptr.word, ~valid_emu_mask);
return 0;
}
@@ -464,6 +476,7 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
uint32_t bar_ro_mask = 0;
uint32_t r_size = 0;
int index = 0;
+ uint32_t *data = cfg_entry->ptr.word;
index = xen_pt_bar_offset_to_index(reg->offset);
if (index < 0 || index >= PCI_NUM_REGIONS) {
@@ -500,7 +513,7 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
/* modify emulate register */
writable_mask = bar_emu_mask & ~bar_ro_mask & valid_mask;
- cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
+ *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
/* check whether we need to update the virtual region address or not */
switch (s->bases[index].bar_flag) {
@@ -533,6 +546,7 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s,
uint32_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
pcibus_t r_size = 0;
uint32_t bar_ro_mask = 0;
+ uint32_t *data = cfg_entry->ptr.word;
r_size = d->io_regions[PCI_ROM_SLOT].size;
base = &s->bases[PCI_ROM_SLOT];
@@ -544,7 +558,7 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s,
/* modify emulate register */
writable_mask = ~bar_ro_mask & valid_mask;
- cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
+ *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
/* create value for writing to I/O device register */
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
@@ -552,6 +566,22 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s,
return 0;
}
+static int xen_pt_intel_opregion_read(XenPCIPassthroughState *s,
+ XenPTReg *cfg_entry,
+ uint32_t *value, uint32_t valid_mask)
+{
+ *value = igd_read_opregion(s);
+ return 0;
+}
+
+static int xen_pt_intel_opregion_write(XenPCIPassthroughState *s,
+ XenPTReg *cfg_entry, uint32_t *value,
+ uint32_t dev_value, uint32_t valid_mask)
+{
+ igd_write_opregion(s, *value);
+ return 0;
+}
+
/* Header Type0 reg static information table */
static XenPTRegInfo xen_pt_emu_reg_header0[] = {
/* Vendor ID reg */
@@ -608,6 +638,7 @@ static XenPTRegInfo xen_pt_emu_reg_header0[] = {
.init_val = 0x0000,
.res_mask = 0x0007,
.ro_mask = 0x06F8,
+ .rw1c_mask = 0xF900,
.emu_mask = 0x0010,
.init = xen_pt_status_reg_init,
.u.w.read = xen_pt_word_reg_read,
@@ -800,15 +831,21 @@ static XenPTRegInfo xen_pt_emu_reg_vendor[] = {
static inline uint8_t get_capability_version(XenPCIPassthroughState *s,
uint32_t offset)
{
- uint8_t flags = pci_get_byte(s->dev.config + offset + PCI_EXP_FLAGS);
- return flags & PCI_EXP_FLAGS_VERS;
+ uint8_t flag;
+ if (xen_host_pci_get_byte(&s->real_device, offset + PCI_EXP_FLAGS, &flag)) {
+ return 0;
+ }
+ return flag & PCI_EXP_FLAGS_VERS;
}
static inline uint8_t get_device_type(XenPCIPassthroughState *s,
uint32_t offset)
{
- uint8_t flags = pci_get_byte(s->dev.config + offset + PCI_EXP_FLAGS);
- return (flags & PCI_EXP_FLAGS_TYPE) >> 4;
+ uint8_t flag;
+ if (xen_host_pci_get_byte(&s->real_device, offset + PCI_EXP_FLAGS, &flag)) {
+ return 0;
+ }
+ return (flag & PCI_EXP_FLAGS_TYPE) >> 4;
}
/* initialize Link Control register */
@@ -857,8 +894,14 @@ static int xen_pt_linkctrl2_reg_init(XenPCIPassthroughState *s,
reg_field = XEN_PT_INVALID_REG;
} else {
/* set Supported Link Speed */
- uint8_t lnkcap = pci_get_byte(s->dev.config + real_offset - reg->offset
- + PCI_EXP_LNKCAP);
+ uint8_t lnkcap;
+ int rc;
+ rc = xen_host_pci_get_byte(&s->real_device,
+ real_offset - reg->offset + PCI_EXP_LNKCAP,
+ &lnkcap);
+ if (rc) {
+ return rc;
+ }
reg_field |= PCI_EXP_LNKCAP_SLS & lnkcap;
}
@@ -907,6 +950,7 @@ static XenPTRegInfo xen_pt_emu_reg_pcie[] = {
.size = 2,
.res_mask = 0xFFC0,
.ro_mask = 0x0030,
+ .rw1c_mask = 0x000F,
.init = xen_pt_common_reg_init,
.u.w.read = xen_pt_word_reg_read,
.u.w.write = xen_pt_word_reg_write,
@@ -927,6 +971,7 @@ static XenPTRegInfo xen_pt_emu_reg_pcie[] = {
.offset = PCI_EXP_LNKSTA,
.size = 2,
.ro_mask = 0x3FFF,
+ .rw1c_mask = 0xC000,
.init = xen_pt_common_reg_init,
.u.w.read = xen_pt_word_reg_read,
.u.w.write = xen_pt_word_reg_write,
@@ -963,26 +1008,6 @@ static XenPTRegInfo xen_pt_emu_reg_pcie[] = {
* Power Management Capability
*/
-/* write Power Management Control/Status register */
-static int xen_pt_pmcsr_reg_write(XenPCIPassthroughState *s,
- XenPTReg *cfg_entry, uint16_t *val,
- uint16_t dev_value, uint16_t valid_mask)
-{
- XenPTRegInfo *reg = cfg_entry->reg;
- uint16_t writable_mask = 0;
- uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
-
- /* modify emulate register */
- writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
- cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
-
- /* create value for writing to I/O device register */
- *val = XEN_PT_MERGE_VALUE(*val, dev_value & ~PCI_PM_CTRL_PME_STATUS,
- throughable_mask);
-
- return 0;
-}
-
/* Power Management Capability reg static information table */
static XenPTRegInfo xen_pt_emu_reg_pm[] = {
/* Next Pointer reg */
@@ -1013,11 +1038,12 @@ static XenPTRegInfo xen_pt_emu_reg_pm[] = {
.size = 2,
.init_val = 0x0008,
.res_mask = 0x00F0,
- .ro_mask = 0xE10C,
+ .ro_mask = 0x610C,
+ .rw1c_mask = 0x8000,
.emu_mask = 0x810B,
.init = xen_pt_common_reg_init,
.u.w.read = xen_pt_word_reg_read,
- .u.w.write = xen_pt_pmcsr_reg_write,
+ .u.w.write = xen_pt_word_reg_write,
},
{
.size = 0,
@@ -1039,13 +1065,15 @@ static int xen_pt_msgctrl_reg_init(XenPCIPassthroughState *s,
XenPTRegInfo *reg, uint32_t real_offset,
uint32_t *data)
{
- PCIDevice *d = &s->dev;
XenPTMSI *msi = s->msi;
- uint16_t reg_field = 0;
+ uint16_t reg_field;
+ int rc;
/* use I/O device register's value as initial value */
- reg_field = pci_get_word(d->config + real_offset);
-
+ rc = xen_host_pci_get_word(&s->real_device, real_offset, &reg_field);
+ if (rc) {
+ return rc;
+ }
if (reg_field & PCI_MSI_FLAGS_ENABLE) {
XEN_PT_LOG(&s->dev, "MSI already enabled, disabling it first\n");
xen_host_pci_set_word(&s->real_device, real_offset,
@@ -1067,6 +1095,7 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s,
XenPTMSI *msi = s->msi;
uint16_t writable_mask = 0;
uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
+ uint16_t *data = cfg_entry->ptr.half_word;
/* Currently no support for multi-vector */
if (*val & PCI_MSI_FLAGS_QSIZE) {
@@ -1075,8 +1104,8 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s,
/* modify emulate register */
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
- cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
- msi->flags |= cfg_entry->data & ~PCI_MSI_FLAGS_ENABLE;
+ *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
+ msi->flags |= *data & ~PCI_MSI_FLAGS_ENABLE;
/* create value for writing to I/O device register */
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
@@ -1086,7 +1115,7 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s,
/* setup MSI pirq for the first time */
if (!msi->initialized) {
/* Init physical one */
- XEN_PT_LOG(&s->dev, "setup MSI\n");
+ XEN_PT_LOG(&s->dev, "setup MSI (register: %x).\n", *val);
if (xen_pt_msi_setup(s)) {
/* We do not broadcast the error to the framework code, so
* that MSI errors are contained in MSI emulation code and
@@ -1094,12 +1123,12 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s,
* Guest MSI would be actually not working.
*/
*val &= ~PCI_MSI_FLAGS_ENABLE;
- XEN_PT_WARN(&s->dev, "Can not map MSI.\n");
+ XEN_PT_WARN(&s->dev, "Can not map MSI (register: %x)!\n", *val);
return 0;
}
if (xen_pt_msi_update(s)) {
*val &= ~PCI_MSI_FLAGS_ENABLE;
- XEN_PT_WARN(&s->dev, "Can not bind MSI\n");
+ XEN_PT_WARN(&s->dev, "Can not bind MSI (register: %x)!\n", *val);
return 0;
}
msi->initialized = true;
@@ -1190,18 +1219,19 @@ static int xen_pt_msgaddr32_reg_write(XenPCIPassthroughState *s,
{
XenPTRegInfo *reg = cfg_entry->reg;
uint32_t writable_mask = 0;
- uint32_t old_addr = cfg_entry->data;
+ uint32_t old_addr = *cfg_entry->ptr.word;
+ uint32_t *data = cfg_entry->ptr.word;
/* modify emulate register */
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
- cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
- s->msi->addr_lo = cfg_entry->data;
+ *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
+ s->msi->addr_lo = *data;
/* create value for writing to I/O device register */
*val = XEN_PT_MERGE_VALUE(*val, dev_value, 0);
/* update MSI */
- if (cfg_entry->data != old_addr) {
+ if (*data != old_addr) {
if (s->msi->mapped) {
xen_pt_msi_update(s);
}
@@ -1216,7 +1246,8 @@ static int xen_pt_msgaddr64_reg_write(XenPCIPassthroughState *s,
{
XenPTRegInfo *reg = cfg_entry->reg;
uint32_t writable_mask = 0;
- uint32_t old_addr = cfg_entry->data;
+ uint32_t old_addr = *cfg_entry->ptr.word;
+ uint32_t *data = cfg_entry->ptr.word;
/* check whether the type is 64 bit or not */
if (!(s->msi->flags & PCI_MSI_FLAGS_64BIT)) {
@@ -1227,15 +1258,15 @@ static int xen_pt_msgaddr64_reg_write(XenPCIPassthroughState *s,
/* modify emulate register */
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
- cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
+ *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
/* update the msi_info too */
- s->msi->addr_hi = cfg_entry->data;
+ s->msi->addr_hi = *data;
/* create value for writing to I/O device register */
*val = XEN_PT_MERGE_VALUE(*val, dev_value, 0);
/* update MSI */
- if (cfg_entry->data != old_addr) {
+ if (*data != old_addr) {
if (s->msi->mapped) {
xen_pt_msi_update(s);
}
@@ -1254,8 +1285,9 @@ static int xen_pt_msgdata_reg_write(XenPCIPassthroughState *s,
XenPTRegInfo *reg = cfg_entry->reg;
XenPTMSI *msi = s->msi;
uint16_t writable_mask = 0;
- uint16_t old_data = cfg_entry->data;
+ uint16_t old_data = *cfg_entry->ptr.half_word;
uint32_t offset = reg->offset;
+ uint16_t *data = cfg_entry->ptr.half_word;
/* check the offset whether matches the type or not */
if (!xen_pt_msi_check_type(offset, msi->flags, DATA)) {
@@ -1266,15 +1298,15 @@ static int xen_pt_msgdata_reg_write(XenPCIPassthroughState *s,
/* modify emulate register */
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
- cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
+ *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
/* update the msi_info too */
- msi->data = cfg_entry->data;
+ msi->data = *data;
/* create value for writing to I/O device register */
*val = XEN_PT_MERGE_VALUE(*val, dev_value, 0);
/* update MSI */
- if (cfg_entry->data != old_data) {
+ if (*data != old_data) {
if (msi->mapped) {
xen_pt_msi_update(s);
}
@@ -1411,14 +1443,16 @@ static int xen_pt_msixctrl_reg_init(XenPCIPassthroughState *s,
XenPTRegInfo *reg, uint32_t real_offset,
uint32_t *data)
{
- PCIDevice *d = &s->dev;
- uint16_t reg_field = 0;
+ uint16_t reg_field;
+ int rc;
/* use I/O device register's value as initial value */
- reg_field = pci_get_word(d->config + real_offset);
-
+ rc = xen_host_pci_get_word(&s->real_device, real_offset, &reg_field);
+ if (rc) {
+ return rc;
+ }
if (reg_field & PCI_MSIX_FLAGS_ENABLE) {
- XEN_PT_LOG(d, "MSIX already enabled, disabling it first\n");
+ XEN_PT_LOG(&s->dev, "MSIX already enabled, disabling it first\n");
xen_host_pci_set_word(&s->real_device, real_offset,
reg_field & ~PCI_MSIX_FLAGS_ENABLE);
}
@@ -1436,10 +1470,11 @@ static int xen_pt_msixctrl_reg_write(XenPCIPassthroughState *s,
uint16_t writable_mask = 0;
uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
int debug_msix_enabled_old;
+ uint16_t *data = cfg_entry->ptr.half_word;
/* modify emulate register */
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
- cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
+ *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
/* create value for writing to I/O device register */
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
@@ -1452,6 +1487,8 @@ static int xen_pt_msixctrl_reg_write(XenPCIPassthroughState *s,
xen_pt_msix_disable(s);
}
+ s->msix->maskall = *val & PCI_MSIX_FLAGS_MASKALL;
+
debug_msix_enabled_old = s->msix->enabled;
s->msix->enabled = !!(*val & PCI_MSIX_FLAGS_ENABLE);
if (s->msix->enabled != debug_msix_enabled_old) {
@@ -1492,6 +1529,19 @@ static XenPTRegInfo xen_pt_emu_reg_msix[] = {
},
};
+static XenPTRegInfo xen_pt_emu_reg_igd_opregion[] = {
+ /* Intel IGFX OpRegion reg */
+ {
+ .offset = 0x0,
+ .size = 4,
+ .init_val = 0,
+ .u.dw.read = xen_pt_intel_opregion_read,
+ .u.dw.write = xen_pt_intel_opregion_write,
+ },
+ {
+ .size = 0,
+ },
+};
/****************************
* Capabilities
@@ -1511,8 +1561,7 @@ static int xen_pt_vendor_size_init(XenPCIPassthroughState *s,
const XenPTRegGroupInfo *grp_reg,
uint32_t base_offset, uint8_t *size)
{
- *size = pci_get_byte(s->dev.config + base_offset + 0x02);
- return 0;
+ return xen_host_pci_get_byte(&s->real_device, base_offset + 0x02, size);
}
/* get PCI Express Capability Structure register group size */
static int xen_pt_pcie_size_init(XenPCIPassthroughState *s,
@@ -1591,12 +1640,15 @@ static int xen_pt_msi_size_init(XenPCIPassthroughState *s,
const XenPTRegGroupInfo *grp_reg,
uint32_t base_offset, uint8_t *size)
{
- PCIDevice *d = &s->dev;
uint16_t msg_ctrl = 0;
uint8_t msi_size = 0xa;
+ int rc;
- msg_ctrl = pci_get_word(d->config + (base_offset + PCI_MSI_FLAGS));
-
+ rc = xen_host_pci_get_word(&s->real_device, base_offset + PCI_MSI_FLAGS,
+ &msg_ctrl);
+ if (rc) {
+ return rc;
+ }
/* check if 64-bit address is capable of per-vector masking */
if (msg_ctrl & PCI_MSI_FLAGS_64BIT) {
msi_size += 4;
@@ -1729,6 +1781,14 @@ static const XenPTRegGroupInfo xen_pt_emu_reg_grps[] = {
.size_init = xen_pt_msix_size_init,
.emu_regs = xen_pt_emu_reg_msix,
},
+ /* Intel IGD Opregion group */
+ {
+ .grp_id = XEN_PCI_INTEL_OPREGION,
+ .grp_type = XEN_PT_GRP_TYPE_EMU,
+ .grp_size = 0x4,
+ .size_init = xen_pt_reg_grp_size_init,
+ .emu_regs = xen_pt_emu_reg_igd_opregion,
+ },
{
.grp_size = 0,
},
@@ -1739,11 +1799,14 @@ static int xen_pt_ptr_reg_init(XenPCIPassthroughState *s,
XenPTRegInfo *reg, uint32_t real_offset,
uint32_t *data)
{
- int i;
- uint8_t *config = s->dev.config;
- uint32_t reg_field = pci_get_byte(config + real_offset);
+ int i, rc;
+ uint8_t reg_field;
uint8_t cap_id = 0;
+ rc = xen_host_pci_get_byte(&s->real_device, real_offset, &reg_field);
+ if (rc) {
+ return rc;
+ }
/* find capability offset */
while (reg_field) {
for (i = 0; xen_pt_emu_reg_grps[i].grp_size != 0; i++) {
@@ -1752,7 +1815,13 @@ static int xen_pt_ptr_reg_init(XenPCIPassthroughState *s,
continue;
}
- cap_id = pci_get_byte(config + reg_field + PCI_CAP_LIST_ID);
+ rc = xen_host_pci_get_byte(&s->real_device,
+ reg_field + PCI_CAP_LIST_ID, &cap_id);
+ if (rc) {
+ XEN_PT_ERR(&s->dev, "Failed to read capability @0x%x (rc:%d)\n",
+ reg_field + PCI_CAP_LIST_ID, rc);
+ return rc;
+ }
if (xen_pt_emu_reg_grps[i].grp_id == cap_id) {
if (xen_pt_emu_reg_grps[i].grp_type == XEN_PT_GRP_TYPE_EMU) {
goto out;
@@ -1763,7 +1832,11 @@ static int xen_pt_ptr_reg_init(XenPCIPassthroughState *s,
}
/* next capability */
- reg_field = pci_get_byte(config + reg_field + PCI_CAP_LIST_NEXT);
+ rc = xen_host_pci_get_byte(&s->real_device,
+ reg_field + PCI_CAP_LIST_NEXT, &reg_field);
+ if (rc) {
+ return rc;
+ }
}
out:
@@ -1779,7 +1852,7 @@ out:
static uint8_t find_cap_offset(XenPCIPassthroughState *s, uint8_t cap)
{
uint8_t id;
- unsigned max_cap = PCI_CAP_MAX;
+ unsigned max_cap = XEN_PCI_CAP_MAX;
uint8_t pos = PCI_CAPABILITY_LIST;
uint8_t status = 0;
@@ -1816,8 +1889,9 @@ static uint8_t find_cap_offset(XenPCIPassthroughState *s, uint8_t cap)
return 0;
}
-static int xen_pt_config_reg_init(XenPCIPassthroughState *s,
- XenPTRegGroup *reg_grp, XenPTRegInfo *reg)
+static void xen_pt_config_reg_init(XenPCIPassthroughState *s,
+ XenPTRegGroup *reg_grp, XenPTRegInfo *reg,
+ Error **errp)
{
XenPTReg *reg_entry;
uint32_t data = 0;
@@ -1827,30 +1901,94 @@ static int xen_pt_config_reg_init(XenPCIPassthroughState *s,
reg_entry->reg = reg;
if (reg->init) {
+ uint32_t host_mask, size_mask;
+ unsigned int offset;
+ uint32_t val;
+
/* initialize emulate register */
rc = reg->init(s, reg_entry->reg,
reg_grp->base_offset + reg->offset, &data);
if (rc < 0) {
g_free(reg_entry);
- return rc;
+ error_setg(errp, "Init emulate register fail");
+ return;
}
if (data == XEN_PT_INVALID_REG) {
/* free unused BAR register entry */
g_free(reg_entry);
- return 0;
+ return;
+ }
+ /* Sync up the data to dev.config */
+ offset = reg_grp->base_offset + reg->offset;
+ size_mask = 0xFFFFFFFF >> ((4 - reg->size) << 3);
+
+ switch (reg->size) {
+ case 1: rc = xen_host_pci_get_byte(&s->real_device, offset, (uint8_t *)&val);
+ break;
+ case 2: rc = xen_host_pci_get_word(&s->real_device, offset, (uint16_t *)&val);
+ break;
+ case 4: rc = xen_host_pci_get_long(&s->real_device, offset, &val);
+ break;
+ default: abort();
+ }
+ if (rc) {
+ /* Serious issues when we cannot read the host values! */
+ g_free(reg_entry);
+ error_setg(errp, "Cannot read host values");
+ return;
+ }
+ /* Set bits in emu_mask are the ones we emulate. The dev.config shall
+ * contain the emulated view of the guest - therefore we flip the mask
+ * to mask out the host values (which dev.config initially has) . */
+ host_mask = size_mask & ~reg->emu_mask;
+
+ if ((data & host_mask) != (val & host_mask)) {
+ uint32_t new_val;
+
+ /* Mask out host (including past size). */
+ new_val = val & host_mask;
+ /* Merge emulated ones (excluding the non-emulated ones). */
+ new_val |= data & host_mask;
+ /* Leave intact host and emulated values past the size - even though
+ * we do not care as we write per reg->size granularity, but for the
+ * logging below lets have the proper value. */
+ new_val |= ((val | data)) & ~size_mask;
+ XEN_PT_LOG(&s->dev,"Offset 0x%04x mismatch! Emulated=0x%04x, host=0x%04x, syncing to 0x%04x.\n",
+ offset, data, val, new_val);
+ val = new_val;
+ } else
+ val = data;
+
+ if (val & ~size_mask) {
+ error_setg(errp, "Offset 0x%04x:0x%04x expands past"
+ " register size (%d)", offset, val, reg->size);
+ g_free(reg_entry);
+ return;
+ }
+ /* This could be just pci_set_long as we don't modify the bits
+ * past reg->size, but in case this routine is run in parallel or the
+ * init value is larger, we do not want to over-write registers. */
+ switch (reg->size) {
+ case 1: pci_set_byte(s->dev.config + offset, (uint8_t)val);
+ break;
+ case 2: pci_set_word(s->dev.config + offset, (uint16_t)val);
+ break;
+ case 4: pci_set_long(s->dev.config + offset, val);
+ break;
+ default: abort();
}
- /* set register value */
- reg_entry->data = data;
+ /* set register value pointer to the data. */
+ reg_entry->ptr.byte = s->dev.config + offset;
+
}
/* list add register entry */
QLIST_INSERT_HEAD(&reg_grp->reg_tbl_list, reg_entry, entries);
-
- return 0;
}
-int xen_pt_config_init(XenPCIPassthroughState *s)
+void xen_pt_config_init(XenPCIPassthroughState *s, Error **errp)
{
int i, rc;
+ Error *err = NULL;
QLIST_INIT(&s->reg_grps);
@@ -1858,7 +1996,8 @@ int xen_pt_config_init(XenPCIPassthroughState *s)
uint32_t reg_grp_offset = 0;
XenPTRegGroup *reg_grp_entry = NULL;
- if (xen_pt_emu_reg_grps[i].grp_id != 0xFF) {
+ if (xen_pt_emu_reg_grps[i].grp_id != 0xFF
+ && xen_pt_emu_reg_grps[i].grp_id != XEN_PCI_INTEL_OPREGION) {
if (xen_pt_hide_dev_cap(&s->real_device,
xen_pt_emu_reg_grps[i].grp_id)) {
continue;
@@ -1871,6 +2010,15 @@ int xen_pt_config_init(XenPCIPassthroughState *s)
}
}
+ /*
+ * By default we will trap up to 0x40 in the cfg space.
+ * If an intel device is pass through we need to trap 0xfc,
+ * therefore the size should be 0xff.
+ */
+ if (xen_pt_emu_reg_grps[i].grp_id == XEN_PCI_INTEL_OPREGION) {
+ reg_grp_offset = XEN_PCI_INTEL_OPREGION;
+ }
+
reg_grp_entry = g_new0(XenPTRegGroup, 1);
QLIST_INIT(&reg_grp_entry->reg_tbl_list);
QLIST_INSERT_HEAD(&s->reg_grps, reg_grp_entry, entries);
@@ -1883,8 +2031,12 @@ int xen_pt_config_init(XenPCIPassthroughState *s)
reg_grp_offset,
&reg_grp_entry->size);
if (rc < 0) {
+ error_setg(&err, "Failed to initialize %d/%zu, type = 0x%x,"
+ " rc: %d", i, ARRAY_SIZE(xen_pt_emu_reg_grps),
+ xen_pt_emu_reg_grps[i].grp_type, rc);
+ error_propagate(errp, err);
xen_pt_config_delete(s);
- return rc;
+ return;
}
}
@@ -1892,20 +2044,24 @@ int xen_pt_config_init(XenPCIPassthroughState *s)
if (xen_pt_emu_reg_grps[i].emu_regs) {
int j = 0;
XenPTRegInfo *regs = xen_pt_emu_reg_grps[i].emu_regs;
+
/* initialize capability register */
for (j = 0; regs->size != 0; j++, regs++) {
- /* initialize capability register */
- rc = xen_pt_config_reg_init(s, reg_grp_entry, regs);
- if (rc < 0) {
+ xen_pt_config_reg_init(s, reg_grp_entry, regs, &err);
+ if (err) {
+ error_append_hint(&err, "Failed to initialize %d/%zu"
+ " reg 0x%x in grp_type = 0x%x (%d/%zu)",
+ j, ARRAY_SIZE(xen_pt_emu_reg_grps[i].emu_regs),
+ regs->offset, xen_pt_emu_reg_grps[i].grp_type,
+ i, ARRAY_SIZE(xen_pt_emu_reg_grps));
+ error_propagate(errp, err);
xen_pt_config_delete(s);
- return rc;
+ return;
}
}
}
}
}
-
- return 0;
}
/* delete all emulate register */
@@ -1916,11 +2072,9 @@ void xen_pt_config_delete(XenPCIPassthroughState *s)
/* free MSI/MSI-X info table */
if (s->msix) {
- xen_pt_msix_delete(s);
- }
- if (s->msi) {
- g_free(s->msi);
+ xen_pt_msix_unmap(s);
}
+ g_free(s->msi);
/* free all register group entry */
QLIST_FOREACH_SAFE(reg_group, &s->reg_grps, entries, next_grp) {
diff --git a/qemu/hw/xen/xen_pt_graphics.c b/qemu/hw/xen/xen_pt_graphics.c
new file mode 100644
index 000000000..0f4c8d77e
--- /dev/null
+++ b/qemu/hw/xen/xen_pt_graphics.c
@@ -0,0 +1,275 @@
+/*
+ * graphics passthrough
+ */
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "xen_pt.h"
+#include "xen-host-pci-device.h"
+#include "hw/xen/xen_backend.h"
+
+static unsigned long igd_guest_opregion;
+static unsigned long igd_host_opregion;
+
+#define XEN_PCI_INTEL_OPREGION_MASK 0xfff
+
+typedef struct VGARegion {
+ int type; /* Memory or port I/O */
+ uint64_t guest_base_addr;
+ uint64_t machine_base_addr;
+ uint64_t size; /* size of the region */
+ int rc;
+} VGARegion;
+
+#define IORESOURCE_IO 0x00000100
+#define IORESOURCE_MEM 0x00000200
+
+static struct VGARegion vga_args[] = {
+ {
+ .type = IORESOURCE_IO,
+ .guest_base_addr = 0x3B0,
+ .machine_base_addr = 0x3B0,
+ .size = 0xC,
+ .rc = -1,
+ },
+ {
+ .type = IORESOURCE_IO,
+ .guest_base_addr = 0x3C0,
+ .machine_base_addr = 0x3C0,
+ .size = 0x20,
+ .rc = -1,
+ },
+ {
+ .type = IORESOURCE_MEM,
+ .guest_base_addr = 0xa0000 >> XC_PAGE_SHIFT,
+ .machine_base_addr = 0xa0000 >> XC_PAGE_SHIFT,
+ .size = 0x20,
+ .rc = -1,
+ },
+};
+
+/*
+ * register VGA resources for the domain with assigned gfx
+ */
+int xen_pt_register_vga_regions(XenHostPCIDevice *dev)
+{
+ int i = 0;
+
+ if (!is_igd_vga_passthrough(dev)) {
+ return 0;
+ }
+
+ for (i = 0 ; i < ARRAY_SIZE(vga_args); i++) {
+ if (vga_args[i].type == IORESOURCE_IO) {
+ vga_args[i].rc = xc_domain_ioport_mapping(xen_xc, xen_domid,
+ vga_args[i].guest_base_addr,
+ vga_args[i].machine_base_addr,
+ vga_args[i].size, DPCI_ADD_MAPPING);
+ } else {
+ vga_args[i].rc = xc_domain_memory_mapping(xen_xc, xen_domid,
+ vga_args[i].guest_base_addr,
+ vga_args[i].machine_base_addr,
+ vga_args[i].size, DPCI_ADD_MAPPING);
+ }
+
+ if (vga_args[i].rc) {
+ XEN_PT_ERR(NULL, "VGA %s mapping failed! (rc: %i)\n",
+ vga_args[i].type == IORESOURCE_IO ? "ioport" : "memory",
+ vga_args[i].rc);
+ return vga_args[i].rc;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * unregister VGA resources for the domain with assigned gfx
+ */
+int xen_pt_unregister_vga_regions(XenHostPCIDevice *dev)
+{
+ int i = 0;
+ int ret = 0;
+
+ if (!is_igd_vga_passthrough(dev)) {
+ return 0;
+ }
+
+ for (i = 0 ; i < ARRAY_SIZE(vga_args); i++) {
+ if (vga_args[i].type == IORESOURCE_IO) {
+ vga_args[i].rc = xc_domain_ioport_mapping(xen_xc, xen_domid,
+ vga_args[i].guest_base_addr,
+ vga_args[i].machine_base_addr,
+ vga_args[i].size, DPCI_REMOVE_MAPPING);
+ } else {
+ vga_args[i].rc = xc_domain_memory_mapping(xen_xc, xen_domid,
+ vga_args[i].guest_base_addr,
+ vga_args[i].machine_base_addr,
+ vga_args[i].size, DPCI_REMOVE_MAPPING);
+ }
+
+ if (vga_args[i].rc) {
+ XEN_PT_ERR(NULL, "VGA %s unmapping failed! (rc: %i)\n",
+ vga_args[i].type == IORESOURCE_IO ? "ioport" : "memory",
+ vga_args[i].rc);
+ return vga_args[i].rc;
+ }
+ }
+
+ if (igd_guest_opregion) {
+ ret = xc_domain_memory_mapping(xen_xc, xen_domid,
+ (unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT),
+ (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT),
+ 3,
+ DPCI_REMOVE_MAPPING);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void *get_vgabios(XenPCIPassthroughState *s, int *size,
+ XenHostPCIDevice *dev)
+{
+ return pci_assign_dev_load_option_rom(&s->dev, OBJECT(&s->dev), size,
+ dev->domain, dev->bus,
+ dev->dev, dev->func);
+}
+
+/* Refer to Seabios. */
+struct rom_header {
+ uint16_t signature;
+ uint8_t size;
+ uint8_t initVector[4];
+ uint8_t reserved[17];
+ uint16_t pcioffset;
+ uint16_t pnpoffset;
+} __attribute__((packed));
+
+struct pci_data {
+ uint32_t signature;
+ uint16_t vendor;
+ uint16_t device;
+ uint16_t vitaldata;
+ uint16_t dlen;
+ uint8_t drevision;
+ uint8_t class_lo;
+ uint16_t class_hi;
+ uint16_t ilen;
+ uint16_t irevision;
+ uint8_t type;
+ uint8_t indicator;
+ uint16_t reserved;
+} __attribute__((packed));
+
+void xen_pt_setup_vga(XenPCIPassthroughState *s, XenHostPCIDevice *dev,
+ Error **errp)
+{
+ unsigned char *bios = NULL;
+ struct rom_header *rom;
+ int bios_size;
+ char *c = NULL;
+ char checksum = 0;
+ uint32_t len = 0;
+ struct pci_data *pd = NULL;
+
+ if (!is_igd_vga_passthrough(dev)) {
+ error_setg(errp, "Need to enable igd-passthrough");
+ return;
+ }
+
+ bios = get_vgabios(s, &bios_size, dev);
+ if (!bios) {
+ error_setg(errp, "VGA: Can't get VBIOS");
+ return;
+ }
+
+ /* Currently we fixed this address as a primary. */
+ rom = (struct rom_header *)bios;
+ pd = (void *)(bios + (unsigned char)rom->pcioffset);
+
+ /* We may need to fixup Device Identification. */
+ if (pd->device != s->real_device.device_id) {
+ pd->device = s->real_device.device_id;
+
+ len = rom->size * 512;
+ /* Then adjust the bios checksum */
+ for (c = (char *)bios; c < ((char *)bios + len); c++) {
+ checksum += *c;
+ }
+ if (checksum) {
+ bios[len - 1] -= checksum;
+ XEN_PT_LOG(&s->dev, "vga bios checksum is adjusted %x!\n",
+ checksum);
+ }
+ }
+
+ /* Currently we fixed this address as a primary for legacy BIOS. */
+ cpu_physical_memory_rw(0xc0000, bios, bios_size, 1);
+}
+
+uint32_t igd_read_opregion(XenPCIPassthroughState *s)
+{
+ uint32_t val = 0;
+
+ if (!igd_guest_opregion) {
+ return val;
+ }
+
+ val = igd_guest_opregion;
+
+ XEN_PT_LOG(&s->dev, "Read opregion val=%x\n", val);
+ return val;
+}
+
+#define XEN_PCI_INTEL_OPREGION_PAGES 0x3
+#define XEN_PCI_INTEL_OPREGION_ENABLE_ACCESSED 0x1
+void igd_write_opregion(XenPCIPassthroughState *s, uint32_t val)
+{
+ int ret;
+
+ if (igd_guest_opregion) {
+ XEN_PT_LOG(&s->dev, "opregion register already been set, ignoring %x\n",
+ val);
+ return;
+ }
+
+ /* We just work with LE. */
+ xen_host_pci_get_block(&s->real_device, XEN_PCI_INTEL_OPREGION,
+ (uint8_t *)&igd_host_opregion, 4);
+ igd_guest_opregion = (unsigned long)(val & ~XEN_PCI_INTEL_OPREGION_MASK)
+ | (igd_host_opregion & XEN_PCI_INTEL_OPREGION_MASK);
+
+ ret = xc_domain_iomem_permission(xen_xc, xen_domid,
+ (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT),
+ XEN_PCI_INTEL_OPREGION_PAGES,
+ XEN_PCI_INTEL_OPREGION_ENABLE_ACCESSED);
+
+ if (ret) {
+ XEN_PT_ERR(&s->dev, "[%d]:Can't enable to access IGD host opregion:"
+ " 0x%lx.\n", ret,
+ (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT)),
+ igd_guest_opregion = 0;
+ return;
+ }
+
+ ret = xc_domain_memory_mapping(xen_xc, xen_domid,
+ (unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT),
+ (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT),
+ XEN_PCI_INTEL_OPREGION_PAGES,
+ DPCI_ADD_MAPPING);
+
+ if (ret) {
+ XEN_PT_ERR(&s->dev, "[%d]:Can't map IGD host opregion:0x%lx to"
+ " guest opregion:0x%lx.\n", ret,
+ (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT),
+ (unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT));
+ igd_guest_opregion = 0;
+ return;
+ }
+
+ XEN_PT_LOG(&s->dev, "Map OpRegion: 0x%lx -> 0x%lx\n",
+ (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT),
+ (unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT));
+}
diff --git a/qemu/hw/xen/xen_pt_msi.c b/qemu/hw/xen/xen_pt_msi.c
index 263e0514a..9a16f2bff 100644
--- a/qemu/hw/xen/xen_pt_msi.c
+++ b/qemu/hw/xen/xen_pt_msi.c
@@ -9,6 +9,7 @@
* This file implements direct PCI assignment to a HVM guest
*/
+#include "qemu/osdep.h"
#include <sys/mman.h>
#include "hw/xen/xen_backend.h"
@@ -25,6 +26,7 @@
#define XEN_PT_GFLAGSSHIFT_DELIV_MODE 12
#define XEN_PT_GFLAGSSHIFT_TRG_MODE 15
+#define latch(fld) latch[PCI_MSIX_ENTRY_##fld / sizeof(uint32_t)]
/*
* Helpers
@@ -75,19 +77,29 @@ static int msi_msix_enable(XenPCIPassthroughState *s,
bool enable)
{
uint16_t val = 0;
+ int rc;
if (!address) {
return -1;
}
- xen_host_pci_get_word(&s->real_device, address, &val);
+ rc = xen_host_pci_get_word(&s->real_device, address, &val);
+ if (rc) {
+ XEN_PT_ERR(&s->dev, "Failed to read MSI/MSI-X register (0x%x), rc:%d\n",
+ address, rc);
+ return rc;
+ }
if (enable) {
val |= flag;
} else {
val &= ~flag;
}
- xen_host_pci_set_word(&s->real_device, address, val);
- return 0;
+ rc = xen_host_pci_set_word(&s->real_device, address, val);
+ if (rc) {
+ XEN_PT_ERR(&s->dev, "Failed to write MSI/MSI-X register (0x%x), rc:%d\n",
+ address, rc);
+ }
+ return rc;
}
static int msi_msix_setup(XenPCIPassthroughState *s,
@@ -103,9 +115,7 @@ static int msi_msix_setup(XenPCIPassthroughState *s,
assert((!is_msix && msix_entry == 0) || is_msix);
- if (gvec == 0) {
- /* if gvec is 0, the guest is asking for a particular pirq that
- * is passed as dest_id */
+ if (xen_is_pirq_msi(data)) {
*ppirq = msi_ext_dest_id(addr >> 32) | msi_dest_id(addr);
if (!*ppirq) {
/* this probably identifies an misconfiguration of the guest,
@@ -220,7 +230,7 @@ static int msi_msix_disable(XenPCIPassthroughState *s,
* MSI virtualization functions
*/
-int xen_pt_msi_set_enable(XenPCIPassthroughState *s, bool enable)
+static int xen_pt_msi_set_enable(XenPCIPassthroughState *s, bool enable)
{
XEN_PT_LOG(&s->dev, "%s MSI.\n", enable ? "enabling" : "disabling");
@@ -276,7 +286,7 @@ void xen_pt_msi_disable(XenPCIPassthroughState *s)
return;
}
- xen_pt_msi_set_enable(s, false);
+ (void)xen_pt_msi_set_enable(s, false);
msi_msix_disable(s, msi_addr64(msi), msi->data, msi->pirq, false,
msi->initialized);
@@ -304,7 +314,8 @@ static int msix_set_enable(XenPCIPassthroughState *s, bool enabled)
enabled);
}
-static int xen_pt_msix_update_one(XenPCIPassthroughState *s, int entry_nr)
+static int xen_pt_msix_update_one(XenPCIPassthroughState *s, int entry_nr,
+ uint32_t vec_ctrl)
{
XenPTMSIXEntry *entry = NULL;
int pirq;
@@ -322,6 +333,19 @@ static int xen_pt_msix_update_one(XenPCIPassthroughState *s, int entry_nr)
pirq = entry->pirq;
+ /*
+ * Update the entry addr and data to the latest values only when the
+ * entry is masked or they are all masked, as required by the spec.
+ * Addr and data changes while the MSI-X entry is unmasked get deferred
+ * until the next masked -> unmasked transition.
+ */
+ if (pirq == XEN_PT_UNASSIGNED_PIRQ || s->msix->maskall ||
+ (vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT)) {
+ entry->addr = entry->latch(LOWER_ADDR) |
+ ((uint64_t)entry->latch(UPPER_ADDR) << 32);
+ entry->data = entry->latch(DATA);
+ }
+
rc = msi_msix_setup(s, entry->addr, entry->data, &pirq, true, entry_nr,
entry->pirq == XEN_PT_UNASSIGNED_PIRQ);
if (rc) {
@@ -347,7 +371,7 @@ int xen_pt_msix_update(XenPCIPassthroughState *s)
int i;
for (i = 0; i < msix->total_entries; i++) {
- xen_pt_msix_update_one(s, i);
+ xen_pt_msix_update_one(s, i, msix->msix_entry[i].latch(VECTOR_CTRL));
}
return 0;
@@ -396,36 +420,14 @@ int xen_pt_msix_update_remap(XenPCIPassthroughState *s, int bar_index)
static uint32_t get_entry_value(XenPTMSIXEntry *e, int offset)
{
- switch (offset) {
- case PCI_MSIX_ENTRY_LOWER_ADDR:
- return e->addr & UINT32_MAX;
- case PCI_MSIX_ENTRY_UPPER_ADDR:
- return e->addr >> 32;
- case PCI_MSIX_ENTRY_DATA:
- return e->data;
- case PCI_MSIX_ENTRY_VECTOR_CTRL:
- return e->vector_ctrl;
- default:
- return 0;
- }
+ assert(!(offset % sizeof(*e->latch)));
+ return e->latch[offset / sizeof(*e->latch)];
}
static void set_entry_value(XenPTMSIXEntry *e, int offset, uint32_t val)
{
- switch (offset) {
- case PCI_MSIX_ENTRY_LOWER_ADDR:
- e->addr = (e->addr & ((uint64_t)UINT32_MAX << 32)) | val;
- break;
- case PCI_MSIX_ENTRY_UPPER_ADDR:
- e->addr = (uint64_t)val << 32 | (e->addr & UINT32_MAX);
- break;
- case PCI_MSIX_ENTRY_DATA:
- e->data = val;
- break;
- case PCI_MSIX_ENTRY_VECTOR_CTRL:
- e->vector_ctrl = val;
- break;
- }
+ assert(!(offset % sizeof(*e->latch)));
+ e->latch[offset / sizeof(*e->latch)] = val;
}
static void pci_msix_write(void *opaque, hwaddr addr,
@@ -444,39 +446,26 @@ static void pci_msix_write(void *opaque, hwaddr addr,
offset = addr % PCI_MSIX_ENTRY_SIZE;
if (offset != PCI_MSIX_ENTRY_VECTOR_CTRL) {
- const volatile uint32_t *vec_ctrl;
-
if (get_entry_value(entry, offset) == val
&& entry->pirq != XEN_PT_UNASSIGNED_PIRQ) {
return;
}
+ entry->updated = true;
+ } else if (msix->enabled && entry->updated &&
+ !(val & PCI_MSIX_ENTRY_CTRL_MASKBIT)) {
+ const volatile uint32_t *vec_ctrl;
+
/*
* If Xen intercepts the mask bit access, entry->vec_ctrl may not be
* up-to-date. Read from hardware directly.
*/
vec_ctrl = s->msix->phys_iomem_base + entry_nr * PCI_MSIX_ENTRY_SIZE
+ PCI_MSIX_ENTRY_VECTOR_CTRL;
-
- if (msix->enabled && !(*vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT)) {
- if (!entry->warned) {
- entry->warned = true;
- XEN_PT_ERR(&s->dev, "Can't update msix entry %d since MSI-X is"
- " already enabled.\n", entry_nr);
- }
- return;
- }
-
- entry->updated = true;
+ xen_pt_msix_update_one(s, entry_nr, *vec_ctrl);
}
set_entry_value(entry, offset, val);
-
- if (offset == PCI_MSIX_ENTRY_VECTOR_CTRL) {
- if (msix->enabled && !(val & PCI_MSIX_ENTRY_CTRL_MASKBIT)) {
- xen_pt_msix_update_one(s, entry_nr);
- }
- }
}
static uint64_t pci_msix_read(void *opaque, hwaddr addr,
@@ -502,6 +491,12 @@ static uint64_t pci_msix_read(void *opaque, hwaddr addr,
}
}
+static bool pci_msix_accepts(void *opaque, hwaddr addr,
+ unsigned size, bool is_write)
+{
+ return !(addr & (size - 1));
+}
+
static const MemoryRegionOps pci_msix_ops = {
.read = pci_msix_read,
.write = pci_msix_write,
@@ -510,7 +505,13 @@ static const MemoryRegionOps pci_msix_ops = {
.min_access_size = 4,
.max_access_size = 4,
.unaligned = false,
+ .accepts = pci_msix_accepts
},
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false
+ }
};
int xen_pt_msix_init(XenPCIPassthroughState *s, uint32_t base)
@@ -600,7 +601,7 @@ error_out:
return rc;
}
-void xen_pt_msix_delete(XenPCIPassthroughState *s)
+void xen_pt_msix_unmap(XenPCIPassthroughState *s)
{
XenPTMSIX *msix = s->msix;
@@ -617,6 +618,17 @@ void xen_pt_msix_delete(XenPCIPassthroughState *s)
}
memory_region_del_subregion(&s->bar[msix->bar_index], &msix->mmio);
+}
+
+void xen_pt_msix_delete(XenPCIPassthroughState *s)
+{
+ XenPTMSIX *msix = s->msix;
+
+ if (!msix) {
+ return;
+ }
+
+ object_unparent(OBJECT(&msix->mmio));
g_free(s->msix);
s->msix = NULL;
diff --git a/qemu/hw/xenpv/Makefile.objs b/qemu/hw/xenpv/Makefile.objs
index 49f6e9e3c..bbf5873fd 100644
--- a/qemu/hw/xenpv/Makefile.objs
+++ b/qemu/hw/xenpv/Makefile.objs
@@ -1,2 +1,4 @@
# Xen PV machine support
-obj-$(CONFIG_XEN) += xen_domainbuild.o xen_machine_pv.o
+obj-$(CONFIG_XEN) += xen_machine_pv.o
+# Xen PV machine builder support
+obj-$(CONFIG_XEN_PV_DOMAIN_BUILD) += xen_domainbuild.o
diff --git a/qemu/hw/xenpv/xen_domainbuild.c b/qemu/hw/xenpv/xen_domainbuild.c
index c0ab7537d..5a9f5ac80 100644
--- a/qemu/hw/xenpv/xen_domainbuild.c
+++ b/qemu/hw/xenpv/xen_domainbuild.c
@@ -1,4 +1,4 @@
-#include <signal.h>
+#include "qemu/osdep.h"
#include "hw/xen/xen_backend.h"
#include "xen_domainbuild.h"
#include "qemu/timer.h"
@@ -174,12 +174,15 @@ static int xen_domain_watcher(void)
for (i = 3; i < n; i++) {
if (i == fd[0])
continue;
- if (i == xc_fd(xen_xc)) {
- continue;
- }
close(i);
}
+ /*
+ * Reopen xc interface, since the original is unsafe after fork
+ * and was closed above.
+ */
+ xen_xc = xc_interface_open(0, 0, 0);
+
/* ignore term signals */
signal(SIGINT, SIG_IGN);
signal(SIGTERM, SIG_IGN);
@@ -234,7 +237,7 @@ int xen_domain_build_pv(const char *kernel, const char *ramdisk,
int rc;
memcpy(uuid, qemu_uuid, sizeof(uuid));
- rc = xc_domain_create(xen_xc, ssidref, uuid, flags, &xen_domid);
+ rc = xen_domain_create(xen_xc, ssidref, uuid, flags, &xen_domid);
if (rc < 0) {
fprintf(stderr, "xen: xc_domain_create() failed\n");
goto err;
diff --git a/qemu/hw/xenpv/xen_machine_pv.c b/qemu/hw/xenpv/xen_machine_pv.c
index 2e545d241..fc1353599 100644
--- a/qemu/hw/xenpv/xen_machine_pv.c
+++ b/qemu/hw/xenpv/xen_machine_pv.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/boards.h"
#include "hw/xen/xen_backend.h"
@@ -30,9 +31,6 @@
static void xen_init_pv(MachineState *machine)
{
- const char *kernel_filename = machine->kernel_filename;
- const char *kernel_cmdline = machine->kernel_cmdline;
- const char *initrd_filename = machine->initrd_filename;
DriveInfo *dinfo;
int i;
@@ -46,17 +44,27 @@ static void xen_init_pv(MachineState *machine)
case XEN_ATTACH:
/* nothing to do, xend handles everything */
break;
- case XEN_CREATE:
+#ifdef CONFIG_XEN_PV_DOMAIN_BUILD
+ case XEN_CREATE: {
+ const char *kernel_filename = machine->kernel_filename;
+ const char *kernel_cmdline = machine->kernel_cmdline;
+ const char *initrd_filename = machine->initrd_filename;
if (xen_domain_build_pv(kernel_filename, initrd_filename,
kernel_cmdline) < 0) {
fprintf(stderr, "xen pv domain creation failed\n");
exit(1);
}
break;
+ }
+#endif
case XEN_EMULATE:
fprintf(stderr, "xen emulation not implemented (yet)\n");
exit(1);
break;
+ default:
+ fprintf(stderr, "unhandled xen_mode %d\n", xen_mode);
+ exit(1);
+ break;
}
xen_be_register("console", &xen_console_ops);
@@ -93,17 +101,12 @@ static void xen_init_pv(MachineState *machine)
xen_init_display(xen_domid);
}
-static QEMUMachine xenpv_machine = {
- .name = "xenpv",
- .desc = "Xen Para-virtualized PC",
- .init = xen_init_pv,
- .max_cpus = 1,
- .default_machine_opts = "accel=xen",
-};
-
-static void xenpv_machine_init(void)
+static void xenpv_machine_init(MachineClass *mc)
{
- qemu_register_machine(&xenpv_machine);
+ mc->desc = "Xen Para-virtualized PC";
+ mc->init = xen_init_pv;
+ mc->max_cpus = 1;
+ mc->default_machine_opts = "accel=xen";
}
-machine_init(xenpv_machine_init);
+DEFINE_MACHINE("xenpv", xenpv_machine_init)
diff --git a/qemu/hw/xtensa/pic_cpu.c b/qemu/hw/xtensa/pic_cpu.c
index 18825d19f..c835bd009 100644
--- a/qemu/hw/xtensa/pic_cpu.c
+++ b/qemu/hw/xtensa/pic_cpu.c
@@ -25,6 +25,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "qemu/log.h"
#include "qemu/timer.h"
diff --git a/qemu/hw/xtensa/sim.c b/qemu/hw/xtensa/sim.c
index 328d20975..5e9400426 100644
--- a/qemu/hw/xtensa/sim.c
+++ b/qemu/hw/xtensa/sim.c
@@ -25,6 +25,10 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "sysemu/sysemu.h"
#include "hw/boards.h"
#include "hw/loader.h"
@@ -79,12 +83,12 @@ static void xtensa_sim_init(MachineState *machine)
}
ram = g_malloc(sizeof(*ram));
- memory_region_init_ram(ram, NULL, "xtensa.sram", ram_size, &error_abort);
+ memory_region_init_ram(ram, NULL, "xtensa.sram", ram_size, &error_fatal);
vmstate_register_ram_global(ram);
memory_region_add_subregion(get_system_memory(), 0, ram);
rom = g_malloc(sizeof(*rom));
- memory_region_init_ram(rom, NULL, "xtensa.rom", 0x1000, &error_abort);
+ memory_region_init_ram(rom, NULL, "xtensa.rom", 0x1000, &error_fatal);
vmstate_register_ram_global(rom);
memory_region_add_subregion(get_system_memory(), 0xfe000000, rom);
@@ -93,10 +97,10 @@ static void xtensa_sim_init(MachineState *machine)
uint64_t elf_lowaddr;
#ifdef TARGET_WORDS_BIGENDIAN
int success = load_elf(kernel_filename, translate_phys_addr, cpu,
- &elf_entry, &elf_lowaddr, NULL, 1, ELF_MACHINE, 0);
+ &elf_entry, &elf_lowaddr, NULL, 1, EM_XTENSA, 0, 0);
#else
int success = load_elf(kernel_filename, translate_phys_addr, cpu,
- &elf_entry, &elf_lowaddr, NULL, 0, ELF_MACHINE, 0);
+ &elf_entry, &elf_lowaddr, NULL, 0, EM_XTENSA, 0, 0);
#endif
if (success > 0) {
env->pc = elf_entry;
@@ -104,17 +108,12 @@ static void xtensa_sim_init(MachineState *machine)
}
}
-static QEMUMachine xtensa_sim_machine = {
- .name = "sim",
- .desc = "sim machine (" XTENSA_DEFAULT_CPU_MODEL ")",
- .is_default = true,
- .init = xtensa_sim_init,
- .max_cpus = 4,
-};
-
-static void xtensa_sim_machine_init(void)
+static void xtensa_sim_machine_init(MachineClass *mc)
{
- qemu_register_machine(&xtensa_sim_machine);
+ mc->desc = "sim machine (" XTENSA_DEFAULT_CPU_MODEL ")";
+ mc->is_default = true;
+ mc->init = xtensa_sim_init;
+ mc->max_cpus = 4;
}
-machine_init(xtensa_sim_machine_init);
+DEFINE_MACHINE("sim", xtensa_sim_machine_init)
diff --git a/qemu/hw/xtensa/xtfpga.c b/qemu/hw/xtensa/xtfpga.c
index ab4d0e412..2d117369a 100644
--- a/qemu/hw/xtensa/xtfpga.c
+++ b/qemu/hw/xtensa/xtfpga.c
@@ -25,6 +25,10 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "sysemu/sysemu.h"
#include "hw/boards.h"
#include "hw/loader.h"
@@ -143,11 +147,34 @@ static void lx60_net_init(MemoryRegion *address_space,
sysbus_mmio_get_region(s, 1));
ram = g_malloc(sizeof(*ram));
- memory_region_init_ram(ram, OBJECT(s), "open_eth.ram", 16384, &error_abort);
+ memory_region_init_ram(ram, OBJECT(s), "open_eth.ram", 16384,
+ &error_fatal);
vmstate_register_ram_global(ram);
memory_region_add_subregion(address_space, buffers, ram);
}
+static pflash_t *xtfpga_flash_init(MemoryRegion *address_space,
+ const LxBoardDesc *board,
+ DriveInfo *dinfo, int be)
+{
+ SysBusDevice *s;
+ DeviceState *dev = qdev_create(NULL, "cfi.pflash01");
+
+ qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo),
+ &error_abort);
+ qdev_prop_set_uint32(dev, "num-blocks",
+ board->flash_size / board->flash_sector_size);
+ qdev_prop_set_uint64(dev, "sector-length", board->flash_sector_size);
+ qdev_prop_set_uint8(dev, "width", 4);
+ qdev_prop_set_bit(dev, "big-endian", be);
+ qdev_prop_set_string(dev, "name", "lx60.io.flash");
+ qdev_init_nofail(dev);
+ s = SYS_BUS_DEVICE(dev);
+ memory_region_add_subregion(address_space, board->flash_base,
+ sysbus_mmio_get_region(s, 0));
+ return OBJECT_CHECK(pflash_t, (dev), "cfi.pflash01");
+}
+
static uint64_t translate_phys_addr(void *opaque, uint64_t addr)
{
XtensaCPU *cpu = opaque;
@@ -223,7 +250,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine)
ram = g_malloc(sizeof(*ram));
memory_region_init_ram(ram, NULL, "lx60.dram", machine->ram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(ram);
memory_region_add_subregion(system_memory, 0, ram);
@@ -246,16 +273,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine)
dinfo = drive_get(IF_PFLASH, 0, 0);
if (dinfo) {
- flash = pflash_cfi01_register(board->flash_base,
- NULL, "lx60.io.flash", board->flash_size,
- blk_by_legacy_dinfo(dinfo),
- board->flash_sector_size,
- board->flash_size / board->flash_sector_size,
- 4, 0x0000, 0x0000, 0x0000, 0x0000, be);
- if (flash == NULL) {
- error_report("unable to mount pflash");
- exit(EXIT_FAILURE);
- }
+ flash = xtfpga_flash_init(system_io, board, dinfo, be);
}
/* Use presence of kernel file name as 'boot from SRAM' switch. */
@@ -275,7 +293,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine)
rom = g_malloc(sizeof(*rom));
memory_region_init_ram(rom, NULL, "lx60.sram", board->sram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(rom);
memory_region_add_subregion(system_memory, 0xfe000000, rom);
@@ -340,7 +358,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine)
uint64_t elf_entry;
uint64_t elf_lowaddr;
int success = load_elf(kernel_filename, translate_phys_addr, cpu,
- &elf_entry, &elf_lowaddr, NULL, be, ELF_MACHINE, 0);
+ &elf_entry, &elf_lowaddr, NULL, be, EM_XTENSA, 0, 0);
if (success > 0) {
entry_point = elf_entry;
} else {
@@ -385,7 +403,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine)
static void xtensa_lx60_init(MachineState *machine)
{
static const LxBoardDesc lx60_board = {
- .flash_base = 0xf8000000,
+ .flash_base = 0x08000000,
.flash_size = 0x00400000,
.flash_sector_size = 0x10000,
.sram_size = 0x20000,
@@ -396,7 +414,7 @@ static void xtensa_lx60_init(MachineState *machine)
static void xtensa_lx200_init(MachineState *machine)
{
static const LxBoardDesc lx200_board = {
- .flash_base = 0xf8000000,
+ .flash_base = 0x08000000,
.flash_size = 0x01000000,
.flash_sector_size = 0x20000,
.sram_size = 0x2000000,
@@ -407,7 +425,7 @@ static void xtensa_lx200_init(MachineState *machine)
static void xtensa_ml605_init(MachineState *machine)
{
static const LxBoardDesc ml605_board = {
- .flash_base = 0xf8000000,
+ .flash_base = 0x08000000,
.flash_size = 0x01000000,
.flash_sector_size = 0x20000,
.sram_size = 0x2000000,
@@ -418,7 +436,7 @@ static void xtensa_ml605_init(MachineState *machine)
static void xtensa_kc705_init(MachineState *machine)
{
static const LxBoardDesc kc705_board = {
- .flash_base = 0xf0000000,
+ .flash_base = 0x00000000,
.flash_size = 0x08000000,
.flash_boot_base = 0x06000000,
.flash_sector_size = 0x20000,
@@ -427,40 +445,72 @@ static void xtensa_kc705_init(MachineState *machine)
lx_init(&kc705_board, machine);
}
-static QEMUMachine xtensa_lx60_machine = {
- .name = "lx60",
- .desc = "lx60 EVB (" XTENSA_DEFAULT_CPU_MODEL ")",
- .init = xtensa_lx60_init,
- .max_cpus = 4,
+static void xtensa_lx60_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "lx60 EVB (" XTENSA_DEFAULT_CPU_MODEL ")";
+ mc->init = xtensa_lx60_init;
+ mc->max_cpus = 4;
+}
+
+static const TypeInfo xtensa_lx60_type = {
+ .name = MACHINE_TYPE_NAME("lx60"),
+ .parent = TYPE_MACHINE,
+ .class_init = xtensa_lx60_class_init,
};
-static QEMUMachine xtensa_lx200_machine = {
- .name = "lx200",
- .desc = "lx200 EVB (" XTENSA_DEFAULT_CPU_MODEL ")",
- .init = xtensa_lx200_init,
- .max_cpus = 4,
+static void xtensa_lx200_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "lx200 EVB (" XTENSA_DEFAULT_CPU_MODEL ")";
+ mc->init = xtensa_lx200_init;
+ mc->max_cpus = 4;
+}
+
+static const TypeInfo xtensa_lx200_type = {
+ .name = MACHINE_TYPE_NAME("lx200"),
+ .parent = TYPE_MACHINE,
+ .class_init = xtensa_lx200_class_init,
};
-static QEMUMachine xtensa_ml605_machine = {
- .name = "ml605",
- .desc = "ml605 EVB (" XTENSA_DEFAULT_CPU_MODEL ")",
- .init = xtensa_ml605_init,
- .max_cpus = 4,
+static void xtensa_ml605_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "ml605 EVB (" XTENSA_DEFAULT_CPU_MODEL ")";
+ mc->init = xtensa_ml605_init;
+ mc->max_cpus = 4;
+}
+
+static const TypeInfo xtensa_ml605_type = {
+ .name = MACHINE_TYPE_NAME("ml605"),
+ .parent = TYPE_MACHINE,
+ .class_init = xtensa_ml605_class_init,
};
-static QEMUMachine xtensa_kc705_machine = {
- .name = "kc705",
- .desc = "kc705 EVB (" XTENSA_DEFAULT_CPU_MODEL ")",
- .init = xtensa_kc705_init,
- .max_cpus = 4,
+static void xtensa_kc705_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "kc705 EVB (" XTENSA_DEFAULT_CPU_MODEL ")";
+ mc->init = xtensa_kc705_init;
+ mc->max_cpus = 4;
+}
+
+static const TypeInfo xtensa_kc705_type = {
+ .name = MACHINE_TYPE_NAME("kc705"),
+ .parent = TYPE_MACHINE,
+ .class_init = xtensa_kc705_class_init,
};
static void xtensa_lx_machines_init(void)
{
- qemu_register_machine(&xtensa_lx60_machine);
- qemu_register_machine(&xtensa_lx200_machine);
- qemu_register_machine(&xtensa_ml605_machine);
- qemu_register_machine(&xtensa_kc705_machine);
+ type_register_static(&xtensa_lx60_type);
+ type_register_static(&xtensa_lx200_type);
+ type_register_static(&xtensa_ml605_type);
+ type_register_static(&xtensa_kc705_type);
}
-machine_init(xtensa_lx_machines_init);
+type_init(xtensa_lx_machines_init)